]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
cifs: convert cifs_iovec_write to use async writes
authorJeff Layton <jlayton@redhat.com>
Fri, 23 Mar 2012 18:40:56 +0000 (14:40 -0400)
committerJeff Layton <jlayton@redhat.com>
Fri, 23 Mar 2012 18:40:56 +0000 (14:40 -0400)
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/file.c

index 57ce6f834220e4d11a0d93a51374ec02c7cbf743..96192c1e380afb9475048f5d932e886fb4338d61 100644 (file)
@@ -483,6 +483,8 @@ int cifs_async_readv(struct cifs_readdata *rdata);
 /* asynchronous write support */
 struct cifs_writedata {
        struct kref                     refcount;
+       struct list_head                list;
+       struct completion               done;
        enum writeback_sync_modes       sync_mode;
        struct work_struct              work;
        struct cifsFileInfo             *cfile;
index b63bf5f0698a397452c32a13d05fcd14f8d7d3d2..8fecc99be34443d7368f88b4db63984b8d4f3f53 100644 (file)
@@ -2081,8 +2081,10 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
        wdata = kzalloc(sizeof(*wdata) +
                        sizeof(struct page *) * (nr_pages - 1), GFP_NOFS);
        if (wdata != NULL) {
-               INIT_WORK(&wdata->work, complete);
                kref_init(&wdata->refcount);
+               INIT_LIST_HEAD(&wdata->list);
+               init_completion(&wdata->done);
+               INIT_WORK(&wdata->work, complete);
        }
        return wdata;
 }
index 6883b08f848c94339d8c09770376a1e374d194d1..daaaca82eeb23ea40bcedd199e00e5d385619ce8 100644 (file)
@@ -2106,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
        return num_pages;
 }
 
+static void
+cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
+{
+       int i;
+       size_t bytes = wdata->bytes;
+
+       /* marshal up the pages into iov array */
+       for (i = 0; i < wdata->nr_pages; i++) {
+               iov[i + 1].iov_len = min(bytes, PAGE_SIZE);
+               iov[i + 1].iov_base = kmap(wdata->pages[i]);
+               bytes -= iov[i + 1].iov_len;
+       }
+}
+
+static void
+cifs_uncached_writev_complete(struct work_struct *work)
+{
+       int i;
+       struct cifs_writedata *wdata = container_of(work,
+                                       struct cifs_writedata, work);
+       struct inode *inode = wdata->cfile->dentry->d_inode;
+       struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+       spin_lock(&inode->i_lock);
+       cifs_update_eof(cifsi, wdata->offset, wdata->bytes);
+       if (cifsi->server_eof > inode->i_size)
+               i_size_write(inode, cifsi->server_eof);
+       spin_unlock(&inode->i_lock);
+
+       complete(&wdata->done);
+
+       if (wdata->result != -EAGAIN) {
+               for (i = 0; i < wdata->nr_pages; i++)
+                       put_page(wdata->pages[i]);
+       }
+
+       kref_put(&wdata->refcount, cifs_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;
+
+       do {
+               if (wdata->cfile->invalidHandle) {
+                       rc = cifs_reopen_file(wdata->cfile, false);
+                       if (rc != 0)
+                               continue;
+               }
+               rc = cifs_async_writev(wdata);
+       } while (rc == -EAGAIN);
+
+       return rc;
+}
+
 static ssize_t
 cifs_iovec_write(struct file *file, const struct iovec *iov,
                 unsigned long nr_segs, loff_t *poffset)
 {
-       unsigned int written;
-       unsigned long num_pages, npages, i;
+       unsigned long nr_pages, i;
        size_t copied, len, cur_len;
        ssize_t total_written = 0;
-       struct kvec *to_send;
-       struct page **pages;
+       loff_t offset = *poffset;
        struct iov_iter it;
-       struct inode *inode;
        struct cifsFileInfo *open_file;
-       struct cifs_tcon *pTcon;
+       struct cifs_tcon *tcon;
        struct cifs_sb_info *cifs_sb;
-       struct cifs_io_parms io_parms;
-       int xid, rc;
-       __u32 pid;
+       struct cifs_writedata *wdata, *tmp;
+       struct list_head wdata_list;
+       int rc;
+       pid_t pid;
 
        len = iov_length(iov, nr_segs);
        if (!len)
@@ -2133,105 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        if (rc)
                return rc;
 
+       INIT_LIST_HEAD(&wdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
-       num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
-
-       pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
-       if (!pages)
-               return -ENOMEM;
-
-       to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
-       if (!to_send) {
-               kfree(pages);
-               return -ENOMEM;
-       }
-
-       rc = cifs_write_allocate_pages(pages, num_pages);
-       if (rc) {
-               kfree(pages);
-               kfree(to_send);
-               return rc;
-       }
-
-       xid = GetXid();
        open_file = file->private_data;
+       tcon = tlink_tcon(open_file->tlink);
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
                pid = open_file->pid;
        else
                pid = current->tgid;
 
-       pTcon = tlink_tcon(open_file->tlink);
-       inode = file->f_path.dentry->d_inode;
-
        iov_iter_init(&it, iov, nr_segs, len, 0);
-       npages = num_pages;
-
        do {
-               size_t save_len = cur_len;
-               for (i = 0; i < npages; i++) {
-                       copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
-                       copied = iov_iter_copy_from_user(pages[i], &it, 0,
-                                                        copied);
+               size_t save_len;
+
+               nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
+               wdata = cifs_writedata_alloc(nr_pages,
+                                            cifs_uncached_writev_complete);
+               if (!wdata) {
+                       rc = -ENOMEM;
+                       break;
+               }
+
+               rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
+               if (rc) {
+                       kfree(wdata);
+                       break;
+               }
+
+               save_len = cur_len;
+               for (i = 0; i < nr_pages; i++) {
+                       copied = min_t(const size_t, cur_len, PAGE_SIZE);
+                       copied = iov_iter_copy_from_user(wdata->pages[i], &it,
+                                                        0, copied);
                        cur_len -= copied;
                        iov_iter_advance(&it, copied);
-                       to_send[i+1].iov_base = kmap(pages[i]);
-                       to_send[i+1].iov_len = copied;
                }
-
                cur_len = save_len - cur_len;
 
-               do {
-                       if (open_file->invalidHandle) {
-                               rc = cifs_reopen_file(open_file, false);
-                               if (rc != 0)
-                                       break;
-                       }
-                       io_parms.netfid = open_file->netfid;
-                       io_parms.pid = pid;
-                       io_parms.tcon = pTcon;
-                       io_parms.offset = *poffset;
-                       io_parms.length = cur_len;
-                       rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send,
-                                          npages, 0);
-               } while (rc == -EAGAIN);
-
-               for (i = 0; i < npages; i++)
-                       kunmap(pages[i]);
-
-               if (written) {
-                       len -= written;
-                       total_written += written;
-                       spin_lock(&inode->i_lock);
-                       cifs_update_eof(CIFS_I(inode), *poffset, written);
-                       spin_unlock(&inode->i_lock);
-                       *poffset += written;
-               } else if (rc < 0) {
-                       if (!total_written)
-                               total_written = rc;
+               wdata->sync_mode = WB_SYNC_ALL;
+               wdata->nr_pages = nr_pages;
+               wdata->offset = (__u64)offset;
+               wdata->cfile = cifsFileInfo_get(open_file);
+               wdata->pid = pid;
+               wdata->bytes = cur_len;
+               wdata->marshal_iov = cifs_uncached_marshal_iov;
+               rc = cifs_uncached_retry_writev(wdata);
+               if (rc) {
+                       kref_put(&wdata->refcount, cifs_writedata_release);
                        break;
                }
 
-               /* get length and number of kvecs of the next write */
-               npages = get_numpages(cifs_sb->wsize, len, &cur_len);
+               list_add_tail(&wdata->list, &wdata_list);
+               offset += cur_len;
+               len -= cur_len;
        } while (len > 0);
 
-       if (total_written > 0) {
-               spin_lock(&inode->i_lock);
-               if (*poffset > inode->i_size)
-                       i_size_write(inode, *poffset);
-               spin_unlock(&inode->i_lock);
+       /*
+        * If at least one write was successfully sent, then discard any rc
+        * value from the later writes. If the other write succeeds, then
+        * we'll end up returning whatever was written. If it fails, then
+        * we'll get a new rc value from that.
+        */
+       if (!list_empty(&wdata_list))
+               rc = 0;
+
+       /*
+        * Wait for and collect replies for any successful sends in order of
+        * increasing offset. Once an error is hit or we get a fatal signal
+        * while waiting, then return without waiting for any more replies.
+        */
+restart_loop:
+       list_for_each_entry_safe(wdata, tmp, &wdata_list, list) {
+               if (!rc) {
+                       /* FIXME: freezable too? */
+                       rc = wait_for_completion_killable(&wdata->done);
+                       if (rc)
+                               rc = -EINTR;
+                       else if (wdata->result)
+                               rc = wdata->result;
+                       else
+                               total_written += wdata->bytes;
+
+                       /* resend call if it's a retryable error */
+                       if (rc == -EAGAIN) {
+                               rc = cifs_uncached_retry_writev(wdata);
+                               goto restart_loop;
+                       }
+               }
+               list_del_init(&wdata->list);
+               kref_put(&wdata->refcount, cifs_writedata_release);
        }
 
-       cifs_stats_bytes_written(pTcon, total_written);
-       mark_inode_dirty_sync(inode);
+       if (total_written > 0)
+               *poffset += total_written;
 
-       for (i = 0; i < num_pages; i++)
-               put_page(pages[i]);
-       kfree(to_send);
-       kfree(pages);
-       FreeXid(xid);
-       return total_written;
+       cifs_stats_bytes_written(tcon, total_written);
+       return total_written ? total_written : (ssize_t)rc;
 }
 
 ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,