cifs_iovec_read: keep iov_iter between the calls of cifs_readdata_to_iov()

... 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>
This commit is contained in:
Al Viro 2014-02-04 14:07:43 -05:00
parent 6130f5315e
commit 7f25bba819
1 changed files with 17 additions and 45 deletions

View 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;