Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f25bba8 authored by Al Viro's avatar Al Viro
Browse files

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 6130f531
Loading
Loading
Loading
Loading
+17 −45
Original line number Diff line number Diff line
@@ -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;