[Linux-cachefs] [RFC PATCH 3/7] cifs: Change the I/O paths to use an iterator rather than a page list
Rohith Surabattula
rohiths.msft at gmail.com
Mon Jan 31 05:06:27 UTC 2022
Hi David,
After copying the buf to the XArray iterator, "got_bytes" field is not
updated. As a result, the read of data which is less than page size
failed.
Below is the patch to fix the above issue.
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index e1649ac194db..5faf45672891 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -4917,6 +4917,7 @@ handle_read_data(struct TCP_Server_Info *server,
struct mid_q_entry *mid,
length = copy_to_iter(buf + data_offset, data_len,
&rdata->iter);
if (length < 0)
return length;
+ rdata->got_bytes = data_len;
} else {
/* read response payload cannot be in both buf and pages */
WARN_ONCE(1, "buf can not contain only a part of read data");
Regards,
Rohith
On Wed, Jan 26, 2022 at 1:21 AM David Howells <dhowells at redhat.com> wrote:
>
>
> ---
>
> fs/cifs/cifsencrypt.c | 40 +++--
> fs/cifs/cifsfs.c | 2
> fs/cifs/cifsfs.h | 3
> fs/cifs/cifsglob.h | 28 +---
> fs/cifs/cifsproto.h | 10 +
> fs/cifs/cifssmb.c | 224 +++++++++++++++++++-----------
> fs/cifs/connect.c | 16 ++
> fs/cifs/misc.c | 19 ---
> fs/cifs/smb2ops.c | 365 ++++++++++++++++++++++++-------------------------
> fs/cifs/smb2pdu.c | 12 --
> fs/cifs/transport.c | 37 +----
> 11 files changed, 379 insertions(+), 377 deletions(-)
>
> diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
> index 0912d8bbbac1..69bbf3d6c4d4 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.c b/fs/cifs/cifsfs.c
> index 199edac0cb59..a56cb9c8c5ff 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -935,7 +935,7 @@ cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
> ssize_t rc;
> struct inode *inode = file_inode(iocb->ki_filp);
>
> - if (iocb->ki_filp->f_flags & O_DIRECT)
> + if (iocb->ki_flags & IOCB_DIRECT)
> return cifs_user_readv(iocb, iter);
>
> rc = cifs_revalidate_mapping(inode);
> diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
> index 15a5c5db038b..1c77bbc0815f 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 0a4085ced40f..3a4fed645636 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -191,11 +191,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;
> @@ -1323,28 +1320,18 @@ struct cifs_readdata {
> struct address_space *mapping;
> struct cifs_aio_ctx *ctx;
> __u64 offset;
> + ssize_t got_bytes;
> unsigned int bytes;
> - unsigned int got_bytes;
> 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;
> #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,6 +1343,8 @@ 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;
> @@ -1364,12 +1353,7 @@ struct cifs_writedata {
> #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 d3701295402d..1b143f0a03c0 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -242,6 +242,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 *);
> @@ -575,10 +578,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,
> @@ -602,8 +602,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 071e2f21a7db..38e7276352e2 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -24,6 +24,7 @@
> #include <linux/task_io_accounting_ops.h>
> #include <linux/uaccess.h>
> #include "cifspdu.h"
> +#include "cifsfs.h"
> #include "cifsglob.h"
> #include "cifsacl.h"
> #include "cifsproto.h"
> @@ -1388,11 +1389,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,
> @@ -1539,10 +1540,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",
> @@ -1566,11 +1572,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",
> @@ -1925,10 +1927,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.
> @@ -1936,51 +2021,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);
> @@ -1995,33 +2085,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);
> @@ -2034,7 +2116,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);
> @@ -2045,40 +2126,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);
> - cifs_readpage_to_fscache(inode, 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);
> @@ -2186,11 +2252,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/connect.c b/fs/cifs/connect.c
> index ed210d774a21..d0851c9881b3 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -704,6 +704,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/misc.c b/fs/cifs/misc.c
> index 56598f7dbe00..f5fe5720456a 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -1122,25 +1122,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 af5d0830bc8a..e1649ac194db 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -4406,15 +4406,30 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
> static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
> unsigned int buflen)
> {
> - void *addr;
> + struct page *page;
> +
> /*
> * VMAP_STACK (at least) puts stack into the vmalloc address space
> */
> if (is_vmalloc_addr(buf))
> - addr = vmalloc_to_page(buf);
> + page = vmalloc_to_page(buf);
> else
> - addr = virt_to_page(buf);
> - sg_set_page(sg, addr, buflen, offset_in_page(buf));
> + page = virt_to_page(buf);
> + sg_set_page(sg, page, 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 len;
> }
>
> /* Assumes the first rqst has a transform header as the first iov.
> @@ -4426,43 +4441,46 @@ 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++) {
> + unsigned int np = iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
> + sg_len += rqst[i].rq_nvec + np;
> + }
>
> - sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
> - if (!sg)
> + priv.idx = 0;
> + 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;
> + size_t count = iov_iter_count(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, count, cifs_init_sg_scan, &priv);
> + iov_iter_revert(iter, count);
> + 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
> @@ -4599,18 +4617,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 cifs_clear_xarray_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))
> + cifs_clear_xarray_buffer(&rqst[i].rq_buffer);
> }
>
> /*
> @@ -4630,50 +4660,51 @@ 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]);
> -
> - for (j = 0; j < npages; j++) {
> - pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
> - if (!pages[j])
> - goto err_free;
> - }
> -
> - /* copy pages form the old */
> - 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;
> + 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, copied = 0;
> +
> + xa_init(buffer);
> +
> + if (size > 0) {
> + npages = DIV_ROUND_UP(size, PAGE_SIZE);
> + for (j = 0; j < npages; j++) {
> + void *o;
> +
> + rc = -ENOMEM;
> + page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
> + if (!page)
> + goto err_free;
> + page->index = j;
> + o = xa_store(buffer, j, page, GFP_KERNEL);
> + if (xa_is_err(o)) {
> + rc = xa_err(o);
> + put_page(page);
> + 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 - copied, PAGE_SIZE);
> + if (copy_page_from_iter(page, 0, seg, &old->rq_iter) != seg) {
> + rc = -EFAULT;
> + goto err_free;
> + }
> + copied += seg;
> + }
> + iov_iter_xarray(&new->rq_iter, iov_iter_rw(&old->rq_iter),
> + buffer, 0, size);
> }
> + new->rq_iov = old->rq_iov;
> + new->rq_nvec = old->rq_nvec;
> + orig_len += smb_rqst_len(server, new);
> }
>
> /* fill the 1st iov with a transform header */
> @@ -4701,12 +4732,12 @@ smb3_is_transform_hdr(void *buf)
>
> static int
> decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
> - unsigned int buf_data_size, struct page **pages,
> - unsigned int npages, unsigned int page_data_size,
> + unsigned int buf_data_size, struct iov_iter *iter,
> bool is_offloaded)
> {
> struct kvec iov[2];
> struct smb_rqst rqst = {NULL};
> + size_t iter_size = 0;
> int rc;
>
> iov[0].iov_base = buf;
> @@ -4716,10 +4747,10 @@ 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;
> + if (iter) {
> + rqst.rq_iter = *iter;
> + iter_size = iov_iter_count(iter);
> + }
>
> rc = crypt_message(server, 1, &rqst, 0);
> cifs_dbg(FYI, "Decrypt message returned %d\n", rc);
> @@ -4730,73 +4761,37 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
> memmove(buf, iov[1].iov_base, buf_data_size);
>
> if (!is_offloaded)
> - server->total_read = buf_data_size + page_data_size;
> + server->total_read = buf_data_size + iter_size;
>
> return rc;
> }
>
> static int
> -read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,
> - unsigned int npages, unsigned int len)
> +cifs_copy_pages_to_iter(struct xarray *pages, unsigned int data_size,
> + unsigned int skip, struct iov_iter *iter)
> {
> - int i;
> - int length;
> + struct page *page;
> + unsigned long index;
>
> - for (i = 0; i < npages; i++) {
> - struct page *page = pages[i];
> - size_t n;
> + xa_for_each(pages, index, page) {
> + size_t n, len = min_t(unsigned int, PAGE_SIZE - skip, data_size);
>
> - n = len;
> - if (len >= PAGE_SIZE) {
> - /* enough data to fill the page */
> - n = PAGE_SIZE;
> - len -= n;
> - } else {
> - zero_user(page, len, PAGE_SIZE - len);
> - len = 0;
> + n = copy_page_to_iter(page, skip, len, iter);
> + if (n != len) {
> + cifs_dbg(VFS, "%s: something went wrong\n", __func__);
> + return -EIO;
> }
> - length = cifs_read_page_from_socket(server, page, 0, n);
> - if (length < 0)
> - return length;
> - server->total_read += length;
> + data_size -= n;
> + skip = 0;
> }
>
> return 0;
> }
>
> -static int
> -init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
> - unsigned int cur_off, struct bio_vec **page_vec)
> -{
> - struct bio_vec *bvec;
> - int i;
> -
> - bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL);
> - if (!bvec)
> - return -ENOMEM;
> -
> - 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;
> - }
> -
> - *page_vec = bvec;
> - return 0;
> -}
> -
> static int
> handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
> - char *buf, unsigned int buf_len, struct page **pages,
> - unsigned int npages, unsigned int page_data_size,
> - bool is_offloaded)
> + char *buf, unsigned int buf_len, struct xarray *pages,
> + unsigned int pages_len, bool is_offloaded)
> {
> unsigned int data_offset;
> unsigned int data_len;
> @@ -4805,9 +4800,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;
>
> @@ -4896,7 +4888,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
> return 0;
> }
>
> - if (data_len > page_data_size - pad_len) {
> + if (data_len > pages_len - pad_len) {
> /* data_len is corrupt -- discard frame */
> rdata->result = -EIO;
> if (is_offloaded)
> @@ -4906,8 +4898,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, pages_len,
> + cur_off, &rdata->iter);
> if (rdata->result != 0) {
> if (is_offloaded)
> mid->mid_state = MID_RESPONSE_MALFORMED;
> @@ -4915,14 +4908,15 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
> dequeue_mid(mid, rdata->result);
> return 0;
> }
> + rdata->got_bytes = pages_len;
>
> - 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);
> + WARN_ONCE(pages && !xa_empty(pages),
> + "read data can be either in buf or in pages");
> + 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");
> @@ -4934,13 +4928,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
> @@ -4951,9 +4938,8 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
> struct smb2_decrypt_work {
> struct work_struct decrypt;
> struct TCP_Server_Info *server;
> - struct page **ppages;
> + struct xarray buffer;
> char *buf;
> - unsigned int npages;
> unsigned int len;
> };
>
> @@ -4962,11 +4948,13 @@ static void smb2_decrypt_offload(struct work_struct *work)
> {
> struct smb2_decrypt_work *dw = container_of(work,
> struct smb2_decrypt_work, decrypt);
> - int i, rc;
> + int rc;
> struct mid_q_entry *mid;
> + struct iov_iter iter;
>
> + iov_iter_xarray(&iter, READ, &dw->buffer, 0, dw->len);
> rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size,
> - dw->ppages, dw->npages, dw->len, true);
> + &iter, true);
> if (rc) {
> cifs_dbg(VFS, "error decrypting rc=%d\n", rc);
> goto free_pages;
> @@ -4980,7 +4968,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
> mid->decrypted = true;
> rc = handle_read_data(dw->server, mid, dw->buf,
> dw->server->vals->read_rsp_size,
> - dw->ppages, dw->npages, dw->len,
> + &dw->buffer, dw->len,
> true);
> if (rc >= 0) {
> #ifdef CONFIG_CIFS_STATS2
> @@ -5012,10 +5000,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
> }
>
> free_pages:
> - for (i = dw->npages-1; i >= 0; i--)
> - put_page(dw->ppages[i]);
> -
> - kfree(dw->ppages);
> + cifs_clear_xarray_buffer(&dw->buffer);
> cifs_small_buf_release(dw->buf);
> kfree(dw);
> }
> @@ -5025,47 +5010,66 @@ static int
> receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
> int *num_mids)
> {
> + struct page *page;
> char *buf = server->smallbuf;
> struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
> - unsigned int npages;
> - struct page **pages;
> - unsigned int len;
> + struct iov_iter iter;
> + unsigned int len, npages;
> unsigned int buflen = server->pdu_size;
> int rc;
> int i = 0;
> struct smb2_decrypt_work *dw;
>
> + dw = kzalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL);
> + if (!dw)
> + return -ENOMEM;
> + xa_init(&dw->buffer);
> + INIT_WORK(&dw->decrypt, smb2_decrypt_offload);
> + dw->server = server;
> +
> *num_mids = 1;
> len = min_t(unsigned int, buflen, server->vals->read_rsp_size +
> sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
>
> rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
> if (rc < 0)
> - return rc;
> + goto free_dw;
> server->total_read += rc;
>
> len = le32_to_cpu(tr_hdr->OriginalMessageSize) -
> server->vals->read_rsp_size;
> + dw->len = len;
> npages = DIV_ROUND_UP(len, PAGE_SIZE);
>
> - pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
> - if (!pages) {
> - rc = -ENOMEM;
> - goto discard_data;
> - }
> -
> + rc = -ENOMEM;
> for (; i < npages; i++) {
> - pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
> - if (!pages[i]) {
> - rc = -ENOMEM;
> + void *old;
> +
> + page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
> + if (!page) {
> + goto discard_data;
> + }
> + page->index = i;
> + old = xa_store(&dw->buffer, i, page, GFP_KERNEL);
> + if (xa_is_err(old)) {
> + rc = xa_err(old);
> + put_page(page);
> goto discard_data;
> }
> }
>
> - /* read read data into pages */
> - rc = read_data_into_pages(server, pages, npages, len);
> - if (rc)
> - goto free_pages;
> + iov_iter_xarray(&iter, READ, &dw->buffer, 0, npages * PAGE_SIZE);
> +
> + /* Read the data into the buffer and clear excess bufferage. */
> + rc = cifs_read_iter_from_socket(server, &iter, dw->len);
> + if (rc < 0)
> + goto discard_data;
> +
> + server->total_read += rc;
> + if (rc < npages * PAGE_SIZE)
> + iov_iter_zero(npages * PAGE_SIZE - rc, &iter);
> + iov_iter_revert(&iter, npages * PAGE_SIZE);
> + iov_iter_truncate(&iter, dw->len);
>
> rc = cifs_discard_remaining_data(server);
> if (rc)
> @@ -5078,39 +5082,28 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
>
> if ((server->min_offload) && (server->in_flight > 1) &&
> (server->pdu_size >= server->min_offload)) {
> - dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL);
> - if (dw == NULL)
> - goto non_offloaded_decrypt;
> -
> dw->buf = server->smallbuf;
> server->smallbuf = (char *)cifs_small_buf_get();
>
> - INIT_WORK(&dw->decrypt, smb2_decrypt_offload);
> -
> - dw->npages = npages;
> - dw->server = server;
> - dw->ppages = pages;
> - dw->len = len;
> queue_work(decrypt_wq, &dw->decrypt);
> *num_mids = 0; /* worker thread takes care of finding mid */
> return -1;
> }
>
> -non_offloaded_decrypt:
> rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size,
> - pages, npages, len, false);
> + &iter, false);
> if (rc)
> goto free_pages;
>
> *mid = smb2_find_mid(server, buf);
> - if (*mid == NULL)
> + if (*mid == NULL) {
> cifs_dbg(FYI, "mid not found\n");
> - else {
> + } else {
> cifs_dbg(FYI, "mid found\n");
> (*mid)->decrypted = true;
> rc = handle_read_data(server, *mid, buf,
> server->vals->read_rsp_size,
> - pages, npages, len, false);
> + &dw->buffer, dw->len, false);
> if (rc >= 0) {
> if (server->ops->is_network_name_deleted) {
> server->ops->is_network_name_deleted(buf,
> @@ -5120,9 +5113,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
> }
>
> free_pages:
> - for (i = i - 1; i >= 0; i--)
> - put_page(pages[i]);
> - kfree(pages);
> + cifs_clear_xarray_buffer(&dw->buffer);
> +free_dw:
> + kfree(dw);
> return rc;
> discard_data:
> cifs_discard_remaining_data(server);
> @@ -5160,7 +5153,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
> server->total_read += length;
>
> buf_size = pdu_length - sizeof(struct smb2_transform_hdr);
> - length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false);
> + length = decrypt_raw_data(server, buf, buf_size, NULL, false);
> if (length)
> return length;
>
> @@ -5259,7 +5252,7 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
> char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
>
> return handle_read_data(server, mid, buf, server->pdu_size,
> - NULL, 0, 0, false);
> + NULL, 0, false);
> }
>
> static int
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 7e7909b1ae11..ebbea7526ee2 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -4118,11 +4118,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",
> @@ -4522,11 +4518,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 8540f7c13eae..cb19c43c0009 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,23 +363,15 @@ __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);
> + if (iov_iter_count(&rqst[j].rq_iter) > 0) {
> + smb_msg.msg_iter = rqst[j].rq_iter;
> rc = smb_send_kvec(server, &smb_msg, &sent);
> if (rc < 0)
> break;
> -
> total_len += sent;
> }
> - }
> +
> +}
>
> unmask:
> sigprocmask(SIG_SETMASK, &oldmask, NULL);
>
>
More information about the Linux-cachefs
mailing list