CIFS: Fix wsize usage in iovec write
authorPavel Shilovsky <pshilovsky@samba.org>
Fri, 20 Jun 2014 12:30:46 +0000 (16:30 +0400)
committerSteve French <smfrench@gmail.com>
Sat, 2 Aug 2014 06:23:02 +0000 (01:23 -0500)
If a server change maximum buffer size for write (wsize) requests
on reconnect we can fail on repeating with a big size buffer on
-EAGAIN error in iovec write. Fix this by checking wsize all the
time before repeating request in iovec write.

Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com>
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifssmb.c
fs/cifs/file.c
fs/cifs/smb2pdu.c

index 52cfd8cf0db0919d026cb49b8d00342765537686..1b3f0aef01e3d44da9166b186d43871ac7737840 100644 (file)
@@ -196,10 +196,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
        if (rc)
                goto out;
 
-       /*
-        * FIXME: check if wsize needs updated due to negotiated smb buffer
-        *        size shrinking
-        */
        atomic_inc(&tconInfoReconnectCount);
 
        /* tell server Unix caps we support */
index 666069811ab854daabf8683d39457e2f7129b001..c9c4f5ac3c78b21c3f8f4045de2d651de5c2c62e 100644 (file)
@@ -2401,28 +2401,6 @@ cifs_uncached_writev_complete(struct work_struct *work)
        kref_put(&wdata->refcount, cifs_uncached_writedata_release);
 }
 
-/* attempt to send write to server, retry on any -EAGAIN errors */
-static int
-cifs_uncached_retry_writev(struct cifs_writedata *wdata)
-{
-       int rc;
-       struct TCP_Server_Info *server;
-
-       server = tlink_tcon(wdata->cfile->tlink)->ses->server;
-
-       do {
-               if (wdata->cfile->invalidHandle) {
-                       rc = cifs_reopen_file(wdata->cfile, false);
-                       if (rc != 0)
-                               continue;
-               }
-               rc = server->ops->async_writev(wdata,
-                                              cifs_uncached_writedata_release);
-       } while (rc == -EAGAIN);
-
-       return rc;
-}
-
 static int
 wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
                      size_t *len, unsigned long *num_pages)
@@ -2474,13 +2452,19 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
        size_t cur_len;
        unsigned long nr_pages, num_pages, i;
        struct cifs_writedata *wdata;
+       struct iov_iter saved_from;
+       loff_t saved_offset = offset;
        pid_t pid;
+       struct TCP_Server_Info *server;
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
                pid = open_file->pid;
        else
                pid = current->tgid;
 
+       server = tlink_tcon(open_file->tlink)->ses->server;
+       memcpy(&saved_from, from, sizeof(struct iov_iter));
+
        do {
                nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
                wdata = cifs_writedata_alloc(nr_pages,
@@ -2520,10 +2504,20 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
                wdata->bytes = cur_len;
                wdata->pagesz = PAGE_SIZE;
                wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
-               rc = cifs_uncached_retry_writev(wdata);
+
+               if (!wdata->cfile->invalidHandle ||
+                   !cifs_reopen_file(wdata->cfile, false))
+                       rc = server->ops->async_writev(wdata,
+                                       cifs_uncached_writedata_release);
                if (rc) {
                        kref_put(&wdata->refcount,
                                 cifs_uncached_writedata_release);
+                       if (rc == -EAGAIN) {
+                               memcpy(from, &saved_from,
+                                      sizeof(struct iov_iter));
+                               iov_iter_advance(from, offset - saved_offset);
+                               continue;
+                       }
                        break;
                }
 
@@ -2545,6 +2539,7 @@ cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
        struct cifs_sb_info *cifs_sb;
        struct cifs_writedata *wdata, *tmp;
        struct list_head wdata_list;
+       struct iov_iter saved_from;
        int rc;
 
        len = iov_iter_count(from);
@@ -2565,6 +2560,8 @@ cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
        if (!tcon->ses->server->ops->async_writev)
                return -ENOSYS;
 
+       memcpy(&saved_from, from, sizeof(struct iov_iter));
+
        rc = cifs_write_from_iter(*poffset, len, from, open_file, cifs_sb,
                                  &wdata_list);
 
@@ -2596,7 +2593,25 @@ restart_loop:
 
                        /* resend call if it's a retryable error */
                        if (rc == -EAGAIN) {
-                               rc = cifs_uncached_retry_writev(wdata);
+                               struct list_head tmp_list;
+                               struct iov_iter tmp_from;
+
+                               INIT_LIST_HEAD(&tmp_list);
+                               list_del_init(&wdata->list);
+
+                               memcpy(&tmp_from, &saved_from,
+                                      sizeof(struct iov_iter));
+                               iov_iter_advance(&tmp_from,
+                                                wdata->offset - *poffset);
+
+                               rc = cifs_write_from_iter(wdata->offset,
+                                               wdata->bytes, &tmp_from,
+                                               open_file, cifs_sb, &tmp_list);
+
+                               list_splice(&tmp_list, &wdata_list);
+
+                               kref_put(&wdata->refcount,
+                                        cifs_uncached_writedata_release);
                                goto restart_loop;
                        }
                }
index 0158104df7456ae365bfce9fc33130bc24ee812d..da7aa62bf0618d827122b780fd45e1c68224ad63 100644 (file)
@@ -245,10 +245,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
        if (rc)
                goto out;
        atomic_inc(&tconInfoReconnectCount);
-       /*
-        * BB FIXME add code to check if wsize needs update due to negotiated
-        * smb buffer size shrinking.
-        */
 out:
        /*
         * Check if handle based operation so we know whether we can continue