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.

Signed-off-by: Pavel Shilovsky <[email protected]>
---
 fs/cifs/file.c |   62 +++++++++++++++++++++++++++++++-------------------------
 1 file changed, 34 insertions(+), 28 deletions(-)

diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 887c18c..4ba59cd 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -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 *it,
                      size_t *len, unsigned long nr_pages)
@@ -2472,23 +2450,28 @@ wdata_fill_from_iovec(struct cifs_writedata *wdata, 
struct iov_iter *it,
 
 static int
 cifs_write_from_iovec(loff_t offset, size_t len, const struct iovec *iov,
-                     unsigned long nr_segs, struct cifsFileInfo *open_file,
+                     unsigned long nr_segs, loff_t written,
+                     struct cifsFileInfo *open_file,
                      struct cifs_sb_info *cifs_sb,
                      struct list_head *wdata_list)
 {
        int rc = 0;
-       size_t cur_len;
+       size_t cur_len, saved_len = len;
        unsigned long nr_pages, i;
        struct cifs_writedata *wdata;
        struct iov_iter it;
+       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;
 
-       iov_iter_init(&it, iov, nr_segs, len, 0);
+       server = tlink_tcon(open_file->tlink)->ses->server;
+
+       iov_iter_init(&it, iov, nr_segs, len, written);
        do {
                nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
                wdata = cifs_writedata_alloc(nr_pages,
@@ -2520,10 +2503,19 @@ cifs_write_from_iovec(loff_t offset, size_t len, const 
struct iovec *iov,
                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) {
+                               iov_iter_init(&it, iov, nr_segs, saved_len,
+                                             written + offset - saved_offset);
+                               continue;
+                       }
                        break;
                }
 
@@ -2564,7 +2556,7 @@ cifs_iovec_write(struct file *file, const struct iovec 
*iov,
        if (!tcon->ses->server->ops->async_writev)
                return -ENOSYS;
 
-       rc = cifs_write_from_iovec(*poffset, len, iov, nr_segs, open_file,
+       rc = cifs_write_from_iovec(*poffset, len, iov, nr_segs, 0, open_file,
                                   cifs_sb, &wdata_list);
 
        /*
@@ -2595,7 +2587,21 @@ restart_loop:
 
                        /* resend call if it's a retryable error */
                        if (rc == -EAGAIN) {
-                               rc = cifs_uncached_retry_writev(wdata);
+                               struct list_head tmp_list;
+
+                               INIT_LIST_HEAD(&tmp_list);
+                               list_del_init(&wdata->list);
+
+                               rc = cifs_write_from_iovec(wdata->offset,
+                                       wdata->bytes, iov, nr_segs,
+                                       wdata->offset - *poffset, open_file,
+                                       cifs_sb, &tmp_list);
+
+                               if (!list_empty(&tmp_list))
+                                       list_splice(&tmp_list, &wdata_list);
+
+                               kref_put(&wdata->refcount,
+                                        cifs_uncached_writedata_release);
                                goto restart_loop;
                        }
                }
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to