All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH v2 0/3] cifs: In-progress conversion to use iov_iters instead of page lists
@ 2022-01-06 21:31 David Howells
  2022-01-06 21:32 ` [PATCH v2 1/3] cifs: Use netfslib David Howells
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: David Howells @ 2022-01-06 21:31 UTC (permalink / raw)
  To: smfrench; +Cc: nspmangalore, dhowells, jlayton, linux-cifs, linux-cachefs


Hi Steve,

For your illumination, here's where I'm currently at with making cifs use
netfslib more fully.  I ended up delving into the realms of converting it
to use iov_iter instead of page lists and doing partial foliation.

It isn't complete yet and some of the patches don't compile, but if you
want a look at what I've been doing...

David
---
David Howells (3):
      cifs: Use netfslib
      cifs: Reverse the way iov_iters are used in reading
      cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst


 fs/cifs/cifsencrypt.c |   40 +-
 fs/cifs/cifsfs.h      |    3 +
 fs/cifs/cifsglob.h    |   28 +-
 fs/cifs/cifsproto.h   |    8 +-
 fs/cifs/cifssmb.c     |  219 ++++++---
 fs/cifs/file.c        | 1041 +++++++++++++----------------------------
 fs/cifs/misc.c        |  109 -----
 fs/cifs/smb2ops.c     |  188 ++++----
 fs/cifs/smb2pdu.c     |   12 +-
 fs/cifs/transport.c   |   41 +-
 include/linux/uio.h   |    7 +
 lib/iov_iter.c        |  136 ++++++
 12 files changed, 746 insertions(+), 1086 deletions(-)



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/3] cifs: Use netfslib
  2022-01-06 21:31 [RFC][PATCH v2 0/3] cifs: In-progress conversion to use iov_iters instead of page lists David Howells
@ 2022-01-06 21:32 ` David Howells
  2022-01-06 21:32 ` [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading David Howells
  2022-01-06 21:32 ` [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst David Howells
  2 siblings, 0 replies; 8+ messages in thread
From: David Howells @ 2022-01-06 21:32 UTC (permalink / raw)
  To: smfrench; +Cc: nspmangalore, dhowells, jlayton, linux-cifs, linux-cachefs


---

 fs/cifs/Kconfig     |    1 
 fs/cifs/cifsglob.h  |    2 
 fs/cifs/cifsproto.h |    3 
 fs/cifs/cifssmb.c   |   16 +
 fs/cifs/connect.c   |   18 +-
 fs/cifs/file.c      |  582 ++++++++++++++-------------------------------------
 fs/cifs/fscache.c   |   35 ---
 fs/cifs/fscache.h   |   52 -----
 fs/cifs/smb2pdu.c   |   10 +
 9 files changed, 201 insertions(+), 518 deletions(-)

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 3b7e3b9e4fd2..c47e2d3a101f 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -2,6 +2,7 @@
 config CIFS
 	tristate "SMB3 and CIFS support (advanced network filesystem)"
 	depends on INET
+	select NETFS_SUPPORT
 	select NLS
 	select CRYPTO
 	select CRYPTO_MD5
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index ba6fbb1ad8f3..a5edae959f60 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1320,6 +1320,7 @@ struct cifs_aio_ctx {
 
 /* asynchronous read support */
 struct cifs_readdata {
+	struct netfs_read_subrequest	*subreq;
 	struct kref			refcount;
 	struct list_head		list;
 	struct completion		done;
@@ -1338,6 +1339,7 @@ struct cifs_readdata {
 	int (*copy_into_pages)(struct TCP_Server_Info *server,
 				struct cifs_readdata *rdata,
 				struct iov_iter *iter);
+	struct iov_iter			iter;
 	struct kvec			iov[2];
 	struct TCP_Server_Info		*server;
 #ifdef CONFIG_CIFS_SMB_DIRECT
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 4f5a3e857df4..99c239dca807 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -237,6 +237,9 @@ extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
 					unsigned int page_offset,
 					unsigned int to_read);
 extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb);
+extern int cifs_read_iter_from_socket(struct TCP_Server_Info *server,
+				      struct iov_iter *iter,
+				      unsigned int to_read);
 extern int cifs_match_super(struct super_block *, void *);
 extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx);
 extern void cifs_umount(struct cifs_sb_info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 243d17696f06..e068e3bda304 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -23,6 +23,7 @@
 #include <linux/swap.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/uaccess.h>
+#include <linux/netfs.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsacl.h"
@@ -1332,11 +1333,11 @@ int
 cifs_discard_remaining_data(struct TCP_Server_Info *server)
 {
 	unsigned int rfclen = server->pdu_size;
-	int remaining = rfclen + server->vals->header_preamble_size -
+	size_t remaining = rfclen + server->vals->header_preamble_size -
 		server->total_read;
 
 	while (remaining > 0) {
-		int length;
+		ssize_t length;
 
 		length = cifs_discard_from_socket(server,
 				min_t(size_t, remaining,
@@ -1551,7 +1552,15 @@ cifs_readv_callback(struct mid_q_entry *mid)
 		rdata->result = -EIO;
 	}
 
-	queue_work(cifsiod_wq, &rdata->work);
+	if (rdata->subreq) {
+		netfs_subreq_terminated(rdata->subreq,
+					(rdata->result == 0 || rdata->result == -EAGAIN) ?
+					rdata->got_bytes : rdata->result,
+					false);
+		kref_put(&rdata->refcount, cifs_readdata_release);
+	} else {
+		queue_work(cifsiod_wq, &rdata->work);
+	}
 	DeleteMidQEntry(mid);
 	add_credits(server, &credits, 0);
 }
@@ -1996,7 +2005,6 @@ cifs_writev_complete(struct work_struct *work)
 		else if (wdata->result < 0)
 			SetPageError(page);
 		end_page_writeback(page);
-		cifs_readpage_to_fscache(inode, page);
 		put_page(page);
 	}
 	if (wdata->result != -EAGAIN)
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a52fd3a30c88..f6a145301e34 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -162,7 +162,7 @@ static void cifs_resolve_server(struct work_struct *work)
 	mutex_unlock(&server->srv_mutex);
 }
 
-/**
+/*
  * Mark all sessions and tcons for reconnect.
  *
  * @server needs to be previously set to CifsNeedReconnect.
@@ -656,6 +656,22 @@ cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
 	return cifs_readv_from_socket(server, &smb_msg);
 }
 
+int
+cifs_read_iter_from_socket(struct TCP_Server_Info *server, struct iov_iter *iter,
+			   unsigned int to_read)
+{
+	struct msghdr smb_msg;
+	int ret;
+
+	smb_msg.msg_iter = *iter;
+	if (smb_msg.msg_iter.count > to_read)
+		smb_msg.msg_iter.count = to_read;
+	ret = cifs_readv_from_socket(server, &smb_msg);
+	if (ret > 0)
+		iov_iter_advance(iter, ret);
+	return ret;
+}
+
 static bool
 is_smb_response(struct TCP_Server_Info *server, unsigned char type)
 {
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index d872f6fe8e7d..a3b9c0ecf02c 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -21,6 +21,7 @@
 #include <linux/slab.h>
 #include <linux/swap.h>
 #include <linux/mm.h>
+#include <linux/netfs.h>
 #include <asm/div64.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -2614,6 +2615,7 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
 	struct inode *inode = mapping->host;
 	struct cifsFileInfo *cfile = file->private_data;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+	struct folio *folio = page_folio(page);
 	__u32 pid;
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
@@ -2624,14 +2626,14 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
 	cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n",
 		 page, pos, copied);
 
-	if (PageChecked(page)) {
+	if (folio_test_checked(folio)) {
 		if (copied == len)
-			SetPageUptodate(page);
-		ClearPageChecked(page);
-	} else if (!PageUptodate(page) && copied == PAGE_SIZE)
-		SetPageUptodate(page);
+			folio_mark_uptodate(folio);
+		folio_clear_checked(folio);
+	} else if (!folio_test_uptodate(folio) && copied == PAGE_SIZE)
+		folio_mark_uptodate(folio);
 
-	if (!PageUptodate(page)) {
+	if (!folio_test_uptodate(folio)) {
 		char *page_data;
 		unsigned offset = pos & (PAGE_SIZE - 1);
 		unsigned int xid;
@@ -4110,98 +4112,6 @@ cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
 	return rc;
 }
 
-static ssize_t
-cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
-{
-	int rc = -EACCES;
-	unsigned int bytes_read = 0;
-	unsigned int total_read;
-	unsigned int current_read_size;
-	unsigned int rsize;
-	struct cifs_sb_info *cifs_sb;
-	struct cifs_tcon *tcon;
-	struct TCP_Server_Info *server;
-	unsigned int xid;
-	char *cur_offset;
-	struct cifsFileInfo *open_file;
-	struct cifs_io_parms io_parms = {0};
-	int buf_type = CIFS_NO_BUFFER;
-	__u32 pid;
-
-	xid = get_xid();
-	cifs_sb = CIFS_FILE_SB(file);
-
-	/* FIXME: set up handlers for larger reads and/or convert to async */
-	rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize);
-
-	if (file->private_data == NULL) {
-		rc = -EBADF;
-		free_xid(xid);
-		return rc;
-	}
-	open_file = file->private_data;
-	tcon = tlink_tcon(open_file->tlink);
-	server = cifs_pick_channel(tcon->ses);
-
-	if (!server->ops->sync_read) {
-		free_xid(xid);
-		return -ENOSYS;
-	}
-
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
-		pid = open_file->pid;
-	else
-		pid = current->tgid;
-
-	if ((file->f_flags & O_ACCMODE) == O_WRONLY)
-		cifs_dbg(FYI, "attempting read on write only file instance\n");
-
-	for (total_read = 0, cur_offset = read_data; read_size > total_read;
-	     total_read += bytes_read, cur_offset += bytes_read) {
-		do {
-			current_read_size = min_t(uint, read_size - total_read,
-						  rsize);
-			/*
-			 * For windows me and 9x we do not want to request more
-			 * than it negotiated since it will refuse the read
-			 * then.
-			 */
-			if (!(tcon->ses->capabilities &
-				tcon->ses->server->vals->cap_large_files)) {
-				current_read_size = min_t(uint,
-					current_read_size, CIFSMaxBufSize);
-			}
-			if (open_file->invalidHandle) {
-				rc = cifs_reopen_file(open_file, true);
-				if (rc != 0)
-					break;
-			}
-			io_parms.pid = pid;
-			io_parms.tcon = tcon;
-			io_parms.offset = *offset;
-			io_parms.length = current_read_size;
-			io_parms.server = server;
-			rc = server->ops->sync_read(xid, &open_file->fid, &io_parms,
-						    &bytes_read, &cur_offset,
-						    &buf_type);
-		} while (rc == -EAGAIN);
-
-		if (rc || (bytes_read == 0)) {
-			if (total_read) {
-				break;
-			} else {
-				free_xid(xid);
-				return rc;
-			}
-		} else {
-			cifs_stats_bytes_read(tcon, total_read);
-			*offset += bytes_read;
-		}
-	}
-	free_xid(xid);
-	return total_read;
-}
-
 /*
  * If the page is mmap'ed into a process' page tables, then we need to make
  * sure that it doesn't change while being written back.
@@ -4211,13 +4121,19 @@ cifs_page_mkwrite(struct vm_fault *vmf)
 {
 	struct page *page = vmf->page;
 
+	/* Wait for the page to be written to the cache before we allow it to
+	 * be modified.  We then assume the entire page will need writing back.
+	 */
 #ifdef CONFIG_CIFS_FSCACHE
 	if (PageFsCache(page) &&
 	    wait_on_page_fscache_killable(page) < 0)
 		return VM_FAULT_RETRY;
 #endif
 
-	lock_page(page);
+	wait_on_page_writeback(page);
+
+	if (lock_page_killable(page) < 0)
+		return VM_FAULT_RETRY;
 	return VM_FAULT_LOCKED;
 }
 
@@ -4264,40 +4180,6 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	return rc;
 }
 
-static void
-cifs_readv_complete(struct work_struct *work)
-{
-	unsigned int i, got_bytes;
-	struct cifs_readdata *rdata = container_of(work,
-						struct cifs_readdata, work);
-
-	got_bytes = rdata->got_bytes;
-	for (i = 0; i < rdata->nr_pages; i++) {
-		struct page *page = rdata->pages[i];
-
-		lru_cache_add(page);
-
-		if (rdata->result == 0 ||
-		    (rdata->result == -EAGAIN && got_bytes)) {
-			flush_dcache_page(page);
-			SetPageUptodate(page);
-		} else
-			SetPageError(page);
-
-		unlock_page(page);
-
-		if (rdata->result == 0 ||
-		    (rdata->result == -EAGAIN && got_bytes))
-			cifs_readpage_to_fscache(rdata->mapping->host, page);
-
-		got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes);
-
-		put_page(page);
-		rdata->pages[i] = NULL;
-	}
-	kref_put(&rdata->refcount, cifs_readdata_release);
-}
-
 static int
 readpages_fill_pages(struct TCP_Server_Info *server,
 		     struct cifs_readdata *rdata, struct iov_iter *iter,
@@ -4372,8 +4254,7 @@ readpages_fill_pages(struct TCP_Server_Info *server,
 			result = n;
 #endif
 		else
-			result = cifs_read_page_from_socket(
-					server, page, page_offset, n);
+			result = cifs_read_iter_from_socket(server, &rdata->iter, n);
 		if (result < 0)
 			break;
 
@@ -4388,7 +4269,12 @@ static int
 cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
 			       struct cifs_readdata *rdata, unsigned int len)
 {
-	return readpages_fill_pages(server, rdata, NULL, len);
+	int rc;
+
+	rc = cifs_read_iter_from_socket(server, &rdata->iter, len);
+	if (rc > 0)
+		rdata->got_bytes += rc;
+	return rc;
 }
 
 static int
@@ -4396,292 +4282,158 @@ cifs_readpages_copy_into_pages(struct TCP_Server_Info *server,
 			       struct cifs_readdata *rdata,
 			       struct iov_iter *iter)
 {
+	printk("cifs_readpages_copy_into_pages\n");
 	return readpages_fill_pages(server, rdata, iter, iter->count);
 }
 
-static int
-readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
-		    unsigned int rsize, struct list_head *tmplist,
-		    unsigned int *nr_pages, loff_t *offset, unsigned int *bytes)
-{
-	struct page *page, *tpage;
-	unsigned int expected_index;
-	int rc;
-	gfp_t gfp = readahead_gfp_mask(mapping);
-
-	INIT_LIST_HEAD(tmplist);
-
-	page = lru_to_page(page_list);
-
-	/*
-	 * Lock the page and put it in the cache. Since no one else
-	 * should have access to this page, we're safe to simply set
-	 * PG_locked without checking it first.
-	 */
-	__SetPageLocked(page);
-	rc = add_to_page_cache_locked(page, mapping,
-				      page->index, gfp);
-
-	/* give up if we can't stick it in the cache */
-	if (rc) {
-		__ClearPageLocked(page);
-		return rc;
-	}
-
-	/* move first page to the tmplist */
-	*offset = (loff_t)page->index << PAGE_SHIFT;
-	*bytes = PAGE_SIZE;
-	*nr_pages = 1;
-	list_move_tail(&page->lru, tmplist);
-
-	/* now try and add more pages onto the request */
-	expected_index = page->index + 1;
-	list_for_each_entry_safe_reverse(page, tpage, page_list, lru) {
-		/* discontinuity ? */
-		if (page->index != expected_index)
-			break;
-
-		/* would this page push the read over the rsize? */
-		if (*bytes + PAGE_SIZE > rsize)
-			break;
-
-		__SetPageLocked(page);
-		rc = add_to_page_cache_locked(page, mapping, page->index, gfp);
-		if (rc) {
-			__ClearPageLocked(page);
-			break;
-		}
-		list_move_tail(&page->lru, tmplist);
-		(*bytes) += PAGE_SIZE;
-		expected_index++;
-		(*nr_pages)++;
-	}
-	return rc;
-}
-
-static int cifs_readpages(struct file *file, struct address_space *mapping,
-	struct list_head *page_list, unsigned num_pages)
+/*
+ * Issue a read operation on behalf of the netfs helper functions.  We're asked
+ * to make a read of a certain size at a point in the file.  We are permitted
+ * to only read a portion of that, but as long as we read something, the netfs
+ * helper will call us again so that we can issue another read.
+ */
+static void cifs_req_issue_op(struct netfs_read_subrequest *subreq)
 {
-	int rc;
-	int err = 0;
-	struct list_head tmplist;
-	struct cifsFileInfo *open_file = file->private_data;
-	struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+	struct netfs_read_request *rreq = subreq->rreq;
 	struct TCP_Server_Info *server;
-	pid_t pid;
+	struct cifs_readdata *rdata;
+	struct cifsFileInfo *open_file = rreq->netfs_priv;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
+	struct cifs_credits credits_on_stack, *credits = &credits_on_stack;
 	unsigned int xid;
+	pid_t pid;
+	int rc = 0;
+	unsigned int rsize;
 
 	xid = get_xid();
-	/*
-	 * Reads as many pages as possible from fscache. Returns -ENOBUFS
-	 * immediately if the cookie is negative
-	 *
-	 * After this point, every page in the list might have PG_fscache set,
-	 * so we will need to clean that up off of every page we don't use.
-	 */
-	rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,
-					 &num_pages);
-	if (rc == 0) {
-		free_xid(xid);
-		return rc;
-	}
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
 		pid = open_file->pid;
 	else
-		pid = current->tgid;
+		pid = current->tgid; // Ummm...  This may be a workqueue
 
-	rc = 0;
 	server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses);
 
-	cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n",
-		 __func__, file, mapping, num_pages);
+	cifs_dbg(FYI, "%s: op=%08x[%x] mapping=%p len=%zu/%zu\n",
+		 __func__, rreq->debug_id, subreq->debug_index, rreq->mapping,
+		 subreq->transferred, subreq->len);
 
-	/*
-	 * Start with the page at end of list and move it to private
-	 * list. Do the same with any following pages until we hit
-	 * the rsize limit, hit an index discontinuity, or run out of
-	 * pages. Issue the async read and then start the loop again
-	 * until the list is empty.
-	 *
-	 * Note that list order is important. The page_list is in
-	 * the order of declining indexes. When we put the pages in
-	 * the rdata->pages, then we want them in increasing order.
-	 */
-	while (!list_empty(page_list) && !err) {
-		unsigned int i, nr_pages, bytes, rsize;
-		loff_t offset;
-		struct page *page, *tpage;
-		struct cifs_readdata *rdata;
-		struct cifs_credits credits_on_stack;
-		struct cifs_credits *credits = &credits_on_stack;
-
-		if (open_file->invalidHandle) {
+	if (open_file->invalidHandle) {
+		do {
 			rc = cifs_reopen_file(open_file, true);
-			if (rc == -EAGAIN)
-				continue;
-			else if (rc)
-				break;
-		}
-
-		rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
-						   &rsize, credits);
+		} while (rc == -EAGAIN);
 		if (rc)
-			break;
-
-		/*
-		 * Give up immediately if rsize is too small to read an entire
-		 * page. The VFS will fall back to readpage. We should never
-		 * reach this point however since we set ra_pages to 0 when the
-		 * rsize is smaller than a cache page.
-		 */
-		if (unlikely(rsize < PAGE_SIZE)) {
-			add_credits_and_wake_if(server, credits, 0);
-			free_xid(xid);
-			return 0;
-		}
-
-		nr_pages = 0;
-		err = readpages_get_pages(mapping, page_list, rsize, &tmplist,
-					 &nr_pages, &offset, &bytes);
-		if (!nr_pages) {
-			add_credits_and_wake_if(server, credits, 0);
-			break;
-		}
-
-		rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete);
-		if (!rdata) {
-			/* best to give up if we're out of mem */
-			list_for_each_entry_safe(page, tpage, &tmplist, lru) {
-				list_del(&page->lru);
-				lru_cache_add(page);
-				unlock_page(page);
-				put_page(page);
-			}
-			rc = -ENOMEM;
-			add_credits_and_wake_if(server, credits, 0);
-			break;
-		}
+			goto out;
+	}
 
-		rdata->cfile = cifsFileInfo_get(open_file);
-		rdata->server = server;
-		rdata->mapping = mapping;
-		rdata->offset = offset;
-		rdata->bytes = bytes;
-		rdata->pid = pid;
-		rdata->pagesz = PAGE_SIZE;
-		rdata->tailsz = PAGE_SIZE;
-		rdata->read_into_pages = cifs_readpages_read_into_pages;
-		rdata->copy_into_pages = cifs_readpages_copy_into_pages;
-		rdata->credits = credits_on_stack;
+	rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, &rsize, credits);
+	if (rc)
+		goto out;
 
-		list_for_each_entry_safe(page, tpage, &tmplist, lru) {
-			list_del(&page->lru);
-			rdata->pages[rdata->nr_pages++] = page;
-		}
+	rdata = cifs_readdata_alloc(0, NULL);
+	if (!rdata) {
+		add_credits_and_wake_if(server, credits, 0);
+		rc = -ENOMEM;
+		goto out;
+	}
 
-		rc = adjust_credits(server, &rdata->credits, rdata->bytes);
+	rdata->subreq = subreq;
+	rdata->cfile = cifsFileInfo_get(open_file);
+	rdata->server = server;
+	rdata->mapping = rreq->mapping;
+	rdata->offset = subreq->start + subreq->transferred;
+	rdata->bytes = subreq->len   - subreq->transferred;
+	rdata->pid = pid;
+	rdata->pagesz = PAGE_SIZE;
+	rdata->tailsz = PAGE_SIZE;
+	rdata->read_into_pages = cifs_readpages_read_into_pages;
+	rdata->copy_into_pages = cifs_readpages_copy_into_pages;
+	rdata->credits = credits_on_stack;
 
-		if (!rc) {
-			if (rdata->cfile->invalidHandle)
-				rc = -EAGAIN;
-			else
-				rc = server->ops->async_readv(rdata);
-		}
+	iov_iter_xarray(&rdata->iter, READ, &rreq->mapping->i_pages,
+			rdata->offset, rdata->bytes);
 
-		if (rc) {
-			add_credits_and_wake_if(server, &rdata->credits, 0);
-			for (i = 0; i < rdata->nr_pages; i++) {
-				page = rdata->pages[i];
-				lru_cache_add(page);
-				unlock_page(page);
-				put_page(page);
-			}
-			/* Fallback to the readpage in error/reconnect cases */
-			kref_put(&rdata->refcount, cifs_readdata_release);
-			break;
-		}
+	rc = adjust_credits(server, &rdata->credits, rdata->bytes);
+	if (!rc) {
+		if (rdata->cfile->invalidHandle)
+			rc = -EAGAIN;
+		else
+			rc = server->ops->async_readv(rdata);
+	}
 
+	if (rc) {
+		add_credits_and_wake_if(server, &rdata->credits, 0);
+		/* Fallback to the readpage in error/reconnect cases */
 		kref_put(&rdata->refcount, cifs_readdata_release);
 	}
 
+	kref_put(&rdata->refcount, cifs_readdata_release);
+
+out:
 	free_xid(xid);
-	return rc;
+	if (rc)
+		netfs_subreq_terminated(subreq, rc, false);
+}
+
+static void cifs_init_rreq(struct netfs_read_request *rreq, struct file *file)
+{
+	rreq->netfs_priv = file->private_data;
 }
 
 /*
- * cifs_readpage_worker must be called with the page pinned
+ * Expand the size of a readahead to the size of the rsize, if at least as
+ * large as a page, allowing for the possibility that rsize is not pow-2
+ * aligned.
  */
-static int cifs_readpage_worker(struct file *file, struct page *page,
-	loff_t *poffset)
+static void cifs_expand_readahead(struct netfs_read_request *rreq)
 {
-	char *read_data;
-	int rc;
-
-	/* Is the page cached? */
-	rc = cifs_readpage_from_fscache(file_inode(file), page);
-	if (rc == 0)
-		goto read_complete;
-
-	read_data = kmap(page);
-	/* for reads over a certain size could initiate async read ahead */
-
-	rc = cifs_read(file, read_data, PAGE_SIZE, poffset);
+	struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
+	unsigned int rsize = cifs_sb->ctx->rsize;
+	loff_t misalignment, i_size = i_size_read(rreq->inode);
 
-	if (rc < 0)
-		goto io_error;
-	else
-		cifs_dbg(FYI, "Bytes read %d\n", rc);
+	if (rsize < PAGE_SIZE)
+		return;
 
-	/* we do not want atime to be less than mtime, it broke some apps */
-	file_inode(file)->i_atime = current_time(file_inode(file));
-	if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime)))
-		file_inode(file)->i_atime = file_inode(file)->i_mtime;
+	if (rsize < INT_MAX)
+		rsize = roundup_pow_of_two(rsize);
 	else
-		file_inode(file)->i_atime = current_time(file_inode(file));
-
-	if (PAGE_SIZE > rc)
-		memset(read_data + rc, 0, PAGE_SIZE - rc);
-
-	flush_dcache_page(page);
-	SetPageUptodate(page);
-
-	/* send this page to the cache */
-	cifs_readpage_to_fscache(file_inode(file), page);
-
-	rc = 0;
+		rsize = ((unsigned int)INT_MAX + 1) / 2;
 
-io_error:
-	kunmap(page);
-	unlock_page(page);
+	misalignment = rreq->start & (rsize - 1);
+	if (misalignment) {
+		rreq->start -= misalignment;
+		rreq->len += misalignment;
+	}
 
-read_complete:
-	return rc;
+	rreq->len = round_up(rreq->len, rsize);
+	if (rreq->start < i_size && rreq->len > i_size - rreq->start)
+		rreq->len = i_size - rreq->start;
 }
 
-static int cifs_readpage(struct file *file, struct page *page)
+static void cifs_rreq_done(struct netfs_read_request *rreq)
 {
-	loff_t offset = page_file_offset(page);
-	int rc = -EACCES;
-	unsigned int xid;
-
-	xid = get_xid();
-
-	if (file->private_data == NULL) {
-		rc = -EBADF;
-		free_xid(xid);
-		return rc;
-	}
-
-	cifs_dbg(FYI, "readpage %p at offset %d 0x%x\n",
-		 page, (int)offset, (int)offset);
+	struct inode *inode = rreq->inode;
 
-	rc = cifs_readpage_worker(file, page, &offset);
+	/* we do not want atime to be less than mtime, it broke some apps */
+	inode->i_atime = current_time(inode);
+	if (timespec64_compare(&inode->i_atime, &inode->i_mtime))
+		inode->i_atime = inode->i_mtime;
+	else
+		inode->i_atime = current_time(inode);
+}
 
-	free_xid(xid);
-	return rc;
+static void cifs_req_cleanup(struct address_space *mapping, void *netfs_priv)
+{
 }
 
+const struct netfs_read_request_ops cifs_req_ops = {
+	.init_rreq		= cifs_init_rreq,
+	.expand_readahead	= cifs_expand_readahead,
+	.issue_op		= cifs_req_issue_op,
+	.done			= cifs_rreq_done,
+	.cleanup		= cifs_req_cleanup,
+};
+
 static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
 {
 	struct cifsFileInfo *open_file;
@@ -4731,34 +4483,21 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned flags,
 			struct page **pagep, void **fsdata)
 {
-	int oncethru = 0;
-	pgoff_t index = pos >> PAGE_SHIFT;
-	loff_t offset = pos & (PAGE_SIZE - 1);
-	loff_t page_start = pos & PAGE_MASK;
-	loff_t i_size;
-	struct page *page;
-	int rc = 0;
+	struct folio *folio;
+	int rc;
 
 	cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len);
 
-start:
-	page = grab_cache_page_write_begin(mapping, index, flags);
-	if (!page) {
-		rc = -ENOMEM;
-		goto out;
-	}
-
-	if (PageUptodate(page))
-		goto out;
-
-	/*
-	 * If we write a full page it will be up to date, no need to read from
-	 * the server. If the write is short, we'll end up doing a sync write
-	 * instead.
+	/* Prefetch area to be written into the cache if we're caching this
+	 * file.  We need to do this before we get a lock on the page in case
+	 * there's more than one writer competing for the same cache block.
 	 */
-	if (len == PAGE_SIZE)
-		goto out;
+	rc = netfs_write_begin(file, mapping, pos, len, flags, &folio, fsdata,
+			       &cifs_req_ops, NULL);
+	if (rc < 0)
+		return rc;
 
+#if 0
 	/*
 	 * optimize away the read when we have an oplock, and we're not
 	 * expecting to use any of the data we'd be reading in. That
@@ -4773,34 +4512,17 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
 					   offset + len,
 					   PAGE_SIZE);
 			/*
-			 * PageChecked means that the parts of the page
-			 * to which we're not writing are considered up
-			 * to date. Once the data is copied to the
-			 * page, it can be set uptodate.
+			 * Marking a folio checked means that the parts of the
+			 * page to which we're not writing are considered up to
+			 * date. Once the data is copied to the page, it can be
+			 * set uptodate.
 			 */
-			SetPageChecked(page);
+			folio_set_checked(folio);
 			goto out;
 		}
 	}
-
-	if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) {
-		/*
-		 * might as well read a page, it is fast enough. If we get
-		 * an error, we don't need to return it. cifs_write_end will
-		 * do a sync write instead since PG_uptodate isn't set.
-		 */
-		cifs_readpage_worker(file, page, &page_start);
-		put_page(page);
-		oncethru = 1;
-		goto start;
-	} else {
-		/* we could try using another file handle if there is one -
-		   but how would we lock it to prevent close of that handle
-		   racing with this read? In any case
-		   this will be written out by write_end so is fine */
-	}
-out:
-	*pagep = page;
+#endif
+	*pagep = folio_page(folio, pos / PAGE_SIZE);
 	return rc;
 }
 
@@ -5010,9 +4732,19 @@ static int cifs_set_page_dirty(struct page *page)
 #define cifs_set_page_dirty __set_page_dirty_nobuffers
 #endif
 
+static int cifs_readpage(struct file *file, struct page *page)
+{
+	return netfs_readpage(file, page_folio(page), &cifs_req_ops, NULL);
+}
+
+static void cifs_readahead(struct readahead_control *ractl)
+{
+	netfs_readahead(ractl, &cifs_req_ops, NULL);
+}
+
 const struct address_space_operations cifs_addr_ops = {
 	.readpage = cifs_readpage,
-	.readpages = cifs_readpages,
+	.readahead = cifs_readahead,
 	.writepage = cifs_writepage,
 	.writepages = cifs_writepages,
 	.write_begin = cifs_write_begin,
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index efaac4d5ff55..76e1edc99aec 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -133,38 +133,3 @@ void cifs_fscache_release_inode_cookie(struct inode *inode)
 		cifsi->fscache = NULL;
 	}
 }
-
-/*
- * Retrieve a page from FS-Cache
- */
-int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
-{
-	cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n",
-		 __func__, CIFS_I(inode)->fscache, page, inode);
-	return -ENOBUFS; // Needs conversion to using netfslib
-}
-
-/*
- * Retrieve a set of pages from FS-Cache
- */
-int __cifs_readpages_from_fscache(struct inode *inode,
-				struct address_space *mapping,
-				struct list_head *pages,
-				unsigned *nr_pages)
-{
-	cifs_dbg(FYI, "%s: (0x%p/%u/0x%p)\n",
-		 __func__, CIFS_I(inode)->fscache, *nr_pages, inode);
-	return -ENOBUFS; // Needs conversion to using netfslib
-}
-
-void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)
-{
-	struct cifsInodeInfo *cifsi = CIFS_I(inode);
-
-	WARN_ON(!cifsi->fscache);
-
-	cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n",
-		 __func__, cifsi->fscache, page, inode);
-
-	// Needs conversion to using netfslib
-}
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
index 0fc3f9252c84..9718feac1d68 100644
--- a/fs/cifs/fscache.h
+++ b/fs/cifs/fscache.h
@@ -58,46 +58,11 @@ void cifs_fscache_fill_coherency(struct inode *inode,
 }
 
 
-extern int cifs_fscache_release_page(struct page *page, gfp_t gfp);
-extern int __cifs_readpage_from_fscache(struct inode *, struct page *);
-extern int __cifs_readpages_from_fscache(struct inode *,
-					 struct address_space *,
-					 struct list_head *,
-					 unsigned *);
-extern void __cifs_readpage_to_fscache(struct inode *, struct page *);
-
 static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode)
 {
 	return CIFS_I(inode)->fscache;
 }
 
-static inline int cifs_readpage_from_fscache(struct inode *inode,
-					     struct page *page)
-{
-	if (CIFS_I(inode)->fscache)
-		return __cifs_readpage_from_fscache(inode, page);
-
-	return -ENOBUFS;
-}
-
-static inline int cifs_readpages_from_fscache(struct inode *inode,
-					      struct address_space *mapping,
-					      struct list_head *pages,
-					      unsigned *nr_pages)
-{
-	if (CIFS_I(inode)->fscache)
-		return __cifs_readpages_from_fscache(inode, mapping, pages,
-						     nr_pages);
-	return -ENOBUFS;
-}
-
-static inline void cifs_readpage_to_fscache(struct inode *inode,
-					    struct page *page)
-{
-	if (PageFsCache(page))
-		__cifs_readpage_to_fscache(inode, page);
-}
-
 #else /* CONFIG_CIFS_FSCACHE */
 static inline
 void cifs_fscache_fill_coherency(struct inode *inode,
@@ -113,23 +78,6 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
 static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {}
 static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; }
 
-static inline int
-cifs_readpage_from_fscache(struct inode *inode, struct page *page)
-{
-	return -ENOBUFS;
-}
-
-static inline int cifs_readpages_from_fscache(struct inode *inode,
-					      struct address_space *mapping,
-					      struct list_head *pages,
-					      unsigned *nr_pages)
-{
-	return -ENOBUFS;
-}
-
-static inline void cifs_readpage_to_fscache(struct inode *inode,
-			struct page *page) {}
-
 #endif /* CONFIG_CIFS_FSCACHE */
 
 #endif /* _CIFS_FSCACHE_H */
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 8b3670388cda..ea2f8a2d9e57 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -23,6 +23,7 @@
 #include <linux/uuid.h>
 #include <linux/pagemap.h>
 #include <linux/xattr.h>
+#include <linux/netfs.h>
 #include "cifsglob.h"
 #include "cifsacl.h"
 #include "cifsproto.h"
@@ -4084,7 +4085,14 @@ smb2_readv_callback(struct mid_q_entry *mid)
 				     tcon->tid, tcon->ses->Suid,
 				     rdata->offset, rdata->got_bytes);
 
-	queue_work(cifsiod_wq, &rdata->work);
+	if (rdata->subreq) {
+		netfs_subreq_terminated(rdata->subreq,
+					(rdata->result == 0 || rdata->result == -EAGAIN) ?
+					rdata->got_bytes : rdata->result, false);
+		kref_put(&rdata->refcount, cifs_readdata_release);
+	} else {
+		queue_work(cifsiod_wq, &rdata->work);
+	}
 	DeleteMidQEntry(mid);
 	add_credits(server, &credits, 0);
 }



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading
  2022-01-06 21:31 [RFC][PATCH v2 0/3] cifs: In-progress conversion to use iov_iters instead of page lists David Howells
  2022-01-06 21:32 ` [PATCH v2 1/3] cifs: Use netfslib David Howells
@ 2022-01-06 21:32 ` David Howells
  2022-01-07 14:35     ` kernel test robot
  2022-01-06 21:32 ` [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst David Howells
  2 siblings, 1 reply; 8+ messages in thread
From: David Howells @ 2022-01-06 21:32 UTC (permalink / raw)
  To: smfrench; +Cc: nspmangalore, dhowells, jlayton, linux-cifs, linux-cachefs

Reverse the way iov_iters are used in reading such that the top layer
passes down an iterator for data to be copied into inside
handle_read_data() and cifs_readv_receive().

Get rid of ->read_into_pages() and ->copy_into_pages() and have the callers
just write directly into the iterator instead.

Generalise setup_aio_ctx_iter() and move it to lib/iov_iter.c as
extract_iter_to_iter().  Other filesystems want to take the iterator passed
to ->read_iter() or ->write_iter() and copy the buffer list too.

Note that extract_iter_to_iter() doesn't actually need to make a separate
allocation for the page array.  It can place the page array at the end of
the bvec array storage, provided it traverses both arrays from the 0th
element forwards.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/cifs/cifsglob.h  |    6 -
 fs/cifs/cifsproto.h |    1 
 fs/cifs/cifssmb.c   |   13 ++-
 fs/cifs/file.c      |  239 ++++-----------------------------------------------
 fs/cifs/misc.c      |   90 -------------------
 fs/cifs/smb2ops.c   |   54 ++++--------
 include/linux/uio.h |    3 +
 lib/iov_iter.c      |   94 ++++++++++++++++++++
 8 files changed, 146 insertions(+), 354 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index a5edae959f60..c1665ef5b946 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1333,12 +1333,6 @@ struct cifs_readdata {
 	pid_t				pid;
 	int				result;
 	struct work_struct		work;
-	int (*read_into_pages)(struct TCP_Server_Info *server,
-				struct cifs_readdata *rdata,
-				unsigned int len);
-	int (*copy_into_pages)(struct TCP_Server_Info *server,
-				struct cifs_readdata *rdata,
-				struct iov_iter *iter);
 	struct iov_iter			iter;
 	struct kvec			iov[2];
 	struct TCP_Server_Info		*server;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 99c239dca807..9cfc160897e9 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -587,7 +587,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
 					enum securityEnum);
 struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
 void cifs_aio_ctx_release(struct kref *refcount);
-int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
 void smb2_cached_lease_break(struct work_struct *work);
 
 int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index e068e3bda304..96c901ed1524 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1484,10 +1484,15 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 		return cifs_readv_discard(server, mid);
 	}
 
-	length = rdata->read_into_pages(server, rdata, data_len);
-	if (length < 0)
-		return length;
-
+#ifdef CONFIG_CIFS_SMB_DIRECT
+	if (rdata->mr)
+		length = data_len; /* An RDMA read is already done. */
+	else
+#endif
+		length = cifs_read_iter_from_socket(server, &rdata->iter,
+						    data_len);
+	if (length > 0)
+		rdata->got_bytes += length;
 	server->total_read += length;
 
 	cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n",
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index a3b9c0ecf02c..a12feb3ffdc4 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -3276,11 +3276,13 @@ static ssize_t __cifs_writev(
 		ctx->iter = *from;
 		ctx->len = len;
 	} else {
-		rc = setup_aio_ctx_iter(ctx, from, WRITE);
-		if (rc) {
+		rc = extract_iter_to_iter(from, &ctx->iter, &ctx->bv);
+		if (rc < 0) {
 			kref_put(&ctx->refcount, cifs_aio_ctx_release);
 			return rc;
 		}
+		ctx->npages = rc;
+		ctx->len = iov_iter_count(&ctx->iter);
 	}
 
 	/* grab a lock here due to read response handlers can access ctx */
@@ -3566,80 +3568,6 @@ cifs_uncached_readv_complete(struct work_struct *work)
 	kref_put(&rdata->refcount, cifs_uncached_readdata_release);
 }
 
-static int
-uncached_fill_pages(struct TCP_Server_Info *server,
-		    struct cifs_readdata *rdata, struct iov_iter *iter,
-		    unsigned int len)
-{
-	int result = 0;
-	unsigned int i;
-	unsigned int nr_pages = rdata->nr_pages;
-	unsigned int page_offset = rdata->page_offset;
-
-	rdata->got_bytes = 0;
-	rdata->tailsz = PAGE_SIZE;
-	for (i = 0; i < nr_pages; i++) {
-		struct page *page = rdata->pages[i];
-		size_t n;
-		unsigned int segment_size = rdata->pagesz;
-
-		if (i == 0)
-			segment_size -= page_offset;
-		else
-			page_offset = 0;
-
-
-		if (len <= 0) {
-			/* no need to hold page hostage */
-			rdata->pages[i] = NULL;
-			rdata->nr_pages--;
-			put_page(page);
-			continue;
-		}
-
-		n = len;
-		if (len >= segment_size)
-			/* enough data to fill the page */
-			n = segment_size;
-		else
-			rdata->tailsz = len;
-		len -= n;
-
-		if (iter)
-			result = copy_page_from_iter(
-					page, page_offset, n, iter);
-#ifdef CONFIG_CIFS_SMB_DIRECT
-		else if (rdata->mr)
-			result = n;
-#endif
-		else
-			result = cifs_read_page_from_socket(
-					server, page, page_offset, n);
-		if (result < 0)
-			break;
-
-		rdata->got_bytes += result;
-	}
-
-	return rdata->got_bytes > 0 && result != -ECONNABORTED ?
-						rdata->got_bytes : result;
-}
-
-static int
-cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
-			      struct cifs_readdata *rdata, unsigned int len)
-{
-	return uncached_fill_pages(server, rdata, NULL, len);
-}
-
-static int
-cifs_uncached_copy_into_pages(struct TCP_Server_Info *server,
-			      struct cifs_readdata *rdata,
-			      struct iov_iter *iter)
-{
-	return uncached_fill_pages(server, rdata, iter, iter->count);
-}
-
 static int cifs_resend_rdata(struct cifs_readdata *rdata,
 			struct list_head *rdata_list,
 			struct cifs_aio_ctx *ctx)
@@ -3725,7 +3653,6 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 	int rc;
 	pid_t pid;
 	struct TCP_Server_Info *server;
-	struct page **pagevec;
 	size_t start;
 	struct iov_iter direct_iov = ctx->iter;
 
@@ -3756,23 +3683,6 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 		cur_len = min_t(const size_t, len, rsize);
 
 		if (ctx->direct_io) {
-			ssize_t result;
-
-			result = iov_iter_get_pages_alloc(
-					&direct_iov, &pagevec,
-					cur_len, &start);
-			if (result < 0) {
-				cifs_dbg(VFS,
-					 "Couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n",
-					 result, iov_iter_type(&direct_iov),
-					 direct_iov.iov_offset,
-					 direct_iov.count);
-				dump_stack();
-
-				rc = result;
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
 			cur_len = (size_t)result;
 			iov_iter_advance(&direct_iov, cur_len);
 
@@ -3815,15 +3725,14 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 
 		rdata->server = server;
 		rdata->cfile = cifsFileInfo_get(open_file);
-		rdata->nr_pages = npages;
+		rdata->nr_pages = ctx->npages;
 		rdata->offset = offset;
 		rdata->bytes = cur_len;
 		rdata->pid = pid;
 		rdata->pagesz = PAGE_SIZE;
-		rdata->read_into_pages = cifs_uncached_read_into_pages;
-		rdata->copy_into_pages = cifs_uncached_copy_into_pages;
 		rdata->credits = credits_on_stack;
 		rdata->ctx = ctx;
+		rdata->iter = ctx->iter;
 		kref_get(&ctx->refcount);
 
 		rc = adjust_credits(server, &rdata->credits, rdata->bytes);
@@ -4013,11 +3922,13 @@ static ssize_t __cifs_readv(
 		ctx->iter = *to;
 		ctx->len = len;
 	} else {
-		rc = setup_aio_ctx_iter(ctx, to, READ);
-		if (rc) {
+		rc = extract_iter_to_iter(to, &ctx->iter, &ctx->bv);
+		if (rc < 0) {
 			kref_put(&ctx->refcount, cifs_aio_ctx_release);
 			return rc;
 		}
+		ctx->npages = rc;
+		ctx->len = iov_iter_count(&ctx->iter);
 		len = ctx->len;
 	}
 
@@ -4180,112 +4091,6 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	return rc;
 }
 
-static int
-readpages_fill_pages(struct TCP_Server_Info *server,
-		     struct cifs_readdata *rdata, struct iov_iter *iter,
-		     unsigned int len)
-{
-	int result = 0;
-	unsigned int i;
-	u64 eof;
-	pgoff_t eof_index;
-	unsigned int nr_pages = rdata->nr_pages;
-	unsigned int page_offset = rdata->page_offset;
-
-	/* determine the eof that the server (probably) has */
-	eof = CIFS_I(rdata->mapping->host)->server_eof;
-	eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0;
-	cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index);
-
-	rdata->got_bytes = 0;
-	rdata->tailsz = PAGE_SIZE;
-	for (i = 0; i < nr_pages; i++) {
-		struct page *page = rdata->pages[i];
-		unsigned int to_read = rdata->pagesz;
-		size_t n;
-
-		if (i == 0)
-			to_read -= page_offset;
-		else
-			page_offset = 0;
-
-		n = to_read;
-
-		if (len >= to_read) {
-			len -= to_read;
-		} else if (len > 0) {
-			/* enough for partial page, fill and zero the rest */
-			zero_user(page, len + page_offset, to_read - len);
-			n = rdata->tailsz = len;
-			len = 0;
-		} else if (page->index > eof_index) {
-			/*
-			 * The VFS will not try to do readahead past the
-			 * i_size, but it's possible that we have outstanding
-			 * writes with gaps in the middle and the i_size hasn't
-			 * caught up yet. Populate those with zeroed out pages
-			 * to prevent the VFS from repeatedly attempting to
-			 * fill them until the writes are flushed.
-			 */
-			zero_user(page, 0, PAGE_SIZE);
-			lru_cache_add(page);
-			flush_dcache_page(page);
-			SetPageUptodate(page);
-			unlock_page(page);
-			put_page(page);
-			rdata->pages[i] = NULL;
-			rdata->nr_pages--;
-			continue;
-		} else {
-			/* no need to hold page hostage */
-			lru_cache_add(page);
-			unlock_page(page);
-			put_page(page);
-			rdata->pages[i] = NULL;
-			rdata->nr_pages--;
-			continue;
-		}
-
-		if (iter)
-			result = copy_page_from_iter(
-					page, page_offset, n, iter);
-#ifdef CONFIG_CIFS_SMB_DIRECT
-		else if (rdata->mr)
-			result = n;
-#endif
-		else
-			result = cifs_read_iter_from_socket(server, &rdata->iter, n);
-		if (result < 0)
-			break;
-
-		rdata->got_bytes += result;
-	}
-
-	return rdata->got_bytes > 0 && result != -ECONNABORTED ?
-						rdata->got_bytes : result;
-}
-
-static int
-cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
-			       struct cifs_readdata *rdata, unsigned int len)
-{
-	int rc;
-
-	rc = cifs_read_iter_from_socket(server, &rdata->iter, len);
-	if (rc > 0)
-		rdata->got_bytes += rc;
-	return rc;
-}
-
-static int
-cifs_readpages_copy_into_pages(struct TCP_Server_Info *server,
-			       struct cifs_readdata *rdata,
-			       struct iov_iter *iter)
-{
-	printk("cifs_readpages_copy_into_pages\n");
-	return readpages_fill_pages(server, rdata, iter, iter->count);
-}
-
 /*
  * Issue a read operation on behalf of the netfs helper functions.  We're asked
  * to make a read of a certain size at a point in the file.  We are permitted
@@ -4337,18 +4142,16 @@ static void cifs_req_issue_op(struct netfs_read_subrequest *subreq)
 		goto out;
 	}
 
-	rdata->subreq = subreq;
-	rdata->cfile = cifsFileInfo_get(open_file);
-	rdata->server = server;
-	rdata->mapping = rreq->mapping;
-	rdata->offset = subreq->start + subreq->transferred;
-	rdata->bytes = subreq->len   - subreq->transferred;
-	rdata->pid = pid;
-	rdata->pagesz = PAGE_SIZE;
-	rdata->tailsz = PAGE_SIZE;
-	rdata->read_into_pages = cifs_readpages_read_into_pages;
-	rdata->copy_into_pages = cifs_readpages_copy_into_pages;
-	rdata->credits = credits_on_stack;
+	rdata->subreq	= subreq;
+	rdata->cfile	= cifsFileInfo_get(open_file);
+	rdata->server	= server;
+	rdata->mapping	= rreq->mapping;
+	rdata->offset	= subreq->start + subreq->transferred;
+	rdata->bytes	= subreq->len   - subreq->transferred;
+	rdata->pid	= pid;
+	rdata->pagesz	= PAGE_SIZE;
+	rdata->tailsz	= PAGE_SIZE;
+	rdata->credits	= credits_on_stack;
 
 	iov_iter_xarray(&rdata->iter, READ, &rreq->mapping->i_pages,
 			rdata->offset, rdata->bytes);
@@ -4652,7 +4455,7 @@ void cifs_oplock_break(struct work_struct *work)
  * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests
  * so this method should never be called.
  *
- * Direct IO is not yet supported in the cached mode. 
+ * Direct IO is not yet supported in the cached mode.
  */
 static ssize_t
 cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 5148d48d6a35..ece7fc8a7740 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -974,96 +974,6 @@ cifs_aio_ctx_release(struct kref *refcount)
 	kfree(ctx);
 }
 
-#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
-
-int
-setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
-{
-	ssize_t rc;
-	unsigned int cur_npages;
-	unsigned int npages = 0;
-	unsigned int i;
-	size_t len;
-	size_t count = iov_iter_count(iter);
-	unsigned int saved_len;
-	size_t start;
-	unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
-	struct page **pages = NULL;
-	struct bio_vec *bv = NULL;
-
-	if (iov_iter_is_kvec(iter)) {
-		memcpy(&ctx->iter, iter, sizeof(*iter));
-		ctx->len = count;
-		iov_iter_advance(iter, count);
-		return 0;
-	}
-
-	if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT)
-		bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL);
-
-	if (!bv) {
-		bv = vmalloc(array_size(max_pages, sizeof(*bv)));
-		if (!bv)
-			return -ENOMEM;
-	}
-
-	if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT)
-		pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL);
-
-	if (!pages) {
-		pages = vmalloc(array_size(max_pages, sizeof(*pages)));
-		if (!pages) {
-			kvfree(bv);
-			return -ENOMEM;
-		}
-	}
-
-	saved_len = count;
-
-	while (count && npages < max_pages) {
-		rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
-		if (rc < 0) {
-			cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc);
-			break;
-		}
-
-		if (rc > count) {
-			cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
-				 count);
-			break;
-		}
-
-		iov_iter_advance(iter, rc);
-		count -= rc;
-		rc += start;
-		cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
-
-		if (npages + cur_npages > max_pages) {
-			cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
-				 npages + cur_npages, max_pages);
-			break;
-		}
-
-		for (i = 0; i < cur_npages; i++) {
-			len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
-			bv[npages + i].bv_page = pages[i];
-			bv[npages + i].bv_offset = start;
-			bv[npages + i].bv_len = len - start;
-			rc -= len;
-			start = 0;
-		}
-
-		npages += cur_npages;
-	}
-
-	kvfree(pages);
-	ctx->bv = bv;
-	ctx->len = saved_len - count;
-	ctx->npages = npages;
-	iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len);
-	return 0;
-}
-
 /**
  * cifs_alloc_hash - allocate hash and hash context together
  * @name: The name of the crypto hash algo
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index c5b1dea54ebc..a5ce04331613 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -4754,30 +4754,24 @@ read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,
 }
 
 static int
-init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
-	       unsigned int cur_off, struct bio_vec **page_vec)
+cifs_copy_pages_to_iter(struct page **pages, unsigned int npages, unsigned int data_size,
+			unsigned int cur_off, struct iov_iter *iter)
 {
-	struct bio_vec *bvec;
-	int i;
-
-	bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL);
-	if (!bvec)
-		return -ENOMEM;
+	unsigned int i;
 
 	for (i = 0; i < npages; i++) {
-		bvec[i].bv_page = pages[i];
-		bvec[i].bv_offset = (i == 0) ? cur_off : 0;
-		bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size);
-		data_size -= bvec[i].bv_len;
-	}
-
-	if (data_size != 0) {
-		cifs_dbg(VFS, "%s: something went wrong\n", __func__);
-		kfree(bvec);
-		return -EIO;
+		size_t n, len = min_t(unsigned int, PAGE_SIZE, data_size);
+
+		n = copy_page_to_iter(pages[i],
+				      (i == 0) ? cur_off : 0,
+				      len, iter);
+		if (n != len) {
+			cifs_dbg(VFS, "%s: something went wrong\n", __func__);
+			return -EIO;
+		}
+		data_size -= n;
 	}
 
-	*page_vec = bvec;
 	return 0;
 }
 
@@ -4794,9 +4788,6 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
 	unsigned int pad_len;
 	struct cifs_readdata *rdata = mid->callback_data;
 	struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
-	struct bio_vec *bvec = NULL;
-	struct iov_iter iter;
-	struct kvec iov;
 	int length;
 	bool use_rdma_mr = false;
 
@@ -4895,8 +4886,9 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
 			return 0;
 		}
 
-		rdata->result = init_read_bvec(pages, npages, page_data_size,
-					       cur_off, &bvec);
+		/* Copy the data to the output I/O iterator. */
+		rdata->result = cifs_copy_pages_to_iter(pages, npages, page_data_size,
+							cur_off, &rdata->iter);
 		if (rdata->result != 0) {
 			if (is_offloaded)
 				mid->mid_state = MID_RESPONSE_MALFORMED;
@@ -4905,13 +4897,12 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
 			return 0;
 		}
 
-		iov_iter_bvec(&iter, WRITE, bvec, npages, data_len);
 	} else if (buf_len >= data_offset + data_len) {
 		/* read response payload is in buf */
 		WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
-		iov.iov_base = buf + data_offset;
-		iov.iov_len = data_len;
-		iov_iter_kvec(&iter, WRITE, &iov, 1, data_len);
+		length = copy_to_iter(buf + data_offset, data_len, &rdata->iter);
+		if (length < 0)
+			return length;
 	} else {
 		/* read response payload cannot be in both buf and pages */
 		WARN_ONCE(1, "buf can not contain only a part of read data");
@@ -4923,13 +4914,6 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
 		return 0;
 	}
 
-	length = rdata->copy_into_pages(server, rdata, &iter);
-
-	kfree(bvec);
-
-	if (length < 0)
-		return length;
-
 	if (is_offloaded)
 		mid->mid_state = MID_RESPONSE_RECEIVED;
 	else
diff --git a/include/linux/uio.h b/include/linux/uio.h
index 6350354f97e9..931528bc2eab 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -247,6 +247,9 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages,
 			size_t maxsize, unsigned maxpages, size_t *start);
 ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
 			size_t maxsize, size_t *start);
+ssize_t extract_iter_to_iter(struct iov_iter *orig,
+			     struct iov_iter *new,
+			     struct bio_vec **_bv);
 int iov_iter_npages(const struct iov_iter *i, int maxpages);
 void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state);
 
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 66a740e6e153..cebfdb617fd8 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1696,6 +1696,100 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
 }
 EXPORT_SYMBOL(iov_iter_get_pages_alloc);
 
+/**
+ * extract_iter_to_iter - Extract the pages from one iterator into another
+ * @orig: The original iterator
+ * @new: The iterator to be set up
+ * @_bv: Where to store the allocated bvec table pointer, if allocated
+ *
+ * Extract the page fragments from an iterator and build up a second iterator
+ * that refers to all of those bits so that the original iterator can be
+ * disposed of.
+ *
+ * If a bvec array is created, the number of pages in the array is returned and
+ * a pointer to the array is saved into *@_bv;
+ */
+ssize_t extract_iter_to_iter(struct iov_iter *orig,
+			     struct iov_iter *new,
+			     struct bio_vec **_bv)
+{
+	struct bio_vec *bv = NULL;
+	struct page **pages;
+	unsigned int cur_npages;
+	unsigned int max_pages = iov_iter_npages(orig, INT_MAX);
+	unsigned int saved_len;
+	unsigned int npages = 0;
+	unsigned int i;
+	ssize_t ret;
+	size_t bv_size, pg_size;
+	size_t count = iov_iter_count(orig);
+	size_t start;
+	size_t len;
+
+	*_bv = NULL;
+
+	if (iov_iter_is_kvec(orig) || iov_iter_is_discard(orig)) {
+		memcpy(new, orig, sizeof(*new));
+		iov_iter_advance(orig, count);
+		return 0;
+	}
+
+	bv_size = array_size(max_pages, sizeof(*bv));
+	bv = kvmalloc(bv_size, GFP_KERNEL);
+	if (!bv)
+		return -ENOMEM;
+
+	/* Put the page list at the end of the bvec list storage.  bvec
+	 * elements are larger than page pointers, so as long as we work
+	 * 0->last, we should be fine.
+	 */
+	pg_size = array_size(max_pages, sizeof(*pages));
+	pages = (void *)bv + bv_size - pg_size;
+
+	saved_len = count;
+
+	while (count && npages < max_pages) {
+		ret = iov_iter_get_pages(orig, pages, count, max_pages - npages,
+					 &start);
+		if (ret < 0) {
+			pr_err("Couldn't get user pages (rc=%zd)\n", ret);
+			break;
+		}
+
+		if (ret > count) {
+			pr_err("get_pages rc=%zd more than %zu\n", ret, count);
+			break;
+		}
+
+		iov_iter_advance(orig, ret);
+		count -= ret;
+		ret += start;
+		cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE);
+
+		if (npages + cur_npages > max_pages) {
+			pr_err("Out of bvec array capacity (%u vs %u)\n",
+			       npages + cur_npages, max_pages);
+			break;
+		}
+
+		for (i = 0; i < cur_npages; i++) {
+			len = ret > PAGE_SIZE ? PAGE_SIZE : ret;
+			bv[npages + i].bv_page	 = *pages++;
+			bv[npages + i].bv_offset = start;
+			bv[npages + i].bv_len	 = len - start;
+			ret -= len;
+			start = 0;
+		}
+
+		npages += cur_npages;
+	}
+
+	*_bv = bv;
+	iov_iter_bvec(new, iov_iter_rw(orig), bv, npages, saved_len - count);
+	return npages;
+}
+EXPORT_SYMBOL(extract_iter_to_iter);
+
 size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
 			       struct iov_iter *i)
 {



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst
  2022-01-06 21:31 [RFC][PATCH v2 0/3] cifs: In-progress conversion to use iov_iters instead of page lists David Howells
  2022-01-06 21:32 ` [PATCH v2 1/3] cifs: Use netfslib David Howells
  2022-01-06 21:32 ` [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading David Howells
@ 2022-01-06 21:32 ` David Howells
  2022-01-07 17:32     ` kernel test robot
  2 siblings, 1 reply; 8+ messages in thread
From: David Howells @ 2022-01-06 21:32 UTC (permalink / raw)
  To: smfrench; +Cc: nspmangalore, dhowells, jlayton, linux-cifs, linux-cachefs

Replace the list of pages in the cifs_readdata, cifs_writedata and smb_rqst
structs with iterators.
---

 fs/cifs/cifsencrypt.c |   40 +-
 fs/cifs/cifsfs.h      |    3 
 fs/cifs/cifsglob.h    |   22 -
 fs/cifs/cifsproto.h   |    7 
 fs/cifs/cifssmb.c     |  206 ++++++++----
 fs/cifs/file.c        |  808 +++++++++++++++++++------------------------------
 fs/cifs/misc.c        |   19 -
 fs/cifs/smb2ops.c     |  134 ++++----
 fs/cifs/smb2pdu.c     |   12 -
 fs/cifs/transport.c   |   41 --
 include/linux/uio.h   |    4 
 lib/iov_iter.c        |   42 +++
 12 files changed, 603 insertions(+), 735 deletions(-)

diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index d118282071b3..1b83c72462d2 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -24,12 +24,27 @@
 #include "../smbfs_common/arc4.h"
 #include <crypto/aead.h>
 
+static ssize_t cifs_signature_scan(struct iov_iter *i, const void *p,
+				   size_t len, size_t off, void *priv)
+{
+	struct shash_desc *shash = priv;
+	int rc;
+
+	rc = crypto_shash_update(shash, p, len);
+	if (rc) {
+		cifs_dbg(VFS, "%s: Could not update with payload\n", __func__);
+		return rc;
+	}
+
+	return len;
+}
+
 int __cifs_calc_signature(struct smb_rqst *rqst,
 			struct TCP_Server_Info *server, char *signature,
 			struct shash_desc *shash)
 {
 	int i;
-	int rc;
+	ssize_t rc;
 	struct kvec *iov = rqst->rq_iov;
 	int n_vec = rqst->rq_nvec;
 	int is_smb2 = server->vals->header_preamble_size == 0;
@@ -62,25 +77,10 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
 		}
 	}
 
-	/* now hash over the rq_pages array */
-	for (i = 0; i < rqst->rq_npages; i++) {
-		void *kaddr;
-		unsigned int len, offset;
-
-		rqst_page_get_length(rqst, i, &len, &offset);
-
-		kaddr = (char *) kmap(rqst->rq_pages[i]) + offset;
-
-		rc = crypto_shash_update(shash, kaddr, len);
-		if (rc) {
-			cifs_dbg(VFS, "%s: Could not update with payload\n",
-				 __func__);
-			kunmap(rqst->rq_pages[i]);
-			return rc;
-		}
-
-		kunmap(rqst->rq_pages[i]);
-	}
+	rc = iov_iter_scan(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter),
+			   cifs_signature_scan, shash);
+	if (rc < 0)
+		return rc;
 
 	rc = crypto_shash_final(shash, signature);
 	if (rc)
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 9e5d9e192ef0..c4b679dd6d5e 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -110,6 +110,9 @@ extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
 extern const struct file_operations cifs_dir_ops;
 extern int cifs_dir_open(struct inode *inode, struct file *file);
 extern int cifs_readdir(struct file *file, struct dir_context *ctx);
+extern void cifs_pages_written_back(struct inode *inode, loff_t start, unsigned int len);
+extern void cifs_pages_write_failed(struct inode *inode, loff_t start, unsigned int len);
+extern void cifs_pages_write_redirty(struct inode *inode, loff_t start, unsigned int len);
 
 /* Functions related to dir entries */
 extern const struct dentry_operations cifs_dentry_ops;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c1665ef5b946..c33803141712 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -185,11 +185,8 @@ struct cifs_cred {
 struct smb_rqst {
 	struct kvec	*rq_iov;	/* array of kvecs */
 	unsigned int	rq_nvec;	/* number of kvecs in array */
-	struct page	**rq_pages;	/* pointer to array of page ptrs */
-	unsigned int	rq_offset;	/* the offset to the 1st page */
-	unsigned int	rq_npages;	/* number pages in array */
-	unsigned int	rq_pagesz;	/* page size to use */
-	unsigned int	rq_tailsz;	/* length of last page */
+	struct iov_iter	rq_iter;	/* Data iterator */
+	struct xarray	rq_buffer;	/* Page buffer for encryption */
 };
 
 struct mid_q_entry;
@@ -1330,21 +1327,18 @@ struct cifs_readdata {
 	__u64				offset;
 	unsigned int			bytes;
 	unsigned int			got_bytes;
+	unsigned int			bv_count;
 	pid_t				pid;
 	int				result;
 	struct work_struct		work;
 	struct iov_iter			iter;
+	struct bio_vec			*bv;
 	struct kvec			iov[2];
 	struct TCP_Server_Info		*server;
 #ifdef CONFIG_CIFS_SMB_DIRECT
 	struct smbd_mr			*mr;
 #endif
-	unsigned int			pagesz;
-	unsigned int			page_offset;
-	unsigned int			tailsz;
 	struct cifs_credits		credits;
-	unsigned int			nr_pages;
-	struct page			**pages;
 };
 
 /* asynchronous write support */
@@ -1356,20 +1350,18 @@ struct cifs_writedata {
 	struct work_struct		work;
 	struct cifsFileInfo		*cfile;
 	struct cifs_aio_ctx		*ctx;
+	struct iov_iter			iter;
+	struct bio_vec			*bv;
 	__u64				offset;
 	pid_t				pid;
 	unsigned int			bytes;
+	unsigned int			bv_count;
 	int				result;
 	struct TCP_Server_Info		*server;
 #ifdef CONFIG_CIFS_SMB_DIRECT
 	struct smbd_mr			*mr;
 #endif
-	unsigned int			pagesz;
-	unsigned int			page_offset;
-	unsigned int			tailsz;
 	struct cifs_credits		credits;
-	unsigned int			nr_pages;
-	struct page			**pages;
 };
 
 /*
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 9cfc160897e9..799c28f90bf1 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -567,10 +567,7 @@ int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
 int cifs_async_writev(struct cifs_writedata *wdata,
 		      void (*release)(struct kref *kref));
 void cifs_writev_complete(struct work_struct *work);
-struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
-						work_func_t complete);
-struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages,
-						work_func_t complete);
+struct cifs_writedata *cifs_writedata_alloc(work_func_t complete);
 void cifs_writedata_release(struct kref *refcount);
 int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
 			  struct cifs_sb_info *cifs_sb,
@@ -593,8 +590,6 @@ int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
 		    struct sdesc **sdesc);
 void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
 
-extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
-				unsigned int *len, unsigned int *offset);
 struct cifs_chan *
 cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
 int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 96c901ed1524..11a6cf44a88f 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -25,6 +25,7 @@
 #include <linux/uaccess.h>
 #include <linux/netfs.h>
 #include "cifspdu.h"
+#include "cifsfs.h"
 #include "cifsglob.h"
 #include "cifsacl.h"
 #include "cifsproto.h"
@@ -1516,11 +1517,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
 	struct TCP_Server_Info *server = tcon->ses->server;
 	struct smb_rqst rqst = { .rq_iov = rdata->iov,
 				 .rq_nvec = 2,
-				 .rq_pages = rdata->pages,
-				 .rq_offset = rdata->page_offset,
-				 .rq_npages = rdata->nr_pages,
-				 .rq_pagesz = rdata->pagesz,
-				 .rq_tailsz = rdata->tailsz };
+				 .rq_iter = rdata->iter };
 	struct cifs_credits credits = { .value = 1, .instance = 0 };
 
 	cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
@@ -1883,10 +1880,93 @@ cifs_writedata_release(struct kref *refcount)
 	if (wdata->cfile)
 		cifsFileInfo_put(wdata->cfile);
 
-	kvfree(wdata->pages);
 	kfree(wdata);
 }
 
+/*
+ * Completion of write to server.
+ */
+void cifs_pages_written_back(struct inode *inode, loff_t start, unsigned int len)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct folio *folio;
+	pgoff_t end;
+
+	XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE);
+
+	rcu_read_lock();
+
+	end = (start + len - 1) / PAGE_SIZE;
+	xas_for_each(&xas, folio, end) {
+		if (!folio_test_writeback(folio)) {
+			pr_err("bad %x @%llx page %lx %lx\n",
+			       len, start, folio_index(folio), end);
+			BUG();
+		}
+
+		folio_detach_private(folio);
+		folio_end_writeback(folio);
+	}
+
+	rcu_read_unlock();
+}
+
+/*
+ * Failure of write to server.
+ */
+void cifs_pages_write_failed(struct inode *inode, loff_t start, unsigned int len)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct folio *folio;
+	pgoff_t end;
+
+	XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE);
+
+	rcu_read_lock();
+
+	end = (start + len - 1) / PAGE_SIZE;
+	xas_for_each(&xas, folio, end) {
+		if (!folio_test_writeback(folio)) {
+			pr_err("bad %x @%llx page %lx %lx\n",
+			       len, start, folio_index(folio), end);
+			BUG();
+		}
+
+		folio_set_error(folio);
+		folio_end_writeback(folio);
+	}
+
+	rcu_read_unlock();
+}
+
+/*
+ * Redirty pages after a temporary failure.
+ */
+void cifs_pages_write_redirty(struct inode *inode, loff_t start, unsigned int len)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct folio *folio;
+	pgoff_t end;
+
+	XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE);
+
+	rcu_read_lock();
+
+	end = (start + len - 1) / PAGE_SIZE;
+	xas_for_each(&xas, folio, end) {
+		if (!folio_test_writeback(folio)) {
+			pr_err("bad %x @%llx page %lx %lx\n",
+			       len, start, folio_index(folio), end);
+			BUG();
+		}
+
+		filemap_dirty_folio(folio->mapping, folio);
+		folio_end_writeback(folio);
+	}
+
+	rcu_read_unlock();
+}
+
 /*
  * Write failed with a retryable error. Resend the write request. It's also
  * possible that the page was redirtied so re-clean the page.
@@ -1894,51 +1974,56 @@ cifs_writedata_release(struct kref *refcount)
 static void
 cifs_writev_requeue(struct cifs_writedata *wdata)
 {
-	int i, rc = 0;
+	int rc = 0;
 	struct inode *inode = d_inode(wdata->cfile->dentry);
 	struct TCP_Server_Info *server;
-	unsigned int rest_len;
+	unsigned int rest_len = wdata->bytes;
+	loff_t fpos = wdata->offset;
 
 	server = tlink_tcon(wdata->cfile->tlink)->ses->server;
-	i = 0;
-	rest_len = wdata->bytes;
 	do {
 		struct cifs_writedata *wdata2;
-		unsigned int j, nr_pages, wsize, tailsz, cur_len;
+		unsigned int wsize, cur_len;
 
 		wsize = server->ops->wp_retry_size(inode);
 		if (wsize < rest_len) {
-			nr_pages = wsize / PAGE_SIZE;
-			if (!nr_pages) {
+			if (wsize < PAGE_SIZE) {
 				rc = -ENOTSUPP;
 				break;
 			}
-			cur_len = nr_pages * PAGE_SIZE;
-			tailsz = PAGE_SIZE;
+			cur_len = min(round_down(wsize, PAGE_SIZE), rest_len);
 		} else {
-			nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE);
 			cur_len = rest_len;
-			tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE;
 		}
 
-		wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete);
+		wdata2 = cifs_writedata_alloc(cifs_writev_complete);
 		if (!wdata2) {
 			rc = -ENOMEM;
 			break;
 		}
 
-		for (j = 0; j < nr_pages; j++) {
-			wdata2->pages[j] = wdata->pages[i + j];
-			lock_page(wdata2->pages[j]);
-			clear_page_dirty_for_io(wdata2->pages[j]);
-		}
-
 		wdata2->sync_mode = wdata->sync_mode;
-		wdata2->nr_pages = nr_pages;
-		wdata2->offset = page_offset(wdata2->pages[0]);
-		wdata2->pagesz = PAGE_SIZE;
-		wdata2->tailsz = tailsz;
-		wdata2->bytes = cur_len;
+		wdata2->offset	= fpos;
+		wdata2->bytes	= cur_len;
+		wdata2->iter	= wdata->iter;
+
+		iov_iter_advance(&wdata2->iter, fpos - wdata->offset);
+		iov_iter_truncate(&wdata2->iter, wdata2->bytes);
+
+#if 0
+		if (iov_iter_is_xarray(&wdata2->iter)) {
+			/* TODO: Check for pages having been redirtied and
+			 * clean them.  We can do this by walking the xarray.
+			 * If it's not an xarray, then it's a DIO and we
+			 * shouldn't be mucking around with the page bits.
+			 */
+			for (j = 0; j < nr_pages; j++) {
+				wdata2->pages[j] = wdata->pages[i + j];
+				lock_page(wdata2->pages[j]);
+				clear_page_dirty_for_io(wdata2->pages[j]);
+			}
+		}
+#endif
 
 		rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY,
 					    &wdata2->cfile);
@@ -1953,33 +2038,25 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
 						       cifs_writedata_release);
 		}
 
-		for (j = 0; j < nr_pages; j++) {
-			unlock_page(wdata2->pages[j]);
-			if (rc != 0 && !is_retryable_error(rc)) {
-				SetPageError(wdata2->pages[j]);
-				end_page_writeback(wdata2->pages[j]);
-				put_page(wdata2->pages[j]);
-			}
-		}
+		if (iov_iter_is_xarray(&wdata2->iter))
+			cifs_pages_written_back(inode, wdata2->offset, wdata2->bytes);
 
 		kref_put(&wdata2->refcount, cifs_writedata_release);
 		if (rc) {
 			if (is_retryable_error(rc))
 				continue;
-			i += nr_pages;
+			fpos += cur_len;
+			rest_len -= cur_len;
 			break;
 		}
 
+		fpos += cur_len;
 		rest_len -= cur_len;
-		i += nr_pages;
-	} while (i < wdata->nr_pages);
+	} while (rest_len > 0);
 
-	/* cleanup remaining pages from the original wdata */
-	for (; i < wdata->nr_pages; i++) {
-		SetPageError(wdata->pages[i]);
-		end_page_writeback(wdata->pages[i]);
-		put_page(wdata->pages[i]);
-	}
+	/* Clean up remaining pages from the original wdata */
+	if (iov_iter_is_xarray(&wdata->iter))
+		cifs_pages_written_back(inode, fpos, rest_len);
 
 	if (rc != 0 && !is_retryable_error(rc))
 		mapping_set_error(inode->i_mapping, rc);
@@ -1992,7 +2069,6 @@ cifs_writev_complete(struct work_struct *work)
 	struct cifs_writedata *wdata = container_of(work,
 						struct cifs_writedata, work);
 	struct inode *inode = d_inode(wdata->cfile->dentry);
-	int i = 0;
 
 	if (wdata->result == 0) {
 		spin_lock(&inode->i_lock);
@@ -2003,39 +2079,25 @@ cifs_writev_complete(struct work_struct *work)
 	} else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
 		return cifs_writev_requeue(wdata);
 
-	for (i = 0; i < wdata->nr_pages; i++) {
-		struct page *page = wdata->pages[i];
-		if (wdata->result == -EAGAIN)
-			__set_page_dirty_nobuffers(page);
-		else if (wdata->result < 0)
-			SetPageError(page);
-		end_page_writeback(page);
-		put_page(page);
-	}
+	if (wdata->result == -EAGAIN)
+		cifs_pages_write_redirty(inode, wdata->offset, wdata->bytes);
+	else if (wdata->result < 0)
+		cifs_pages_write_failed(inode, wdata->offset, wdata->bytes);
+	else
+		cifs_pages_written_back(inode, wdata->offset, wdata->bytes);
+
 	if (wdata->result != -EAGAIN)
 		mapping_set_error(inode->i_mapping, wdata->result);
 	kref_put(&wdata->refcount, cifs_writedata_release);
 }
 
 struct cifs_writedata *
-cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
-{
-	struct page **pages =
-		kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
-	if (pages)
-		return cifs_writedata_direct_alloc(pages, complete);
-
-	return NULL;
-}
-
-struct cifs_writedata *
-cifs_writedata_direct_alloc(struct page **pages, work_func_t complete)
+cifs_writedata_alloc(work_func_t complete)
 {
 	struct cifs_writedata *wdata;
 
 	wdata = kzalloc(sizeof(*wdata), GFP_NOFS);
 	if (wdata != NULL) {
-		wdata->pages = pages;
 		kref_init(&wdata->refcount);
 		INIT_LIST_HEAD(&wdata->list);
 		init_completion(&wdata->done);
@@ -2143,11 +2205,7 @@ cifs_async_writev(struct cifs_writedata *wdata,
 
 	rqst.rq_iov = iov;
 	rqst.rq_nvec = 2;
-	rqst.rq_pages = wdata->pages;
-	rqst.rq_offset = wdata->page_offset;
-	rqst.rq_npages = wdata->nr_pages;
-	rqst.rq_pagesz = wdata->pagesz;
-	rqst.rq_tailsz = wdata->tailsz;
+	rqst.rq_iter = wdata->iter;
 
 	cifs_dbg(FYI, "async write at %llu %u bytes\n",
 		 wdata->offset, wdata->bytes);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index a12feb3ffdc4..e53df5398dc4 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2269,294 +2269,333 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
 	return rc;
 }
 
-static struct cifs_writedata *
-wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping,
-			  pgoff_t end, pgoff_t *index,
-			  unsigned int *found_pages)
+/*
+ * Extend the region to be written back to include subsequent contiguously
+ * dirty pages if possible, but don't sleep while doing so.
+ */
+static void cifs_extend_writeback(struct address_space *mapping,
+				  long *_count,
+				  loff_t start,
+				  loff_t max_len,
+				  unsigned int *_len)
 {
-	struct cifs_writedata *wdata;
-
-	wdata = cifs_writedata_alloc((unsigned int)tofind,
-				     cifs_writev_complete);
-	if (!wdata)
-		return NULL;
-
-	*found_pages = find_get_pages_range_tag(mapping, index, end,
-				PAGECACHE_TAG_DIRTY, tofind, wdata->pages);
-	return wdata;
-}
+	struct pagevec pvec;
+	struct folio *folio;
+	unsigned int psize;
+	loff_t len = *_len;
+	pgoff_t index = (start + len) / PAGE_SIZE;
+	bool stop = true;
+	unsigned int i;
 
-static unsigned int
-wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages,
-		    struct address_space *mapping,
-		    struct writeback_control *wbc,
-		    pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done)
-{
-	unsigned int nr_pages = 0, i;
-	struct page *page;
+	XA_STATE(xas, &mapping->i_pages, index);
+	pagevec_init(&pvec);
 
-	for (i = 0; i < found_pages; i++) {
-		page = wdata->pages[i];
-		/*
-		 * At this point we hold neither the i_pages lock nor the
-		 * page lock: the page may be truncated or invalidated
-		 * (changing page->mapping to NULL), or even swizzled
-		 * back from swapper_space to tmpfs file mapping
+	do {
+		/* Firstly, we gather up a batch of contiguous dirty pages
+		 * under the RCU read lock - but we can't clear the dirty flags
+		 * there if any of those pages are mapped.
 		 */
+		rcu_read_lock();
 
-		if (nr_pages == 0)
-			lock_page(page);
-		else if (!trylock_page(page))
-			break;
+		xas_for_each(&xas, folio, ULONG_MAX) {
+			stop = true;
+			if (xas_retry(&xas, folio))
+				continue;
+			if (xa_is_value(folio))
+				break;
+			if (folio_index(folio) != index)
+				break;
 
-		if (unlikely(page->mapping != mapping)) {
-			unlock_page(page);
-			break;
-		}
+			if (!folio_try_get_rcu(folio)) {
+				xas_reset(&xas);
+				continue;
+			}
 
-		if (!wbc->range_cyclic && page->index > end) {
-			*done = true;
-			unlock_page(page);
-			break;
-		}
+			/* Has the page moved or been split? */
+			if (unlikely(folio != xas_reload(&xas))) {
+				folio_put(folio);
+				break;
+			}
 
-		if (*next && (page->index != *next)) {
-			/* Not next consecutive page */
-			unlock_page(page);
-			break;
-		}
+			if (!folio_trylock(folio)) {
+				folio_put(folio);
+				break;
+			}
+			if (!folio_test_dirty(folio) || folio_test_writeback(folio)) {
+				folio_unlock(folio);
+				folio_put(folio);
+				break;
+			}
 
-		if (wbc->sync_mode != WB_SYNC_NONE)
-			wait_on_page_writeback(page);
+			psize = folio_size(folio);
+			len += psize;
+			if (len >= max_len || *_count <= 0)
+				stop = true;
 
-		if (PageWriteback(page) ||
-				!clear_page_dirty_for_io(page)) {
-			unlock_page(page);
-			break;
+			index += folio_nr_pages(folio);
+			if (!pagevec_add(&pvec, &folio->page))
+				break;
+			if (stop)
+				break;
 		}
 
-		/*
-		 * This actually clears the dirty bit in the radix tree.
-		 * See cifs_writepage() for more commentary.
+		if (!stop)
+			xas_pause(&xas);
+		rcu_read_unlock();
+
+		/* Now, if we obtained any pages, we can shift them to being
+		 * writable and mark them for caching.
 		 */
-		set_page_writeback(page);
-		if (page_offset(page) >= i_size_read(mapping->host)) {
-			*done = true;
-			unlock_page(page);
-			end_page_writeback(page);
+		if (!pagevec_count(&pvec))
 			break;
-		}
-
-		wdata->pages[i] = page;
-		*next = page->index + 1;
-		++nr_pages;
-	}
-
-	/* reset index to refind any pages skipped */
-	if (nr_pages == 0)
-		*index = wdata->pages[0]->index + 1;
-
-	/* put any pages we aren't going to use */
-	for (i = nr_pages; i < found_pages; i++) {
-		put_page(wdata->pages[i]);
-		wdata->pages[i] = NULL;
-	}
-
-	return nr_pages;
-}
 
-static int
-wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
-		 struct address_space *mapping, struct writeback_control *wbc)
-{
-	int rc;
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			folio = page_folio(pvec.pages[i]);
+			if (!folio_clear_dirty_for_io(folio))
+				BUG();
+			if (folio_start_writeback(folio))
+				BUG();
 
-	wdata->sync_mode = wbc->sync_mode;
-	wdata->nr_pages = nr_pages;
-	wdata->offset = page_offset(wdata->pages[0]);
-	wdata->pagesz = PAGE_SIZE;
-	wdata->tailsz = min(i_size_read(mapping->host) -
-			page_offset(wdata->pages[nr_pages - 1]),
-			(loff_t)PAGE_SIZE);
-	wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz;
-	wdata->pid = wdata->cfile->pid;
-
-	rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes);
-	if (rc)
-		return rc;
+			*_count -= folio_nr_pages(folio);
+			folio_unlock(folio);
+		}
 
-	if (wdata->cfile->invalidHandle)
-		rc = -EAGAIN;
-	else
-		rc = wdata->server->ops->async_writev(wdata,
-						      cifs_writedata_release);
+		pagevec_release(&pvec);
+		cond_resched();
+	} while (!stop);
 
-	return rc;
+	*_len = len;
 }
 
-static int cifs_writepages(struct address_space *mapping,
-			   struct writeback_control *wbc)
+/*
+ * Write back the locked page and any subsequent non-locked dirty pages.
+ */
+static ssize_t cifs_write_back_from_locked_folio(struct address_space *mapping,
+						 struct writeback_control *wbc,
+						 struct folio *folio,
+						 loff_t start, loff_t end)
 {
 	struct inode *inode = mapping->host;
-	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 	struct TCP_Server_Info *server;
-	bool done = false, scanned = false, range_whole = false;
-	pgoff_t end, index;
 	struct cifs_writedata *wdata;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifs_credits credits_on_stack;
+	struct cifs_credits *credits = &credits_on_stack;
 	struct cifsFileInfo *cfile = NULL;
-	int rc = 0;
-	int saved_rc = 0;
-	unsigned int xid;
+	unsigned int xid, wsize, len, max_len;
+	loff_t i_size = i_size_read(inode);
+	long count = wbc->nr_to_write;
+	int rc;
 
-	/*
-	 * If wsize is smaller than the page cache size, default to writing
-	 * one page at a time via cifs_writepage
-	 */
-	if (cifs_sb->ctx->wsize < PAGE_SIZE)
-		return generic_writepages(mapping, wbc);
+	if (folio_start_writeback(folio))
+		BUG();
+
+	count -= folio_nr_pages(folio);
 
 	xid = get_xid();
-	if (wbc->range_cyclic) {
-		index = mapping->writeback_index; /* Start from prev offset */
-		end = -1;
-	} else {
-		index = wbc->range_start >> PAGE_SHIFT;
-		end = wbc->range_end >> PAGE_SHIFT;
-		if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
-			range_whole = true;
-		scanned = true;
-	}
 	server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses);
 
-retry:
-	while (!done && index <= end) {
-		unsigned int i, nr_pages, found_pages, wsize;
-		pgoff_t next = 0, tofind, saved_index = index;
-		struct cifs_credits credits_on_stack;
-		struct cifs_credits *credits = &credits_on_stack;
-		int get_file_rc = 0;
+	rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
+	if (rc) {
+		cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", rc);
+		goto err_xid;
+	}
 
-		if (cfile)
-			cifsFileInfo_put(cfile);
+	rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
+					   &wsize, credits);
+	if (rc != 0)
+		goto err_close;
 
-		rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
+	wdata = cifs_writedata_alloc(cifs_writev_complete);
+	if (!wdata) {
+		rc = -ENOMEM;
+		goto err_uncredit;
+	}
 
-		/* in case of an error store it to return later */
-		if (rc)
-			get_file_rc = rc;
+	wdata->sync_mode = wbc->sync_mode;
+	wdata->offset = folio_pos(folio);
+	wdata->pid = wdata->cfile->pid;
+	wdata->credits = credits_on_stack;
+	wdata->cfile = cfile;
+	wdata->server = server;
+	cfile = NULL;
+
+	/* Find all consecutive lockable dirty pages, stopping when we find a
+	 * page that is not immediately lockable, is not dirty or is missing,
+	 * or we reach the end of the range.
+	 */
+	if (start < i_size) {
+		/* Trim the write to the EOF; the extra data is ignored.  Also
+		 * put an upper limit on the size of a single storedata op.
+		 */
+		max_len = wsize;
+		max_len = min_t(unsigned long long, max_len, end - start + 1);
+		max_len = min_t(unsigned long long, max_len, i_size - start);
+
+		if (len < max_len)
+			cifs_extend_writeback(mapping, &count, start,
+					      max_len, &len);
+		len = min_t(loff_t, len, max_len);
+	}
 
-		rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
-						   &wsize, credits);
-		if (rc != 0) {
-			done = true;
-			break;
-		}
+	wdata->bytes = len;
 
-		tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1;
+	/* We now have a contiguous set of dirty pages, each with writeback
+	 * set; the first page is still locked at this point, but all the rest
+	 * have been unlocked.
+	 */
+	folio_unlock(folio);
 
-		wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index,
-						  &found_pages);
-		if (!wdata) {
-			rc = -ENOMEM;
-			done = true;
-			add_credits_and_wake_if(server, credits, 0);
-			break;
-		}
+	if (start < i_size) {
+		iov_iter_xarray(&wdata->iter, WRITE, &mapping->i_pages, start, len);
 
-		if (found_pages == 0) {
-			kref_put(&wdata->refcount, cifs_writedata_release);
-			add_credits_and_wake_if(server, credits, 0);
-			break;
-		}
+		rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes);
+		if (rc)
+			goto err_wdata;
 
-		nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc,
-					       end, &index, &next, &done);
+		if (wdata->cfile->invalidHandle)
+			rc = -EAGAIN;
+		else
+			rc = wdata->server->ops->async_writev(wdata,
+							      cifs_writedata_release);
+	} else {
+		/* The dirty region was entirely beyond the EOF. */
+		rc = 0;
+	}
 
-		/* nothing to write? */
-		if (nr_pages == 0) {
-			kref_put(&wdata->refcount, cifs_writedata_release);
-			add_credits_and_wake_if(server, credits, 0);
-			continue;
-		}
+err_wdata:
+	kref_put(&wdata->refcount, cifs_writedata_release);
+err_uncredit:
+	add_credits_and_wake_if(server, credits, 0);
+err_close:
+	if (cfile)
+		cifsFileInfo_put(cfile);
+err_xid:
+	free_xid(xid);
+	if (rc == 0) {
+		cifs_pages_written_back(inode, start, len);
+		wbc->nr_to_write = count;
+	} else if (is_retryable_error(rc)) {
+		cifs_pages_write_redirty(inode, start, len);
+	} else {
+		cifs_pages_write_failed(inode, start, len);
+		mapping_set_error(mapping, rc);
+	}
+	/* Indication to update ctime and mtime as close is deferred */
+	set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
+	return rc;
+}
 
-		wdata->credits = credits_on_stack;
-		wdata->cfile = cfile;
-		wdata->server = server;
-		cfile = NULL;
+/*
+ * write a region of pages back to the server
+ */
+static int cifs_writepages_region(struct address_space *mapping,
+				  struct writeback_control *wbc,
+				  loff_t start, loff_t end, loff_t *_next)
+{
+	struct folio *folio;
+	struct page *head_page;
+	ssize_t ret;
+	int n;
 
-		if (!wdata->cfile) {
-			cifs_dbg(VFS, "No writable handle in writepages rc=%d\n",
-				 get_file_rc);
-			if (is_retryable_error(get_file_rc))
-				rc = get_file_rc;
-			else
-				rc = -EBADF;
-		} else
-			rc = wdata_send_pages(wdata, nr_pages, mapping, wbc);
+	do {
+		pgoff_t index = start / PAGE_SIZE;
 
-		for (i = 0; i < nr_pages; ++i)
-			unlock_page(wdata->pages[i]);
+		n = find_get_pages_range_tag(mapping, &index, end / PAGE_SIZE,
+					     PAGECACHE_TAG_DIRTY, 1, &head_page);
+		if (!n)
+			break;
 
-		/* send failure -- clean up the mess */
-		if (rc != 0) {
-			add_credits_and_wake_if(server, &wdata->credits, 0);
-			for (i = 0; i < nr_pages; ++i) {
-				if (is_retryable_error(rc))
-					redirty_page_for_writepage(wbc,
-							   wdata->pages[i]);
-				else
-					SetPageError(wdata->pages[i]);
-				end_page_writeback(wdata->pages[i]);
-				put_page(wdata->pages[i]);
+		folio = page_folio(head_page);
+		start = folio_pos(folio); /* May regress with THPs */
+
+		/* At this point we hold neither the i_pages lock nor the
+		 * page lock: the page may be truncated or invalidated
+		 * (changing page->mapping to NULL), or even swizzled
+		 * back from swapper_space to tmpfs file mapping
+		 */
+		if (wbc->sync_mode != WB_SYNC_NONE) {
+			ret = folio_lock_killable(folio);
+			if (ret < 0) {
+				folio_put(folio);
+				return ret;
+			}
+		} else {
+			if (!folio_trylock(folio)) {
+				folio_put(folio);
+				return 0;
 			}
-			if (!is_retryable_error(rc))
-				mapping_set_error(mapping, rc);
 		}
-		kref_put(&wdata->refcount, cifs_writedata_release);
 
-		if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) {
-			index = saved_index;
+		if (folio_mapping(folio) != mapping ||
+		    !folio_test_dirty(folio)) {
+			start += folio_size(folio);
+			folio_unlock(folio);
+			folio_put(folio);
 			continue;
 		}
 
-		/* Return immediately if we received a signal during writing */
-		if (is_interrupt_error(rc)) {
-			done = true;
-			break;
+		if (folio_test_writeback(folio)) {
+			folio_unlock(folio);
+			if (wbc->sync_mode != WB_SYNC_NONE)
+				folio_wait_writeback(folio);
+			folio_put(folio);
+			continue;
 		}
 
-		if (rc != 0 && saved_rc == 0)
-			saved_rc = rc;
+		if (!folio_clear_dirty_for_io(folio))
+			BUG();
 
-		wbc->nr_to_write -= nr_pages;
-		if (wbc->nr_to_write <= 0)
-			done = true;
+		ret = cifs_write_back_from_locked_folio(mapping, wbc, folio, start, end);
+		folio_put(folio);
+		if (ret < 0)
+			return ret;
 
-		index = next;
-	}
+		start += ret;
+		cond_resched();
+	} while (wbc->nr_to_write > 0);
 
-	if (!scanned && !done) {
-		/*
-		 * We hit the last page and there is more work to be done: wrap
-		 * back to the start of the file
-		 */
-		scanned = true;
-		index = 0;
-		goto retry;
-	}
+	*_next = start;
+	return 0;
+}
 
-	if (saved_rc != 0)
-		rc = saved_rc;
+/*
+ * Write some of the pending data back to the server
+ */
+static int cifs_writepages(struct address_space *mapping,
+			   struct writeback_control *wbc)
+{
+	loff_t start, next;
+	int ret;
 
-	if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
-		mapping->writeback_index = index;
+	/* We have to be careful as we can end up racing with setattr()
+	 * truncating the pagecache since the caller doesn't take a lock here
+	 * to prevent it.
+	 */
 
-	if (cfile)
-		cifsFileInfo_put(cfile);
-	free_xid(xid);
-	/* Indication to update ctime and mtime as close is deferred */
-	set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
-	return rc;
+	if (wbc->range_cyclic) {
+		start = mapping->writeback_index * PAGE_SIZE;
+		ret = cifs_writepages_region(mapping, wbc, start, LLONG_MAX, &next);
+		if (ret == 0) {
+			mapping->writeback_index = next / PAGE_SIZE;
+			if (start > 0 && wbc->nr_to_write > 0) {
+				ret = cifs_writepages_region(mapping, wbc, 0,
+							     start, &next);
+				if (ret == 0)
+					mapping->writeback_index =
+						next / PAGE_SIZE;
+			}
+		}
+	} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
+		ret = cifs_writepages_region(mapping, wbc, 0, LLONG_MAX, &next);
+		if (wbc->nr_to_write > 0 && ret == 0)
+			mapping->writeback_index = next / PAGE_SIZE;
+	} else {
+		ret = cifs_writepages_region(mapping, wbc,
+					     wbc->range_start, wbc->range_end, &next);
+	}
+
+	return ret;
 }
 
 static int
@@ -2790,47 +2829,6 @@ int cifs_flush(struct file *file, fl_owner_t id)
 	return rc;
 }
 
-static int
-cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
-{
-	int rc = 0;
-	unsigned long i;
-
-	for (i = 0; i < num_pages; i++) {
-		pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
-		if (!pages[i]) {
-			/*
-			 * save number of pages we have already allocated and
-			 * return with ENOMEM error
-			 */
-			num_pages = i;
-			rc = -ENOMEM;
-			break;
-		}
-	}
-
-	if (rc) {
-		for (i = 0; i < num_pages; i++)
-			put_page(pages[i]);
-	}
-	return rc;
-}
-
-static inline
-size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
-{
-	size_t num_pages;
-	size_t clen;
-
-	clen = min_t(const size_t, len, wsize);
-	num_pages = DIV_ROUND_UP(clen, PAGE_SIZE);
-
-	if (cur_len)
-		*cur_len = clen;
-
-	return num_pages;
-}
-
 static void
 cifs_uncached_writedata_release(struct kref *refcount)
 {
@@ -2839,8 +2837,11 @@ cifs_uncached_writedata_release(struct kref *refcount)
 					struct cifs_writedata, refcount);
 
 	kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release);
-	for (i = 0; i < wdata->nr_pages; i++)
-		put_page(wdata->pages[i]);
+	if (wdata->bv) {
+		for (i = 0; i < wdata->bv_count; i++)
+			if (wdata->bv[i].bv_page)
+				put_page(wdata->bv[i].bv_page);
+	}
 	cifs_writedata_release(refcount);
 }
 
@@ -2866,48 +2867,6 @@ cifs_uncached_writev_complete(struct work_struct *work)
 	kref_put(&wdata->refcount, cifs_uncached_writedata_release);
 }
 
-static int
-wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
-		      size_t *len, unsigned long *num_pages)
-{
-	size_t save_len, copied, bytes, cur_len = *len;
-	unsigned long i, nr_pages = *num_pages;
-
-	save_len = cur_len;
-	for (i = 0; i < nr_pages; i++) {
-		bytes = min_t(const size_t, cur_len, PAGE_SIZE);
-		copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from);
-		cur_len -= copied;
-		/*
-		 * If we didn't copy as much as we expected, then that
-		 * may mean we trod into an unmapped area. Stop copying
-		 * at that point. On the next pass through the big
-		 * loop, we'll likely end up getting a zero-length
-		 * write and bailing out of it.
-		 */
-		if (copied < bytes)
-			break;
-	}
-	cur_len = save_len - cur_len;
-	*len = cur_len;
-
-	/*
-	 * If we have no data to send, then that probably means that
-	 * the copy above failed altogether. That's most likely because
-	 * the address in the iovec was bogus. Return -EFAULT and let
-	 * the caller free anything we allocated and bail out.
-	 */
-	if (!cur_len)
-		return -EFAULT;
-
-	/*
-	 * i + 1 now represents the number of pages we actually used in
-	 * the copy phase above.
-	 */
-	*num_pages = i + 1;
-	return 0;
-}
-
 static int
 cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
 	struct cifs_aio_ctx *ctx)
@@ -2986,14 +2945,11 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
 {
 	int rc = 0;
 	size_t cur_len;
-	unsigned long nr_pages, num_pages, i;
 	struct cifs_writedata *wdata;
 	struct iov_iter saved_from = *from;
 	loff_t saved_offset = offset;
 	pid_t pid;
 	struct TCP_Server_Info *server;
-	struct page **pagevec;
-	size_t start;
 	unsigned int xid;
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
@@ -3024,95 +2980,30 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
 
 		cur_len = min_t(const size_t, len, wsize);
 
-		if (ctx->direct_io) {
-			ssize_t result;
-
-			result = iov_iter_get_pages_alloc(
-				from, &pagevec, cur_len, &start);
-			if (result < 0) {
-				cifs_dbg(VFS,
-					 "direct_writev couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n",
-					 result, iov_iter_type(from),
-					 from->iov_offset, from->count);
-				dump_stack();
-
-				rc = result;
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-			cur_len = (size_t)result;
-			iov_iter_advance(from, cur_len);
-
-			nr_pages =
-				(cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE;
-
-			wdata = cifs_writedata_direct_alloc(pagevec,
-					     cifs_uncached_writev_complete);
-			if (!wdata) {
-				rc = -ENOMEM;
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-
-
-			wdata->page_offset = start;
-			wdata->tailsz =
-				nr_pages > 1 ?
-					cur_len - (PAGE_SIZE - start) -
-					(nr_pages - 2) * PAGE_SIZE :
-					cur_len;
-		} else {
-			nr_pages = get_numpages(wsize, len, &cur_len);
-			wdata = cifs_writedata_alloc(nr_pages,
-					     cifs_uncached_writev_complete);
-			if (!wdata) {
-				rc = -ENOMEM;
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-
-			rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
-			if (rc) {
-				kvfree(wdata->pages);
-				kfree(wdata);
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-
-			num_pages = nr_pages;
-			rc = wdata_fill_from_iovec(
-				wdata, from, &cur_len, &num_pages);
-			if (rc) {
-				for (i = 0; i < nr_pages; i++)
-					put_page(wdata->pages[i]);
-				kvfree(wdata->pages);
-				kfree(wdata);
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-
-			/*
-			 * Bring nr_pages down to the number of pages we
-			 * actually used, and free any pages that we didn't use.
-			 */
-			for ( ; nr_pages > num_pages; nr_pages--)
-				put_page(wdata->pages[nr_pages - 1]);
-
-			wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
+		wdata = cifs_writedata_alloc(cifs_uncached_writev_complete);
+		if (!wdata) {
+			rc = -ENOMEM;
+			add_credits_and_wake_if(server, credits, 0);
+			break;
 		}
 
 		wdata->sync_mode = WB_SYNC_ALL;
-		wdata->nr_pages = nr_pages;
 		wdata->offset = (__u64)offset;
 		wdata->cfile = cifsFileInfo_get(open_file);
 		wdata->server = server;
 		wdata->pid = pid;
 		wdata->bytes = cur_len;
-		wdata->pagesz = PAGE_SIZE;
 		wdata->credits = credits_on_stack;
 		wdata->ctx = ctx;
 		kref_get(&ctx->refcount);
 
+		if (ctx->direct_io) {
+			wdata->bv_count = extract_iter_to_iter(&ctx->iter, &wdata->iter,
+							       &wdata->bv);
+			cur_len = iov_iter_count(&wdata->iter);
+			iov_iter_advance(&ctx->iter, cur_len);
+		}
+
 		rc = adjust_credits(server, &wdata->credits, wdata->bytes);
 
 		if (!rc) {
@@ -3429,13 +3320,12 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
 }
 
 static struct cifs_readdata *
-cifs_readdata_direct_alloc(struct page **pages, work_func_t complete)
+cifs_readdata_alloc(work_func_t complete)
 {
 	struct cifs_readdata *rdata;
 
 	rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
 	if (rdata != NULL) {
-		rdata->pages = pages;
 		kref_init(&rdata->refcount);
 		INIT_LIST_HEAD(&rdata->list);
 		init_completion(&rdata->done);
@@ -3445,22 +3335,6 @@ cifs_readdata_direct_alloc(struct page **pages, work_func_t complete)
 	return rdata;
 }
 
-static struct cifs_readdata *
-cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete)
-{
-	struct page **pages =
-		kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
-	struct cifs_readdata *ret = NULL;
-
-	if (pages) {
-		ret = cifs_readdata_direct_alloc(pages, complete);
-		if (!ret)
-			kfree(pages);
-	}
-
-	return ret;
-}
-
 void
 cifs_readdata_release(struct kref *refcount)
 {
@@ -3475,37 +3349,9 @@ cifs_readdata_release(struct kref *refcount)
 	if (rdata->cfile)
 		cifsFileInfo_put(rdata->cfile);
 
-	kvfree(rdata->pages);
 	kfree(rdata);
 }
 
-static int
-cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages)
-{
-	int rc = 0;
-	struct page *page;
-	unsigned int i;
-
-	for (i = 0; i < nr_pages; i++) {
-		page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
-		if (!page) {
-			rc = -ENOMEM;
-			break;
-		}
-		rdata->pages[i] = page;
-	}
-
-	if (rc) {
-		unsigned int nr_page_failed = i;
-
-		for (i = 0; i < nr_page_failed; i++) {
-			put_page(rdata->pages[i]);
-			rdata->pages[i] = NULL;
-		}
-	}
-	return rc;
-}
-
 static void
 cifs_uncached_readdata_release(struct kref *refcount)
 {
@@ -3514,12 +3360,15 @@ cifs_uncached_readdata_release(struct kref *refcount)
 	unsigned int i;
 
 	kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release);
-	for (i = 0; i < rdata->nr_pages; i++) {
-		put_page(rdata->pages[i]);
+	if (rdata->bv) {
+		for (i = 0; i < rdata->bv_count; i++)
+			if (rdata->bv[i].bv_page)
+				put_page(rdata->bv[i].bv_page);
 	}
 	cifs_readdata_release(refcount);
 }
 
+#if 0
 /**
  * cifs_readdata_to_iov - copy data from pages in response to an iovec
  * @rdata:	the readdata response with list of pages holding data
@@ -3553,6 +3402,7 @@ cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
 	}
 	return remaining ? -EFAULT : 0;
 }
+#endif
 
 static void collect_uncached_read_data(struct cifs_aio_ctx *ctx);
 
@@ -3568,6 +3418,7 @@ cifs_uncached_readv_complete(struct work_struct *work)
 	kref_put(&rdata->refcount, cifs_uncached_readdata_release);
 }
 
+#if 0
 static int cifs_resend_rdata(struct cifs_readdata *rdata,
 			struct list_head *rdata_list,
 			struct cifs_aio_ctx *ctx)
@@ -3639,6 +3490,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
 	kref_put(&rdata->refcount, cifs_uncached_readdata_release);
 	return rc;
 }
+#endif
 
 static int
 cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
@@ -3646,14 +3498,13 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 		     struct cifs_aio_ctx *ctx)
 {
 	struct cifs_readdata *rdata;
-	unsigned int npages, rsize;
+	unsigned int rsize;
 	struct cifs_credits credits_on_stack;
 	struct cifs_credits *credits = &credits_on_stack;
 	size_t cur_len;
 	int rc;
 	pid_t pid;
 	struct TCP_Server_Info *server;
-	size_t start;
 	struct iov_iter direct_iov = ctx->iter;
 
 	server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses);
@@ -3682,59 +3533,28 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 
 		cur_len = min_t(const size_t, len, rsize);
 
-		if (ctx->direct_io) {
-			cur_len = (size_t)result;
-			iov_iter_advance(&direct_iov, cur_len);
-
-			rdata = cifs_readdata_direct_alloc(
-					pagevec, cifs_uncached_readv_complete);
-			if (!rdata) {
-				add_credits_and_wake_if(server, credits, 0);
-				rc = -ENOMEM;
-				break;
-			}
-
-			npages = (cur_len + start + PAGE_SIZE-1) / PAGE_SIZE;
-			rdata->page_offset = start;
-			rdata->tailsz = npages > 1 ?
-				cur_len-(PAGE_SIZE-start)-(npages-2)*PAGE_SIZE :
-				cur_len;
-
-		} else {
-
-			npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
-			/* allocate a readdata struct */
-			rdata = cifs_readdata_alloc(npages,
-					    cifs_uncached_readv_complete);
-			if (!rdata) {
-				add_credits_and_wake_if(server, credits, 0);
-				rc = -ENOMEM;
-				break;
-			}
-
-			rc = cifs_read_allocate_pages(rdata, npages);
-			if (rc) {
-				kvfree(rdata->pages);
-				kfree(rdata);
-				add_credits_and_wake_if(server, credits, 0);
-				break;
-			}
-
-			rdata->tailsz = PAGE_SIZE;
+		rdata = cifs_readdata_alloc(cifs_uncached_readv_complete);
+		if (!rdata) {
+			add_credits_and_wake_if(server, credits, 0);
+			rc = -ENOMEM;
+			break;
 		}
 
 		rdata->server = server;
 		rdata->cfile = cifsFileInfo_get(open_file);
-		rdata->nr_pages = ctx->npages;
 		rdata->offset = offset;
 		rdata->bytes = cur_len;
 		rdata->pid = pid;
-		rdata->pagesz = PAGE_SIZE;
 		rdata->credits = credits_on_stack;
 		rdata->ctx = ctx;
 		rdata->iter = ctx->iter;
 		kref_get(&ctx->refcount);
 
+		if (ctx->direct_io) {
+			cur_len = iov_iter_count(&ctx->iter);
+			iov_iter_advance(&direct_iov, cur_len);
+		}
+
 		rc = adjust_credits(server, &rdata->credits, rdata->bytes);
 
 		if (!rc) {
@@ -3768,11 +3588,9 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 {
 	struct cifs_readdata *rdata, *tmp;
 	struct iov_iter *to = &ctx->iter;
-	struct cifs_sb_info *cifs_sb;
+	//struct cifs_sb_info *cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb);
 	int rc;
 
-	cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb);
-
 	mutex_lock(&ctx->aio_mutex);
 
 	if (list_empty(&ctx->list)) {
@@ -3782,7 +3600,7 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 
 	rc = ctx->rc;
 	/* the loop below should proceed in the order of increasing offsets */
-again:
+//again:
 	list_for_each_entry_safe(rdata, tmp, &ctx->list, list) {
 		if (!rc) {
 			if (!try_wait_for_completion(&rdata->done)) {
@@ -3790,6 +3608,7 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 				return;
 			}
 
+#if 0 // TODO
 			if (rdata->result == -EAGAIN) {
 				/* resend call if it's a retryable error */
 				struct list_head tmp_list;
@@ -3840,6 +3659,9 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 				rc = rdata->result;
 			else if (!ctx->direct_io)
 				rc = cifs_readdata_to_iov(rdata, to);
+#else
+			rc = rdata->result;
+#endif
 
 			/* if there was a short read -- discard anything left */
 			if (rdata->got_bytes && rdata->got_bytes < rdata->bytes)
@@ -4135,7 +3957,7 @@ static void cifs_req_issue_op(struct netfs_read_subrequest *subreq)
 	if (rc)
 		goto out;
 
-	rdata = cifs_readdata_alloc(0, NULL);
+	rdata = cifs_readdata_alloc(NULL);
 	if (!rdata) {
 		add_credits_and_wake_if(server, credits, 0);
 		rc = -ENOMEM;
@@ -4149,8 +3971,6 @@ static void cifs_req_issue_op(struct netfs_read_subrequest *subreq)
 	rdata->offset	= subreq->start + subreq->transferred;
 	rdata->bytes	= subreq->len   - subreq->transferred;
 	rdata->pid	= pid;
-	rdata->pagesz	= PAGE_SIZE;
-	rdata->tailsz	= PAGE_SIZE;
 	rdata->credits	= credits_on_stack;
 
 	iov_iter_xarray(&rdata->iter, READ, &rreq->mapping->i_pages,
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index ece7fc8a7740..2c012fef9da8 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1032,25 +1032,6 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
 	*shash = NULL;
 }
 
-/**
- * rqst_page_get_length - obtain the length and offset for a page in smb_rqst
- * @rqst: The request descriptor
- * @page: The index of the page to query
- * @len: Where to store the length for this page:
- * @offset: Where to store the offset for this page
- */
-void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
-				unsigned int *len, unsigned int *offset)
-{
-	*len = rqst->rq_pagesz;
-	*offset = (page == 0) ? rqst->rq_offset : 0;
-
-	if (rqst->rq_npages == 1 || page == rqst->rq_npages-1)
-		*len = rqst->rq_tailsz;
-	else if (page == 0)
-		*len = rqst->rq_pagesz - rqst->rq_offset;
-}
-
 void extract_unc_hostname(const char *unc, const char **h, size_t *len)
 {
 	const char *end;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index a5ce04331613..fc07cd353b81 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -4406,6 +4406,20 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
 	sg_set_page(sg, addr, buflen, offset_in_page(buf));
 }
 
+struct cifs_init_sg_priv {
+		struct scatterlist *sg;
+		unsigned int idx;
+};
+
+static ssize_t cifs_init_sg_scan(struct iov_iter *i, const void *p,
+				 size_t len, size_t off, void *_priv)
+{
+	struct cifs_init_sg_priv *priv = _priv;
+
+	smb2_sg_set_buf(&priv->sg[priv->idx++], p, len);
+	return 0;
+}
+
 /* Assumes the first rqst has a transform header as the first iov.
  * I.e.
  * rqst[0].rq_iov[0]  is transform header
@@ -4415,43 +4429,44 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
 static struct scatterlist *
 init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
 {
+	struct cifs_init_sg_priv priv;
 	unsigned int sg_len;
-	struct scatterlist *sg;
 	unsigned int i;
 	unsigned int j;
-	unsigned int idx = 0;
+	ssize_t rc;
 	int skip;
 
 	sg_len = 1;
-	for (i = 0; i < num_rqst; i++)
-		sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
+	for (i = 0; i < num_rqst; i++) {
+		sg_len += rqst[i].rq_nvec;
+		sg_len += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
+	}
 
-	sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
-	if (!sg)
+	priv.sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
+	if (!priv.sg)
 		return NULL;
 
-	sg_init_table(sg, sg_len);
+	sg_init_table(priv.sg, sg_len);
 	for (i = 0; i < num_rqst; i++) {
+		struct iov_iter *iter = &rqst[i].rq_iter;
+
 		for (j = 0; j < rqst[i].rq_nvec; j++) {
 			/*
 			 * The first rqst has a transform header where the
 			 * first 20 bytes are not part of the encrypted blob
 			 */
 			skip = (i == 0) && (j == 0) ? 20 : 0;
-			smb2_sg_set_buf(&sg[idx++],
+			smb2_sg_set_buf(&priv.sg[priv.idx++],
 					rqst[i].rq_iov[j].iov_base + skip,
 					rqst[i].rq_iov[j].iov_len - skip);
-			}
-
-		for (j = 0; j < rqst[i].rq_npages; j++) {
-			unsigned int len, offset;
-
-			rqst_page_get_length(&rqst[i], j, &len, &offset);
-			sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
 		}
+
+		rc = iov_iter_scan(iter, iov_iter_count(iter),
+				   cifs_init_sg_scan, &priv);
+		WARN_ON(rc < 0);
 	}
-	smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
-	return sg;
+	smb2_sg_set_buf(&priv.sg[priv.idx], sign, SMB2_SIGNATURE_SIZE);
+	return priv.sg;
 }
 
 static int
@@ -4588,18 +4603,30 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
 	return rc;
 }
 
+/*
+ * Clear a read buffer, discarding the folios which have XA_MARK_0 set.
+ */
+static void netfs_clear_buffer(struct xarray *buffer)
+{
+       struct folio *folio;
+       XA_STATE(xas, buffer, 0);
+
+       rcu_read_lock();
+       xas_for_each_marked(&xas, folio, ULONG_MAX, XA_MARK_0) {
+               folio_put(folio);
+       }
+       rcu_read_unlock();
+       xa_destroy(buffer);
+}
+
 void
 smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst)
 {
-	int i, j;
+	int i;
 
-	for (i = 0; i < num_rqst; i++) {
-		if (rqst[i].rq_pages) {
-			for (j = rqst[i].rq_npages - 1; j >= 0; j--)
-				put_page(rqst[i].rq_pages[j]);
-			kfree(rqst[i].rq_pages);
-		}
-	}
+	for (i = 0; i < num_rqst; i++)
+		if (!xa_empty(&rqst[i].rq_buffer))
+			netfs_clear_buffer(&rqst[i].rq_buffer);
 }
 
 /*
@@ -4619,50 +4646,37 @@ static int
 smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
 		       struct smb_rqst *new_rq, struct smb_rqst *old_rq)
 {
-	struct page **pages;
 	struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base;
-	unsigned int npages;
+	struct page *page;
 	unsigned int orig_len = 0;
 	int i, j;
 	int rc = -ENOMEM;
 
 	for (i = 1; i < num_rqst; i++) {
-		npages = old_rq[i - 1].rq_npages;
-		pages = kmalloc_array(npages, sizeof(struct page *),
-				      GFP_KERNEL);
-		if (!pages)
-			goto err_free;
-
-		new_rq[i].rq_pages = pages;
-		new_rq[i].rq_npages = npages;
-		new_rq[i].rq_offset = old_rq[i - 1].rq_offset;
-		new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz;
-		new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz;
-		new_rq[i].rq_iov = old_rq[i - 1].rq_iov;
-		new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec;
-
-		orig_len += smb_rqst_len(server, &old_rq[i - 1]);
+		struct smb_rqst *old = &old_rq[i - 1];
+		struct smb_rqst *new = &new_rq[i];
+		struct xarray *buffer = &new->rq_buffer;
+		unsigned int npages;
+		size_t size = iov_iter_count(&old->rq_iter), seg;
 
-		for (j = 0; j < npages; j++) {
-			pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
-			if (!pages[j])
-				goto err_free;
-		}
+		orig_len += size;
+		xa_init(buffer);
 
-		/* copy pages form the old */
+		npages = DIV_ROUND_UP(size, PAGE_SIZE);
 		for (j = 0; j < npages; j++) {
-			char *dst, *src;
-			unsigned int offset, len;
-
-			rqst_page_get_length(&new_rq[i], j, &len, &offset);
-
-			dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset;
-			src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset;
+			page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+			if (!xa_store(buffer, j, page, GFP_KERNEL))
+				goto err_free;
 
-			memcpy(dst, src, len);
-			kunmap(new_rq[i].rq_pages[j]);
-			kunmap(old_rq[i - 1].rq_pages[j]);
+			seg = min(size, PAGE_SIZE);
+			if (copy_page_from_iter(page, 0, seg, &old->rq_iter) != seg) {
+				rc = -EFAULT;
+				goto err_free;
+			}
 		}
+
+		new->rq_iov = old->rq_iov;
+		new->rq_nvec = old->rq_nvec;
 	}
 
 	/* fill the 1st iov with a transform header */
@@ -4705,10 +4719,6 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
 
 	rqst.rq_iov = iov;
 	rqst.rq_nvec = 2;
-	rqst.rq_pages = pages;
-	rqst.rq_npages = npages;
-	rqst.rq_pagesz = PAGE_SIZE;
-	rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE;
 
 	rc = crypt_message(server, 1, &rqst, 0);
 	cifs_dbg(FYI, "Decrypt message returned %d\n", rc);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index ea2f8a2d9e57..4d43e06c5a32 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -4014,11 +4014,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
 	struct cifs_credits credits = { .value = 0, .instance = 0 };
 	struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
 				 .rq_nvec = 1,
-				 .rq_pages = rdata->pages,
-				 .rq_offset = rdata->page_offset,
-				 .rq_npages = rdata->nr_pages,
-				 .rq_pagesz = rdata->pagesz,
-				 .rq_tailsz = rdata->tailsz };
+				 .rq_iter = rdata->iter };
 
 	WARN_ONCE(rdata->server != mid->server,
 		  "rdata server %p != mid server %p",
@@ -4425,11 +4421,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
 
 	rqst.rq_iov = iov;
 	rqst.rq_nvec = 1;
-	rqst.rq_pages = wdata->pages;
-	rqst.rq_offset = wdata->page_offset;
-	rqst.rq_npages = wdata->nr_pages;
-	rqst.rq_pagesz = wdata->pagesz;
-	rqst.rq_tailsz = wdata->tailsz;
+	rqst.rq_iter = wdata->iter;
 #ifdef CONFIG_CIFS_SMB_DIRECT
 	if (wdata->mr) {
 		iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 61ea3d3f95b4..2c4574ae832e 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -276,26 +276,7 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
 	for (i = 0; i < nvec; i++)
 		buflen += iov[i].iov_len;
 
-	/*
-	 * Add in the page array if there is one. The caller needs to make
-	 * sure rq_offset and rq_tailsz are set correctly. If a buffer of
-	 * multiple pages ends at page boundary, rq_tailsz needs to be set to
-	 * PAGE_SIZE.
-	 */
-	if (rqst->rq_npages) {
-		if (rqst->rq_npages == 1)
-			buflen += rqst->rq_tailsz;
-		else {
-			/*
-			 * If there is more than one page, calculate the
-			 * buffer length based on rq_offset and rq_tailsz
-			 */
-			buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
-					rqst->rq_offset;
-			buflen += rqst->rq_tailsz;
-		}
-	}
-
+	buflen += iov_iter_count(&rqst->rq_iter);
 	return buflen;
 }
 
@@ -382,22 +363,12 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
 
 		total_len += sent;
 
-		/* now walk the page array and send each page in it */
-		for (i = 0; i < rqst[j].rq_npages; i++) {
-			struct bio_vec bvec;
-
-			bvec.bv_page = rqst[j].rq_pages[i];
-			rqst_page_get_length(&rqst[j], i, &bvec.bv_len,
-					     &bvec.bv_offset);
-
-			iov_iter_bvec(&smb_msg.msg_iter, WRITE,
-				      &bvec, 1, bvec.bv_len);
-			rc = smb_send_kvec(server, &smb_msg, &sent);
-			if (rc < 0)
-				break;
+		smb_msg.msg_iter = rqst[j].rq_iter;
+		rc = smb_send_kvec(server, &smb_msg, &sent);
+		if (rc < 0)
+			break;
 
-			total_len += sent;
-		}
+		total_len += sent;
 	}
 
 unmask:
diff --git a/include/linux/uio.h b/include/linux/uio.h
index 931528bc2eab..c6f84416425e 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -254,6 +254,10 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages);
 void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state);
 
 const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags);
+ssize_t iov_iter_scan(struct iov_iter *i, size_t bytes,
+		      ssize_t (*scanner)(struct iov_iter *i, const void *p,
+					 size_t len, size_t off, void *priv),
+		      void *priv);
 
 static inline size_t iov_iter_count(const struct iov_iter *i)
 {
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index cebfdb617fd8..3cd53e7ed5b9 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1790,6 +1790,48 @@ ssize_t extract_iter_to_iter(struct iov_iter *orig,
 }
 EXPORT_SYMBOL(extract_iter_to_iter);
 
+/**
+ * iov_iter_scan - Scan a source iter
+ * @i: The iterator to scan
+ * @bytes: The amount of data to scan
+ * @scanner: The function to call for each bit
+ * @priv: Private data to pass to the scanner function
+ *
+ * Scan a source iterator (WRITE-type), passing each segment to the scanner
+ * function.  If the scanner returns an error at any time, scanning stops and
+ * the error is returned, otherwise the sum of the scanner results is returned.
+ */
+ssize_t iov_iter_scan(struct iov_iter *i, size_t bytes,
+		      ssize_t (*scanner)(struct iov_iter *i, const void *p,
+					 size_t len, size_t off, void *priv),
+		      void *priv)
+{
+	ssize_t ret = 0, scanned = 0;
+
+	if (!bytes)
+		return 0;
+	if (WARN_ON(iov_iter_rw(i) != WRITE))
+		return 0;
+	if (iter_is_iovec(i))
+		might_fault();
+
+	iterate_and_advance(
+		i, bytes, base, len, off, ({
+				ret = scanner(i, base, len, off, priv);
+				if (ret < 0)
+					break;
+				scanned += ret;
+			}), ({
+				ret = scanner(i, base, len, off, priv);
+				if (ret < 0)
+					break;
+				scanned += ret;
+			})
+	);
+	return ret < 0 ? ret : scanned;
+}
+EXPORT_SYMBOL(iov_iter_scan);
+
 size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
 			       struct iov_iter *i)
 {



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading
  2022-01-06 21:32 ` [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading David Howells
@ 2022-01-07 14:35     ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-01-07 14:35 UTC (permalink / raw)
  To: David Howells; +Cc: llvm, kbuild-all

Hi David,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220106]
[cannot apply to cifs/for-next linus/master v5.16-rc8 v5.16-rc7 v5.16-rc6 v5.16-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
base:    3770333b3f8cb7c9110889853afaa49777c26ea7
config: i386-randconfig-r026-20220107 (https://download.01.org/0day-ci/archive/20220107/202201072209.NObYCbNl-lkp@intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 32167bfe64a4c5dd4eb3f7a58e24f4cba76f5ac2)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/f6d9da3b96ace245edc0dcdb78607b58edef4dcb
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
        git checkout f6d9da3b96ace245edc0dcdb78607b58edef4dcb
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/gpu/drm/amd/amdgpu/ drivers/gpu/drm/i810/ drivers/i2c/busses/ drivers/pci/endpoint/functions/ fs/cifs/ sound/pci/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/cifs/smb2ops.c:4902:7: warning: variable 'length' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (rdata->result != 0) {
                       ^~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4931:9: note: uninitialized use occurs here
           return length;
                  ^~~~~~
   fs/cifs/smb2ops.c:4902:3: note: remove the 'if' if its condition is always true
                   if (rdata->result != 0) {
                   ^~~~~~~~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4801:12: note: initialize the variable 'length' to silence this warning
           int length;
                     ^
                      = 0
   1 warning generated.


vim +4902 fs/cifs/smb2ops.c

c42a6abe3012832 Pavel Shilovsky    2016-11-17  4787  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4788  static int
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4789  handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4790  		 char *buf, unsigned int buf_len, struct page **pages,
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4791  		 unsigned int npages, unsigned int page_data_size,
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4792  		 bool is_offloaded)
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4793  {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4794  	unsigned int data_offset;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4795  	unsigned int data_len;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4796  	unsigned int cur_off;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4797  	unsigned int cur_page_idx;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4798  	unsigned int pad_len;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4799  	struct cifs_readdata *rdata = mid->callback_data;
0d35e382e4e96a4 Ronnie Sahlberg    2021-11-05  4800  	struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4801  	int length;
74dcf418fe34465 Long Li            2017-11-22  4802  	bool use_rdma_mr = false;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4803  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4804  	if (shdr->Command != SMB2_READ) {
3175eb9b577e82b Ronnie Sahlberg    2019-09-04  4805  		cifs_server_dbg(VFS, "only big read responses are supported\n");
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4806  		return -ENOTSUPP;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4807  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4808  
511c54a2f69195b Pavel Shilovsky    2017-07-08  4809  	if (server->ops->is_session_expired &&
511c54a2f69195b Pavel Shilovsky    2017-07-08  4810  	    server->ops->is_session_expired(buf)) {
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4811  		if (!is_offloaded)
183eea2ee5ba968 Shyam Prasad N     2021-07-19  4812  			cifs_reconnect(server, true);
511c54a2f69195b Pavel Shilovsky    2017-07-08  4813  		return -1;
511c54a2f69195b Pavel Shilovsky    2017-07-08  4814  	}
511c54a2f69195b Pavel Shilovsky    2017-07-08  4815  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4816  	if (server->ops->is_status_pending &&
66265f134acfb20 Pavel Shilovsky    2019-01-23  4817  			server->ops->is_status_pending(buf, server))
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4818  		return -1;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4819  
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4820  	/* set up first two iov to get credits */
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4821  	rdata->iov[0].iov_base = buf;
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4822  	rdata->iov[0].iov_len = 0;
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4823  	rdata->iov[1].iov_base = buf;
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4824  	rdata->iov[1].iov_len =
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4825  		min_t(unsigned int, buf_len, server->vals->read_rsp_size);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4826  	cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4827  		 rdata->iov[0].iov_base, rdata->iov[0].iov_len);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4828  	cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4829  		 rdata->iov[1].iov_base, rdata->iov[1].iov_len);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4830  
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4831  	rdata->result = server->ops->map_error(buf, true);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4832  	if (rdata->result != 0) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4833  		cifs_dbg(FYI, "%s: server returned error %d\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4834  			 __func__, rdata->result);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4835  		/* normal error on read response */
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4836  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4837  			mid->mid_state = MID_RESPONSE_RECEIVED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4838  		else
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4839  			dequeue_mid(mid, false);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4840  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4841  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4842  
1fc6ad2f10ad6f5 Ronnie Sahlberg    2018-06-01  4843  	data_offset = server->ops->read_data_offset(buf);
74dcf418fe34465 Long Li            2017-11-22  4844  #ifdef CONFIG_CIFS_SMB_DIRECT
74dcf418fe34465 Long Li            2017-11-22  4845  	use_rdma_mr = rdata->mr;
74dcf418fe34465 Long Li            2017-11-22  4846  #endif
74dcf418fe34465 Long Li            2017-11-22  4847  	data_len = server->ops->read_data_length(buf, use_rdma_mr);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4848  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4849  	if (data_offset < server->vals->read_rsp_size) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4850  		/*
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4851  		 * win2k8 sometimes sends an offset of 0 when the read
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4852  		 * is beyond the EOF. Treat it as if the data starts just after
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4853  		 * the header.
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4854  		 */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4855  		cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4856  			 __func__, data_offset);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4857  		data_offset = server->vals->read_rsp_size;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4858  	} else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4859  		/* data_offset is beyond the end of smallbuf */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4860  		cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4861  			 __func__, data_offset);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4862  		rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4863  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4864  			mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4865  		else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4866  			dequeue_mid(mid, rdata->result);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4867  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4868  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4869  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4870  	pad_len = data_offset - server->vals->read_rsp_size;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4871  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4872  	if (buf_len <= data_offset) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4873  		/* read response payload is in pages */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4874  		cur_page_idx = pad_len / PAGE_SIZE;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4875  		cur_off = pad_len % PAGE_SIZE;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4876  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4877  		if (cur_page_idx != 0) {
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4878  			/* data offset is beyond the 1st page of response */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4879  			cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4880  				 __func__, data_offset);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4881  			rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4882  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4883  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4884  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4885  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4886  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4887  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4888  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4889  		if (data_len > page_data_size - pad_len) {
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4890  			/* data_len is corrupt -- discard frame */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4891  			rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4892  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4893  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4894  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4895  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4896  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4897  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4898  
f6d9da3b96ace24 David Howells      2022-01-06  4899  		/* Copy the data to the output I/O iterator. */
f6d9da3b96ace24 David Howells      2022-01-06  4900  		rdata->result = cifs_copy_pages_to_iter(pages, npages, page_data_size,
f6d9da3b96ace24 David Howells      2022-01-06  4901  							cur_off, &rdata->iter);
c42a6abe3012832 Pavel Shilovsky    2016-11-17 @4902  		if (rdata->result != 0) {
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4903  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4904  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4905  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4906  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4907  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4908  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4909  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4910  	} else if (buf_len >= data_offset + data_len) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4911  		/* read response payload is in buf */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4912  		WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
f6d9da3b96ace24 David Howells      2022-01-06  4913  		length = copy_to_iter(buf + data_offset, data_len, &rdata->iter);
f6d9da3b96ace24 David Howells      2022-01-06  4914  		if (length < 0)
f6d9da3b96ace24 David Howells      2022-01-06  4915  			return length;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4916  	} else {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4917  		/* read response payload cannot be in both buf and pages */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4918  		WARN_ONCE(1, "buf can not contain only a part of read data");
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4919  		rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4920  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4921  			mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4922  		else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4923  			dequeue_mid(mid, rdata->result);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4924  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4925  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4926  
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4927  	if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4928  		mid->mid_state = MID_RESPONSE_RECEIVED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4929  	else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4930  		dequeue_mid(mid, false);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4931  	return length;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4932  }
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4933  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading
@ 2022-01-07 14:35     ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-01-07 14:35 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 14861 bytes --]

Hi David,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220106]
[cannot apply to cifs/for-next linus/master v5.16-rc8 v5.16-rc7 v5.16-rc6 v5.16-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
base:    3770333b3f8cb7c9110889853afaa49777c26ea7
config: i386-randconfig-r026-20220107 (https://download.01.org/0day-ci/archive/20220107/202201072209.NObYCbNl-lkp(a)intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 32167bfe64a4c5dd4eb3f7a58e24f4cba76f5ac2)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/f6d9da3b96ace245edc0dcdb78607b58edef4dcb
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
        git checkout f6d9da3b96ace245edc0dcdb78607b58edef4dcb
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/gpu/drm/amd/amdgpu/ drivers/gpu/drm/i810/ drivers/i2c/busses/ drivers/pci/endpoint/functions/ fs/cifs/ sound/pci/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/cifs/smb2ops.c:4902:7: warning: variable 'length' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (rdata->result != 0) {
                       ^~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4931:9: note: uninitialized use occurs here
           return length;
                  ^~~~~~
   fs/cifs/smb2ops.c:4902:3: note: remove the 'if' if its condition is always true
                   if (rdata->result != 0) {
                   ^~~~~~~~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4801:12: note: initialize the variable 'length' to silence this warning
           int length;
                     ^
                      = 0
   1 warning generated.


vim +4902 fs/cifs/smb2ops.c

c42a6abe3012832 Pavel Shilovsky    2016-11-17  4787  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4788  static int
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4789  handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4790  		 char *buf, unsigned int buf_len, struct page **pages,
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4791  		 unsigned int npages, unsigned int page_data_size,
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4792  		 bool is_offloaded)
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4793  {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4794  	unsigned int data_offset;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4795  	unsigned int data_len;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4796  	unsigned int cur_off;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4797  	unsigned int cur_page_idx;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4798  	unsigned int pad_len;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4799  	struct cifs_readdata *rdata = mid->callback_data;
0d35e382e4e96a4 Ronnie Sahlberg    2021-11-05  4800  	struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4801  	int length;
74dcf418fe34465 Long Li            2017-11-22  4802  	bool use_rdma_mr = false;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4803  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4804  	if (shdr->Command != SMB2_READ) {
3175eb9b577e82b Ronnie Sahlberg    2019-09-04  4805  		cifs_server_dbg(VFS, "only big read responses are supported\n");
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4806  		return -ENOTSUPP;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4807  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4808  
511c54a2f69195b Pavel Shilovsky    2017-07-08  4809  	if (server->ops->is_session_expired &&
511c54a2f69195b Pavel Shilovsky    2017-07-08  4810  	    server->ops->is_session_expired(buf)) {
de9ac0a6e9efdff Rohith Surabattula 2020-10-28  4811  		if (!is_offloaded)
183eea2ee5ba968 Shyam Prasad N     2021-07-19  4812  			cifs_reconnect(server, true);
511c54a2f69195b Pavel Shilovsky    2017-07-08  4813  		return -1;
511c54a2f69195b Pavel Shilovsky    2017-07-08  4814  	}
511c54a2f69195b Pavel Shilovsky    2017-07-08  4815  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4816  	if (server->ops->is_status_pending &&
66265f134acfb20 Pavel Shilovsky    2019-01-23  4817  			server->ops->is_status_pending(buf, server))
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4818  		return -1;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4819  
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4820  	/* set up first two iov to get credits */
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4821  	rdata->iov[0].iov_base = buf;
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4822  	rdata->iov[0].iov_len = 0;
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4823  	rdata->iov[1].iov_base = buf;
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4824  	rdata->iov[1].iov_len =
bb1bccb60c2ebd9 Pavel Shilovsky    2019-01-17  4825  		min_t(unsigned int, buf_len, server->vals->read_rsp_size);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4826  	cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4827  		 rdata->iov[0].iov_base, rdata->iov[0].iov_len);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4828  	cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4829  		 rdata->iov[1].iov_base, rdata->iov[1].iov_len);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4830  
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4831  	rdata->result = server->ops->map_error(buf, true);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4832  	if (rdata->result != 0) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4833  		cifs_dbg(FYI, "%s: server returned error %d\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4834  			 __func__, rdata->result);
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4835  		/* normal error on read response */
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4836  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4837  			mid->mid_state = MID_RESPONSE_RECEIVED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4838  		else
ec678eae746dd25 Pavel Shilovsky    2019-01-18  4839  			dequeue_mid(mid, false);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4840  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4841  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4842  
1fc6ad2f10ad6f5 Ronnie Sahlberg    2018-06-01  4843  	data_offset = server->ops->read_data_offset(buf);
74dcf418fe34465 Long Li            2017-11-22  4844  #ifdef CONFIG_CIFS_SMB_DIRECT
74dcf418fe34465 Long Li            2017-11-22  4845  	use_rdma_mr = rdata->mr;
74dcf418fe34465 Long Li            2017-11-22  4846  #endif
74dcf418fe34465 Long Li            2017-11-22  4847  	data_len = server->ops->read_data_length(buf, use_rdma_mr);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4848  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4849  	if (data_offset < server->vals->read_rsp_size) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4850  		/*
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4851  		 * win2k8 sometimes sends an offset of 0 when the read
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4852  		 * is beyond the EOF. Treat it as if the data starts just after
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4853  		 * the header.
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4854  		 */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4855  		cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4856  			 __func__, data_offset);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4857  		data_offset = server->vals->read_rsp_size;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4858  	} else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4859  		/* data_offset is beyond the end of smallbuf */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4860  		cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4861  			 __func__, data_offset);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4862  		rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4863  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4864  			mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4865  		else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4866  			dequeue_mid(mid, rdata->result);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4867  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4868  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4869  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4870  	pad_len = data_offset - server->vals->read_rsp_size;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4871  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4872  	if (buf_len <= data_offset) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4873  		/* read response payload is in pages */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4874  		cur_page_idx = pad_len / PAGE_SIZE;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4875  		cur_off = pad_len % PAGE_SIZE;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4876  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4877  		if (cur_page_idx != 0) {
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4878  			/* data offset is beyond the 1st page of response */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4879  			cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4880  				 __func__, data_offset);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4881  			rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4882  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4883  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4884  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4885  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4886  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4887  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4888  
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4889  		if (data_len > page_data_size - pad_len) {
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4890  			/* data_len is corrupt -- discard frame */
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4891  			rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4892  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4893  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4894  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4895  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4896  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4897  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4898  
f6d9da3b96ace24 David Howells      2022-01-06  4899  		/* Copy the data to the output I/O iterator. */
f6d9da3b96ace24 David Howells      2022-01-06  4900  		rdata->result = cifs_copy_pages_to_iter(pages, npages, page_data_size,
f6d9da3b96ace24 David Howells      2022-01-06  4901  							cur_off, &rdata->iter);
c42a6abe3012832 Pavel Shilovsky    2016-11-17 @4902  		if (rdata->result != 0) {
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4903  			if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4904  				mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4905  			else
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4906  				dequeue_mid(mid, rdata->result);
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4907  			return 0;
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4908  		}
c42a6abe3012832 Pavel Shilovsky    2016-11-17  4909  
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4910  	} else if (buf_len >= data_offset + data_len) {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4911  		/* read response payload is in buf */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4912  		WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
f6d9da3b96ace24 David Howells      2022-01-06  4913  		length = copy_to_iter(buf + data_offset, data_len, &rdata->iter);
f6d9da3b96ace24 David Howells      2022-01-06  4914  		if (length < 0)
f6d9da3b96ace24 David Howells      2022-01-06  4915  			return length;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4916  	} else {
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4917  		/* read response payload cannot be in both buf and pages */
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4918  		WARN_ONCE(1, "buf can not contain only a part of read data");
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4919  		rdata->result = -EIO;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4920  		if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4921  			mid->mid_state = MID_RESPONSE_MALFORMED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4922  		else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4923  			dequeue_mid(mid, rdata->result);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4924  		return 0;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4925  	}
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4926  
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4927  	if (is_offloaded)
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4928  		mid->mid_state = MID_RESPONSE_RECEIVED;
ac873aa3dc21707 Rohith Surabattula 2020-10-29  4929  	else
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4930  		dequeue_mid(mid, false);
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4931  	return length;
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4932  }
4326ed2f6a16ae9 Pavel Shilovsky    2016-11-17  4933  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst
  2022-01-06 21:32 ` [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst David Howells
@ 2022-01-07 17:32     ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-01-07 17:32 UTC (permalink / raw)
  To: David Howells; +Cc: llvm, kbuild-all

Hi David,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220106]
[cannot apply to cifs/for-next linus/master v5.16-rc8 v5.16-rc7 v5.16-rc6 v5.16-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
base:    3770333b3f8cb7c9110889853afaa49777c26ea7
config: i386-randconfig-r026-20220107 (https://download.01.org/0day-ci/archive/20220108/202201080142.Tton1sB4-lkp@intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 32167bfe64a4c5dd4eb3f7a58e24f4cba76f5ac2)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/259db76d15582dcfb2436b4f3a39a18c2bae00a6
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
        git checkout 259db76d15582dcfb2436b4f3a39a18c2bae00a6
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/gpu/drm/amd/amdgpu/ drivers/gpu/drm/i810/ drivers/i2c/busses/ drivers/pci/endpoint/functions/ fs/cifs/ sound/pci/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/cifs/file.c:2435:7: warning: variable 'len' is uninitialized when used here [-Wuninitialized]
                   if (len < max_len)
                       ^~~
   fs/cifs/file.c:2385:30: note: initialize the variable 'len' to silence this warning
           unsigned int xid, wsize, len, max_len;
                                       ^
                                        = 0
   1 warning generated.
--
>> fs/cifs/smb2ops.c:4681:10: warning: comparison of distinct pointer types ('typeof (size) *' (aka 'unsigned int *') and 'typeof (((1UL) << 12)) *' (aka 'unsigned long *')) [-Wcompare-distinct-pointer-types]
                           seg = min(size, PAGE_SIZE);
                                 ^~~~~~~~~~~~~~~~~~~~
   include/linux/minmax.h:45:19: note: expanded from macro 'min'
   #define min(x, y)       __careful_cmp(x, y, <)
                           ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/minmax.h:36:24: note: expanded from macro '__careful_cmp'
           __builtin_choose_expr(__safe_cmp(x, y), \
                                 ^~~~~~~~~~~~~~~~
   include/linux/minmax.h:26:4: note: expanded from macro '__safe_cmp'
                   (__typecheck(x, y) && __no_side_effects(x, y))
                    ^~~~~~~~~~~~~~~~~
   include/linux/minmax.h:20:28: note: expanded from macro '__typecheck'
           (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
                      ~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4912:7: warning: variable 'length' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (rdata->result != 0) {
                       ^~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4941:9: note: uninitialized use occurs here
           return length;
                  ^~~~~~
   fs/cifs/smb2ops.c:4912:3: note: remove the 'if' if its condition is always true
                   if (rdata->result != 0) {
                   ^~~~~~~~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4811:12: note: initialize the variable 'length' to silence this warning
           int length;
                     ^
                      = 0
   2 warnings generated.


vim +/len +2435 fs/cifs/file.c

  2369	
  2370	/*
  2371	 * Write back the locked page and any subsequent non-locked dirty pages.
  2372	 */
  2373	static ssize_t cifs_write_back_from_locked_folio(struct address_space *mapping,
  2374							 struct writeback_control *wbc,
  2375							 struct folio *folio,
  2376							 loff_t start, loff_t end)
  2377	{
  2378		struct inode *inode = mapping->host;
  2379		struct TCP_Server_Info *server;
  2380		struct cifs_writedata *wdata;
  2381		struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
  2382		struct cifs_credits credits_on_stack;
  2383		struct cifs_credits *credits = &credits_on_stack;
  2384		struct cifsFileInfo *cfile = NULL;
  2385		unsigned int xid, wsize, len, max_len;
  2386		loff_t i_size = i_size_read(inode);
  2387		long count = wbc->nr_to_write;
  2388		int rc;
  2389	
  2390		if (folio_start_writeback(folio))
  2391			BUG();
  2392	
  2393		count -= folio_nr_pages(folio);
  2394	
  2395		xid = get_xid();
  2396		server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses);
  2397	
  2398		rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
  2399		if (rc) {
  2400			cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", rc);
  2401			goto err_xid;
  2402		}
  2403	
  2404		rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
  2405						   &wsize, credits);
  2406		if (rc != 0)
  2407			goto err_close;
  2408	
  2409		wdata = cifs_writedata_alloc(cifs_writev_complete);
  2410		if (!wdata) {
  2411			rc = -ENOMEM;
  2412			goto err_uncredit;
  2413		}
  2414	
  2415		wdata->sync_mode = wbc->sync_mode;
  2416		wdata->offset = folio_pos(folio);
  2417		wdata->pid = wdata->cfile->pid;
  2418		wdata->credits = credits_on_stack;
  2419		wdata->cfile = cfile;
  2420		wdata->server = server;
  2421		cfile = NULL;
  2422	
  2423		/* Find all consecutive lockable dirty pages, stopping when we find a
  2424		 * page that is not immediately lockable, is not dirty or is missing,
  2425		 * or we reach the end of the range.
  2426		 */
  2427		if (start < i_size) {
  2428			/* Trim the write to the EOF; the extra data is ignored.  Also
  2429			 * put an upper limit on the size of a single storedata op.
  2430			 */
  2431			max_len = wsize;
  2432			max_len = min_t(unsigned long long, max_len, end - start + 1);
  2433			max_len = min_t(unsigned long long, max_len, i_size - start);
  2434	
> 2435			if (len < max_len)
  2436				cifs_extend_writeback(mapping, &count, start,
  2437						      max_len, &len);
  2438			len = min_t(loff_t, len, max_len);
  2439		}
  2440	
  2441		wdata->bytes = len;
  2442	
  2443		/* We now have a contiguous set of dirty pages, each with writeback
  2444		 * set; the first page is still locked at this point, but all the rest
  2445		 * have been unlocked.
  2446		 */
  2447		folio_unlock(folio);
  2448	
  2449		if (start < i_size) {
  2450			iov_iter_xarray(&wdata->iter, WRITE, &mapping->i_pages, start, len);
  2451	
  2452			rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes);
  2453			if (rc)
  2454				goto err_wdata;
  2455	
  2456			if (wdata->cfile->invalidHandle)
  2457				rc = -EAGAIN;
  2458			else
  2459				rc = wdata->server->ops->async_writev(wdata,
  2460								      cifs_writedata_release);
  2461		} else {
  2462			/* The dirty region was entirely beyond the EOF. */
  2463			rc = 0;
  2464		}
  2465	
  2466	err_wdata:
  2467		kref_put(&wdata->refcount, cifs_writedata_release);
  2468	err_uncredit:
  2469		add_credits_and_wake_if(server, credits, 0);
  2470	err_close:
  2471		if (cfile)
  2472			cifsFileInfo_put(cfile);
  2473	err_xid:
  2474		free_xid(xid);
  2475		if (rc == 0) {
  2476			cifs_pages_written_back(inode, start, len);
  2477			wbc->nr_to_write = count;
  2478		} else if (is_retryable_error(rc)) {
  2479			cifs_pages_write_redirty(inode, start, len);
  2480		} else {
  2481			cifs_pages_write_failed(inode, start, len);
  2482			mapping_set_error(mapping, rc);
  2483		}
  2484		/* Indication to update ctime and mtime as close is deferred */
  2485		set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
  2486		return rc;
  2487	}
  2488	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst
@ 2022-01-07 17:32     ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-01-07 17:32 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 8420 bytes --]

Hi David,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220106]
[cannot apply to cifs/for-next linus/master v5.16-rc8 v5.16-rc7 v5.16-rc6 v5.16-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
base:    3770333b3f8cb7c9110889853afaa49777c26ea7
config: i386-randconfig-r026-20220107 (https://download.01.org/0day-ci/archive/20220108/202201080142.Tton1sB4-lkp(a)intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 32167bfe64a4c5dd4eb3f7a58e24f4cba76f5ac2)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/259db76d15582dcfb2436b4f3a39a18c2bae00a6
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Howells/cifs-In-progress-conversion-to-use-iov_iters-instead-of-page-lists/20220107-053700
        git checkout 259db76d15582dcfb2436b4f3a39a18c2bae00a6
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/gpu/drm/amd/amdgpu/ drivers/gpu/drm/i810/ drivers/i2c/busses/ drivers/pci/endpoint/functions/ fs/cifs/ sound/pci/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/cifs/file.c:2435:7: warning: variable 'len' is uninitialized when used here [-Wuninitialized]
                   if (len < max_len)
                       ^~~
   fs/cifs/file.c:2385:30: note: initialize the variable 'len' to silence this warning
           unsigned int xid, wsize, len, max_len;
                                       ^
                                        = 0
   1 warning generated.
--
>> fs/cifs/smb2ops.c:4681:10: warning: comparison of distinct pointer types ('typeof (size) *' (aka 'unsigned int *') and 'typeof (((1UL) << 12)) *' (aka 'unsigned long *')) [-Wcompare-distinct-pointer-types]
                           seg = min(size, PAGE_SIZE);
                                 ^~~~~~~~~~~~~~~~~~~~
   include/linux/minmax.h:45:19: note: expanded from macro 'min'
   #define min(x, y)       __careful_cmp(x, y, <)
                           ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/minmax.h:36:24: note: expanded from macro '__careful_cmp'
           __builtin_choose_expr(__safe_cmp(x, y), \
                                 ^~~~~~~~~~~~~~~~
   include/linux/minmax.h:26:4: note: expanded from macro '__safe_cmp'
                   (__typecheck(x, y) && __no_side_effects(x, y))
                    ^~~~~~~~~~~~~~~~~
   include/linux/minmax.h:20:28: note: expanded from macro '__typecheck'
           (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
                      ~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4912:7: warning: variable 'length' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (rdata->result != 0) {
                       ^~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4941:9: note: uninitialized use occurs here
           return length;
                  ^~~~~~
   fs/cifs/smb2ops.c:4912:3: note: remove the 'if' if its condition is always true
                   if (rdata->result != 0) {
                   ^~~~~~~~~~~~~~~~~~~~~~~~
   fs/cifs/smb2ops.c:4811:12: note: initialize the variable 'length' to silence this warning
           int length;
                     ^
                      = 0
   2 warnings generated.


vim +/len +2435 fs/cifs/file.c

  2369	
  2370	/*
  2371	 * Write back the locked page and any subsequent non-locked dirty pages.
  2372	 */
  2373	static ssize_t cifs_write_back_from_locked_folio(struct address_space *mapping,
  2374							 struct writeback_control *wbc,
  2375							 struct folio *folio,
  2376							 loff_t start, loff_t end)
  2377	{
  2378		struct inode *inode = mapping->host;
  2379		struct TCP_Server_Info *server;
  2380		struct cifs_writedata *wdata;
  2381		struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
  2382		struct cifs_credits credits_on_stack;
  2383		struct cifs_credits *credits = &credits_on_stack;
  2384		struct cifsFileInfo *cfile = NULL;
  2385		unsigned int xid, wsize, len, max_len;
  2386		loff_t i_size = i_size_read(inode);
  2387		long count = wbc->nr_to_write;
  2388		int rc;
  2389	
  2390		if (folio_start_writeback(folio))
  2391			BUG();
  2392	
  2393		count -= folio_nr_pages(folio);
  2394	
  2395		xid = get_xid();
  2396		server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses);
  2397	
  2398		rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
  2399		if (rc) {
  2400			cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", rc);
  2401			goto err_xid;
  2402		}
  2403	
  2404		rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
  2405						   &wsize, credits);
  2406		if (rc != 0)
  2407			goto err_close;
  2408	
  2409		wdata = cifs_writedata_alloc(cifs_writev_complete);
  2410		if (!wdata) {
  2411			rc = -ENOMEM;
  2412			goto err_uncredit;
  2413		}
  2414	
  2415		wdata->sync_mode = wbc->sync_mode;
  2416		wdata->offset = folio_pos(folio);
  2417		wdata->pid = wdata->cfile->pid;
  2418		wdata->credits = credits_on_stack;
  2419		wdata->cfile = cfile;
  2420		wdata->server = server;
  2421		cfile = NULL;
  2422	
  2423		/* Find all consecutive lockable dirty pages, stopping when we find a
  2424		 * page that is not immediately lockable, is not dirty or is missing,
  2425		 * or we reach the end of the range.
  2426		 */
  2427		if (start < i_size) {
  2428			/* Trim the write to the EOF; the extra data is ignored.  Also
  2429			 * put an upper limit on the size of a single storedata op.
  2430			 */
  2431			max_len = wsize;
  2432			max_len = min_t(unsigned long long, max_len, end - start + 1);
  2433			max_len = min_t(unsigned long long, max_len, i_size - start);
  2434	
> 2435			if (len < max_len)
  2436				cifs_extend_writeback(mapping, &count, start,
  2437						      max_len, &len);
  2438			len = min_t(loff_t, len, max_len);
  2439		}
  2440	
  2441		wdata->bytes = len;
  2442	
  2443		/* We now have a contiguous set of dirty pages, each with writeback
  2444		 * set; the first page is still locked at this point, but all the rest
  2445		 * have been unlocked.
  2446		 */
  2447		folio_unlock(folio);
  2448	
  2449		if (start < i_size) {
  2450			iov_iter_xarray(&wdata->iter, WRITE, &mapping->i_pages, start, len);
  2451	
  2452			rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes);
  2453			if (rc)
  2454				goto err_wdata;
  2455	
  2456			if (wdata->cfile->invalidHandle)
  2457				rc = -EAGAIN;
  2458			else
  2459				rc = wdata->server->ops->async_writev(wdata,
  2460								      cifs_writedata_release);
  2461		} else {
  2462			/* The dirty region was entirely beyond the EOF. */
  2463			rc = 0;
  2464		}
  2465	
  2466	err_wdata:
  2467		kref_put(&wdata->refcount, cifs_writedata_release);
  2468	err_uncredit:
  2469		add_credits_and_wake_if(server, credits, 0);
  2470	err_close:
  2471		if (cfile)
  2472			cifsFileInfo_put(cfile);
  2473	err_xid:
  2474		free_xid(xid);
  2475		if (rc == 0) {
  2476			cifs_pages_written_back(inode, start, len);
  2477			wbc->nr_to_write = count;
  2478		} else if (is_retryable_error(rc)) {
  2479			cifs_pages_write_redirty(inode, start, len);
  2480		} else {
  2481			cifs_pages_write_failed(inode, start, len);
  2482			mapping_set_error(mapping, rc);
  2483		}
  2484		/* Indication to update ctime and mtime as close is deferred */
  2485		set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags);
  2486		return rc;
  2487	}
  2488	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-01-07 17:36 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-06 21:31 [RFC][PATCH v2 0/3] cifs: In-progress conversion to use iov_iters instead of page lists David Howells
2022-01-06 21:32 ` [PATCH v2 1/3] cifs: Use netfslib David Howells
2022-01-06 21:32 ` [PATCH v2 2/3] cifs: Reverse the way iov_iters are used in reading David Howells
2022-01-07 14:35   ` kernel test robot
2022-01-07 14:35     ` kernel test robot
2022-01-06 21:32 ` [PATCH v2 3/3] cifs: Eliminate pages list from cifs_readdata, cifs_writedata and smb_rqst David Howells
2022-01-07 17:32   ` kernel test robot
2022-01-07 17:32     ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.