Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.24 /fs/cifs/file.c

Filename:/fs/cifs/file.c
Lines Added:122
Lines Deleted:33
Also changed in: (Previous) 2.6.24-rc8  2.6.24-rc7  2.6.24-rc6  2.6.24-rc5  2.6.24-rc4  2.6.24-rc3-git7 
(Following) 2.6.24-git3  2.6.24-git4  2.6.24-git5  2.6.24-git6  2.6.24-git7  2.6.24-git8 

Location
[  2.6.24
  [  fs
    [  cifs
       o  file.c

Patch

diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 894b1f7..dd26e27 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -130,7 +130,9 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
       if (file->f_path.dentry->d_inode->i_mapping) {
       /* BB no need to lock inode until after invalidate
          since namei code should already have it locked? */
-         filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
+         rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
+         if (rc != 0)
+            CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc;
       }
       cFYI(1, ("invalidating remote inode since open detected it "
           "changed"));
@@ -425,7 +427,9 @@ reopen_error_exit:
       pCifsInode = CIFS_I(inode);
       if (pCifsInode) {
          if (can_flush) {
-            filemap_write_and_wait(inode->i_mapping);
+            rc = filemap_write_and_wait(inode->i_mapping);
+            if (rc != 0)
+               CIFS_I(inode)->write_behind_rc = rc;
          /* temporarily disable caching while we
             go to server to get inode info */
             pCifsInode->clientCanCacheAll = FALSE;
@@ -467,7 +471,7 @@ reopen_error_exit:
 int cifs_close(struct inode *inode, struct file *file)
 {
    int rc = 0;
-   int xid;
+   int xid, timeout;
    struct cifs_sb_info *cifs_sb;
    struct cifsTconInfo *pTcon;
    struct cifsFileInfo *pSMBFile =
@@ -485,9 +489,9 @@ 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;
+            timeout = 2;
             while ((atomic_read(&pSMBFile->wrtPending) != 0)
-                && (timeout < 1000) ) {
+               && (timeout <= 2048)) {
                /* 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
@@ -522,12 +526,30 @@ int cifs_close(struct inode *inode, struct file *file)
       list_del(&pSMBFile->flist);
       list_del(&pSMBFile->tlist);
       write_unlock(&GlobalSMBSeslock);
+      timeout = 10;
+      /* We waited above to give the SMBWrite a chance to issue
+         on the wire (so we do not get SMBWrite returning EBADF
+         if writepages is racing with close.  Note that writepages
+         does not specify a file handle, so it is possible for a file
+         to be opened twice, and the application close the "wrong"
+         file handle - in these cases we delay long enough to allow
+         the SMBWrite to get on the wire before the SMB Close.
+         We allow total wait here over 45 seconds, more than
+         oplock break time, and more than enough to allow any write
+         to complete on the server, or to time out on the client */
+      while ((atomic_read(&pSMBFile->wrtPending) != 0)
+            && (timeout <= 50000)) {
+         cERROR(1, ("writes pending, delay free of handle"));
+         msleep(timeout);
+         timeout *= 8;
+      }
       kfree(pSMBFile->search_resume_name);
       kfree(file->private_data);
       file->private_data = NULL;
    } else
       rc = -EBADF;
 
+   read_lock(&GlobalSMBSeslock);
    if (list_empty(&(CIFS_I(inode)->openFileList))) {
       cFYI(1, ("closing last open instance for inode %p", inode));
       /* if the file is not open we do not know if we can cache info
@@ -535,6 +557,7 @@ int cifs_close(struct inode *inode, struct file *file)
       CIFS_I(inode)->clientCanCacheRead = FALSE;
       CIFS_I(inode)->clientCanCacheAll  = FALSE;
    }
+   read_unlock(&GlobalSMBSeslock);
    if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
       rc = CIFS_I(inode)->write_behind_rc;
    FreeXid(xid);
@@ -767,7 +790,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
          mutex_lock(&fid->lock_mutex);
          list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
             if (pfLock->fl_start <= li->offset &&
-                  length >= li->length) {
+                  (pfLock->fl_start + length) >=
+                  (li->offset + li->length)) {
                stored_rc = CIFSSMBLock(xid, pTcon,
                      netfid,
                      li->length, li->offset,
@@ -815,9 +839,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
    xid = GetXid();
 
    if (*poffset > file->f_path.dentry->d_inode->i_size)
-      long_op = 2; /* writes past end of file can take a long time */
+      long_op = CIFS_VLONG_OP; /* writes past EOF take long time */
    else
-      long_op = 1;
+      long_op = CIFS_LONG_OP;
 
    for (total_written = 0; write_size > total_written;
         total_written += bytes_written) {
@@ -864,7 +888,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
          }
       } else
          *poffset += bytes_written;
-      long_op = FALSE; /* subsequent writes fast -
+      long_op = CIFS_STD_OP; /* subsequent writes fast -
                 15 seconds is plenty */
    }
 
@@ -914,9 +938,9 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
    xid = GetXid();
 
    if (*poffset > file->f_path.dentry->d_inode->i_size)
-      long_op = 2; /* writes past end of file can take a long time */
+      long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */
    else
-      long_op = 1;
+      long_op = CIFS_LONG_OP;
 
    for (total_written = 0; write_size > total_written;
         total_written += bytes_written) {
@@ -982,7 +1006,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
          }
       } else
          *poffset += bytes_written;
-      long_op = FALSE; /* subsequent writes fast -
+      long_op = CIFS_STD_OP; /* subsequent writes fast -
                 15 seconds is plenty */
    }
 
@@ -1006,6 +1030,37 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
    return total_written;
 }
 
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
+{
+   struct cifsFileInfo *open_file = NULL;
+
+   read_lock(&GlobalSMBSeslock);
+   /* we could simply get the first_list_entry since write-only entries
+      are always at the end of the list but since the first entry might
+      have a close pending, we go through the whole list */
+   list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+      if (open_file->closePend)
+         continue;
+      if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
+          (open_file->pfile->f_flags & O_RDONLY))) {
+         if (!open_file->invalidHandle) {
+            /* found a good file */
+            /* lock it so it will not be closed on us */
+            atomic_inc(&open_file->wrtPending);
+            read_unlock(&GlobalSMBSeslock);
+            return open_file;
+         } /* else might as well continue, and look for
+              another, or simply have the caller reopen it
+              again rather than trying to fix this handle */
+      } else /* write only file */
+         break; /* write only files are last so must be done */
+   }
+   read_unlock(&GlobalSMBSeslock);
+   return NULL;
+}
+#endif
+
 struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
 {
    struct cifsFileInfo *open_file;
@@ -1022,6 +1077,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
    }
 
    read_lock(&GlobalSMBSeslock);
+refind_writable:
    list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
       if (open_file->closePend)
          continue;
@@ -1029,24 +1085,49 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
           ((open_file->pfile->f_flags & O_RDWR) ||
            (open_file->pfile->f_flags & O_WRONLY))) {
          atomic_inc(&open_file->wrtPending);
+
+         if (!open_file->invalidHandle) {
+            /* found a good writable file */
+            read_unlock(&GlobalSMBSeslock);
+            return open_file;
+         }
+
          read_unlock(&GlobalSMBSeslock);
-         if ((open_file->invalidHandle) &&
-            (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
-            rc = cifs_reopen_file(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"));
+         /* Had to unlock since following call can block */
+         rc = cifs_reopen_file(open_file->pfile, FALSE);
+         if (!rc) {
+            if (!open_file->closePend)
+               return open_file;
+            else { /* start over in case this was deleted */
+                   /* since the list could be modified */
                read_lock(&GlobalSMBSeslock);
-               /* can not use this handle, no write
-               pending on this one after all */
-               atomic_dec
-                    (&open_file->wrtPending);
-               continue;
+               atomic_dec(&open_file->wrtPending);
+               goto refind_writable;
             }
          }
-         return open_file;
+
+         /* if it fails, try another handle if possible -
+         (we can not do this if closePending since
+         loop could be modified - in which case we
+         have to start at the beginning of the list
+         again. Note that it would be bad
+         to hold up writepages here (rather than
+         in caller) with continuous retries */
+         cFYI(1, ("wp failed on reopen file"));
+         read_lock(&GlobalSMBSeslock);
+         /* can not use this handle, no write
+            pending on this one after all */
+         atomic_dec(&open_file->wrtPending);
+
+         if (open_file->closePend) /* list could have changed */
+            goto refind_writable;
+         /* else we simply continue to the next entry. Thus
+            we do not loop on reopen errors.  If we
+            can not reopen the file, for example if we
+            reconnected to a server with another client
+            racing to delete or lock the file we would not
+            make progress if we restarted before the beginning
+            of the loop here. */
       }
    }
    read_unlock(&GlobalSMBSeslock);
@@ -1283,14 +1364,17 @@ retry:
                      open_file->netfid,
                      bytes_to_write, offset,
                      &bytes_written, iov, n_iov,
-                     1);
+                     CIFS_LONG_OP);
             atomic_dec(&open_file->wrtPending);
             if (rc || bytes_written < bytes_to_write) {
                cERROR(1, ("Write2 ret %d, wrote %d",
                     rc, bytes_written));
                /* BB what if continued retry is
                   requested via mount flags? */
-               set_bit(AS_EIO, &mapping->flags);
+               if (rc == -ENOSPC)
+                  set_bit(AS_ENOSPC, &mapping->flags);
+               else
+                  set_bit(AS_EIO, &mapping->flags);
             } else {
                cifs_stats_bytes_written(cifs_sb->tcon,
                          bytes_written);
@@ -1422,9 +1506,11 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
    cFYI(1, ("Sync file - name: %s datasync: 0x%x",
       dentry->d_name.name, datasync));
 
-   rc = filemap_fdatawrite(inode->i_mapping);
-   if (rc == 0)
+   rc = filemap_write_and_wait(inode->i_mapping);
+   if (rc == 0) {
+      rc = CIFS_I(inode)->write_behind_rc;
       CIFS_I(inode)->write_behind_rc = 0;
+   }
    FreeXid(xid);
    return rc;
 }
@@ -1476,8 +1562,11 @@ int cifs_flush(struct file *file, fl_owner_t id)
       filemapfdatawrite appears easier for the time being */
 
    rc = filemap_fdatawrite(inode->i_mapping);
-   if (!rc) /* reset wb rc if we were able to write out dirty pages */
+   /* reset wb rc if we were able to write out dirty pages */
+   if (!rc) {
+      rc = CIFS_I(inode)->write_behind_rc;
       CIFS_I(inode)->write_behind_rc = 0;
+   }
 
    cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc));
 
@@ -1709,7 +1798,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
    struct page *page;
    struct cifs_sb_info *cifs_sb;
    struct cifsTconInfo *pTcon;
-   int bytes_read = 0;
+   unsigned int bytes_read = 0;
    unsigned int read_size, i;
    char *smb_read_data = NULL;
    struct smb_com_read_rsp *pSMBr;
@@ -1803,7 +1892,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 
          i +=  bytes_read >> PAGE_CACHE_SHIFT;
          cifs_stats_bytes_read(pTcon, bytes_read);
-         if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) {
+         if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) {
             i++; /* account for partial page */
 
             /* server copy of file can have smaller size


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.