Skip to content

Commit

Permalink
[CIFS] Defer close of file handle slightly if there are pending write…
Browse files Browse the repository at this point in the history
…s that

need to get in ahead of it that depend on that file handle. Fixes
occassional bad file handle errors on write with heavy use multiple process
cases.

Signed-off-by: Steve French <sfrench@us.ibm.com>
  • Loading branch information
Steve French committed Oct 20, 2005
1 parent 84d2f07 commit 23e7dd7
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 25 deletions.
6 changes: 6 additions & 0 deletions fs/cifs/CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version 1.39
------------
Defer close of a file handle slightly if pending writes depend on that file handle
(this reduces the EBADF bad file handle errors that can be logged under heavy
stress on writes).

Version 1.38
------------
Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket)
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/cifsfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern int cifs_ioctl (struct inode * inode, struct file * filep,
unsigned int command, unsigned long arg);
#define CIFS_VERSION "1.38"
#define CIFS_VERSION "1.39"
#endif /* _CIFSFS_H */
1 change: 1 addition & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ struct cifsFileInfo {
struct inode * pInode; /* needed for oplock break */
unsigned closePend:1; /* file is marked to close */
unsigned invalidHandle:1; /* file closed via session abend */
atomic_t wrtPending; /* handle in use - defer close */
struct semaphore fh_sem; /* prevents reopen race after dead ses*/
char * search_resume_name; /* BB removeme BB */
unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */
Expand Down
88 changes: 64 additions & 24 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/pagevec.h>
#include <linux/smp_lock.h>
#include <linux/writeback.h>
#include <linux/delay.h>
#include <asm/div64.h>
#include "cifsfs.h"
#include "cifspdu.h"
Expand All @@ -50,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private(
private_data->pInode = inode;
private_data->invalidHandle = FALSE;
private_data->closePend = FALSE;
/* we have to track num writers to the inode, since writepages
does not tell us which handle the write is for so there can
be a close (overlapping with write) of the filehandle that
cifs_writepages chose to use */
atomic_set(&private_data->wrtPending,0);

return private_data;
}
Expand Down Expand Up @@ -473,6 +479,20 @@ int cifs_close(struct inode *inode, struct file *file)
/* no sense reconnecting to close a file that is
already closed */
if (pTcon->tidStatus != CifsNeedReconnect) {
int timeout = 2;
while((atomic_read(&pSMBFile->wrtPending) != 0)
&& (timeout < 1000) ) {
/* Give write a better chance to get to
server ahead of the close. We do not
want to add a wait_q here as it would
increase the memory utilization as
the struct would be in each open file,
but this should give enough time to
clear the socket */
cERROR(1,("close with pending writes"));
msleep(timeout);
timeout *= 4;
}
write_unlock(&file->f_owner.lock);
rc = CIFSSMBClose(xid, pTcon,
pSMBFile->netfid);
Expand Down Expand Up @@ -919,16 +939,21 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
if (open_file->pfile &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
atomic_inc(&open_file->wrtPending);
read_unlock(&GlobalSMBSeslock);
if((open_file->invalidHandle) &&
(!open_file->closePend)) {
(!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
rc = cifs_reopen_file(&cifs_inode->vfs_inode,
open_file->pfile, FALSE);
/* if it fails, try another handle - might be */
/* dangerous to hold up writepages with retry */
if(rc) {
cFYI(1,("failed on reopen file in wp"));
read_lock(&GlobalSMBSeslock);
/* can not use this handle, no write
pending on this one after all */
atomic_dec
(&open_file->wrtPending);
continue;
}
}
Expand Down Expand Up @@ -981,6 +1006,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
if (open_file) {
bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset);
atomic_dec(&open_file->wrtPending);
/* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
if ((bytes_written > 0) && (offset)) {
Expand Down Expand Up @@ -1016,7 +1042,7 @@ static int cifs_writepages(struct address_space *mapping,
pgoff_t next;
int nr_pages;
__u64 offset = 0;
struct cifsFileInfo *open_file = NULL;
struct cifsFileInfo *open_file;
struct page *page;
struct pagevec pvec;
int rc = 0;
Expand Down Expand Up @@ -1071,15 +1097,6 @@ static int cifs_writepages(struct address_space *mapping,
int first;
unsigned int i;

if (!open_file) {
open_file = find_writable_file(CIFS_I(mapping->host));
if (!open_file) {
pagevec_release(&pvec);
cERROR(1, ("No writable handles for inode"));
return -EIO;
}
}

first = -1;
next = 0;
n_iov = 0;
Expand Down Expand Up @@ -1155,18 +1172,32 @@ static int cifs_writepages(struct address_space *mapping,
break;
}
if (n_iov) {
rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
open_file->netfid, bytes_to_write,
offset, &bytes_written, iov, n_iov,
1);
if (rc || bytes_written < bytes_to_write) {
cERROR(1,("CIFSSMBWrite2 returned %d, written = %x",
rc, bytes_written));
set_bit(AS_EIO, &mapping->flags);
SetPageError(page);
/* Search for a writable handle every time we call
* CIFSSMBWrite2. We can't rely on the last handle
* we used to still be valid
*/
open_file = find_writable_file(CIFS_I(mapping->host));
if (!open_file) {
cERROR(1, ("No writable handles for inode"));
rc = -EBADF;
} else {
cifs_stats_bytes_written(cifs_sb->tcon,
bytes_written);
rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
open_file->netfid,
bytes_to_write, offset,
&bytes_written, iov, n_iov,
1);
atomic_dec(&open_file->wrtPending);
if (rc || bytes_written < bytes_to_write) {
cERROR(1,("Write2 ret %d, written = %d",
rc, bytes_written));
/* BB what if continued retry is
requested via mount flags? */
set_bit(AS_EIO, &mapping->flags);
SetPageError(page);
} else {
cifs_stats_bytes_written(cifs_sb->tcon,
bytes_written);
}
}
for (i = 0; i < n_iov; i++) {
page = pvec.pages[first + i];
Expand Down Expand Up @@ -1788,9 +1819,18 @@ static int cifs_readpage(struct file *file, struct page *page)
page caching in the current Linux kernel design */
int is_size_safe_to_change(struct cifsInodeInfo *cifsInode)
{
if (cifsInode && find_writable_file(cifsInode))
struct cifsFileInfo *open_file = NULL;

if (cifsInode)
open_file = find_writable_file(cifsInode);

if(open_file) {
/* there is not actually a write pending so let
this handle go free and allow it to
be closable if needed */
atomic_dec(&open_file->wrtPending);
return 0;
else
} else
return 1;
}

Expand Down
1 change: 1 addition & 0 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
__u32 npid = open_file->pid;
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size,
nfid, npid, FALSE);
atomic_dec(&open_file->wrtPending);
cFYI(1,("SetFSize for attrs rc = %d", rc));
if(rc == -EINVAL) {
int bytes_written;
Expand Down

0 comments on commit 23e7dd7

Please sign in to comment.