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:
parent
6130f5315e
commit
7f25bba819
1 changed files with 17 additions and 45 deletions
|
@ -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 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
|
|||
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 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
|
|||
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 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
|
|||
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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue