cifs_iovec_read: keep iov_iter between the calls of cifs_readdata_to_iov()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 4 Feb 2014 19:07:43 +0000 (14:07 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 2 Apr 2014 03:19:23 +0000 (23:19 -0400)
... we are doing them on adjacent parts of file, so what happens is that
each subsequent call works to rebuild the iov_iter to exact state it
had been abandoned in by previous one.  Just keep it through the entire
cifs_iovec_read().  And use copy_page_to_iter() instead of doing
kmap/copy_to_user/kunmap manually...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/cifs/file.c

index df414db74ab96d27c48f961b8e741f528f993a41..ad63e4740afff7085eaf40ab2d4cecedeaeefb4e 100644 (file)
@@ -2727,56 +2727,27 @@ cifs_retry_async_readv(struct cifs_readdata *rdata)
 /**
  * cifs_readdata_to_iov - copy data from pages in response to an iovec
  * @rdata:     the readdata response with list of pages holding data
- * @iov:       vector in which we should copy the data
- * @nr_segs:   number of segments in vector
- * @offset:    offset into file of the first iovec
- * @copied:    used to return the amount of data copied to the iov
+ * @iter:      destination for our data
  *
  * This function copies data from a list of pages in a readdata response into
  * an array of iovecs. It will first calculate where the data should go
  * based on the info in the readdata and then copy the data into that spot.
  */
-static ssize_t
-cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
-                       unsigned long nr_segs, loff_t offset, ssize_t *copied)
+static int
+cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
 {
-       int rc = 0;
-       struct iov_iter ii;
-       size_t pos = rdata->offset - offset;
-       ssize_t remaining = rdata->bytes;
-       unsigned char *pdata;
+       size_t remaining = rdata->bytes;
        unsigned int i;
 
-       /* set up iov_iter and advance to the correct offset */
-       iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0);
-       iov_iter_advance(&ii, pos);
-
-       *copied = 0;
        for (i = 0; i < rdata->nr_pages; i++) {
-               ssize_t copy;
                struct page *page = rdata->pages[i];
-
-               /* copy a whole page or whatever's left */
-               copy = min_t(ssize_t, remaining, PAGE_SIZE);
-
-               /* ...but limit it to whatever space is left in the iov */
-               copy = min_t(ssize_t, copy, iov_iter_count(&ii));
-
-               /* go while there's data to be copied and no errors */
-               if (copy && !rc) {
-                       pdata = kmap(page);
-                       rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset,
-                                               (int)copy);
-                       kunmap(page);
-                       if (!rc) {
-                               *copied += copy;
-                               remaining -= copy;
-                               iov_iter_advance(&ii, copy);
-                       }
-               }
+               size_t copy = min(remaining, PAGE_SIZE);
+               size_t written = copy_page_to_iter(page, 0, copy, iter);
+               remaining -= written;
+               if (written < copy && iov_iter_count(iter) > 0)
+                       break;
        }
-
-       return rc;
+       return remaining ? -EFAULT : 0;
 }
 
 static void
@@ -2851,6 +2822,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
        struct cifsFileInfo *open_file;
        struct cifs_readdata *rdata, *tmp;
        struct list_head rdata_list;
+       struct iov_iter to;
        pid_t pid;
 
        if (!nr_segs)
@@ -2860,6 +2832,8 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
        if (!len)
                return 0;
 
+       iov_iter_init(&to, iov, nr_segs, len, 0);
+
        INIT_LIST_HEAD(&rdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        open_file = file->private_data;
@@ -2917,12 +2891,11 @@ error:
        if (!list_empty(&rdata_list))
                rc = 0;
 
+       len = iov_iter_count(&to);
        /* the loop below should proceed in the order of increasing offsets */
        list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
        again:
                if (!rc) {
-                       ssize_t copied;
-
                        /* FIXME: freezable sleep too? */
                        rc = wait_for_completion_killable(&rdata->done);
                        if (rc)
@@ -2935,10 +2908,7 @@ error:
                                        goto again;
                                }
                        } else {
-                               rc = cifs_readdata_to_iov(rdata, iov,
-                                                       nr_segs, *poffset,
-                                                       &copied);
-                               total_read += copied;
+                               rc = cifs_readdata_to_iov(rdata, &to);
                        }
 
                }
@@ -2946,6 +2916,8 @@ error:
                kref_put(&rdata->refcount, cifs_uncached_readdata_release);
        }
 
+       total_read = len - iov_iter_count(&to);
+
        cifs_stats_bytes_read(tcon, total_read);
        *poffset += total_read;