Merge branch 'for-2.6.24' of master.kernel.org:/pub/scm/linux/kernel/git/olof/pasemi...
[pandora-kernel.git] / fs / cifs / file.c
index 7925491..68ad4ca 100644 (file)
@@ -1026,6 +1026,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;
@@ -1042,6 +1073,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;
@@ -1049,26 +1081,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) {
-                               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, ("wp failed on reopen file"));
+                       /* 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;
+                                       goto refind_writable;
                                }
                        }
-                       if (open_file->closePend) {
-                               read_lock(&GlobalSMBSeslock);
-                               atomic_dec(&open_file->wrtPending);
-                               continue;
-                       }
-                       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);
@@ -1731,7 +1786,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;
@@ -1825,7 +1880,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