[CIFS] Defer close of file handle slightly if there are pending writes that
authorSteve French <sfrench@us.ibm.com>
Thu, 20 Oct 2005 20:44:56 +0000 (13:44 -0700)
committerSteve French <sfrench@us.ibm.com>
Thu, 20 Oct 2005 20:44:56 +0000 (13:44 -0700)
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>
fs/cifs/CHANGES
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/file.c
fs/cifs/inode.c

index f554a70..5bab24f 100644 (file)
@@ -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)
index 4cdb29f..1223fa8 100644 (file)
@@ -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 */
index 839a556..1ba08f8 100644 (file)
@@ -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 */
index 23af20d..da4f5e1 100644 (file)
@@ -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"
@@ -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;
 }
@@ -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);
@@ -919,9 +939,10 @@ 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 */
@@ -929,6 +950,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
                                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;
                                }
                        }
@@ -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)) {
@@ -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;
@@ -1071,15 +1097,6 @@ retry:
                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;
@@ -1155,18 +1172,32 @@ retry:
                                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];
@@ -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;
 }
 
index ff4d1cc..912d401 100644 (file)
@@ -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;