Commit d08089f6 authored by David Howells's avatar David Howells Committed by Steve French

cifs: Change the I/O paths to use an iterator rather than a page list

Currently, the cifs I/O paths hand lists of pages from the VM interface
routines at the top all the way through the intervening layers to the
socket interface at the bottom.

This is a problem, however, for interfacing with netfslib which passes an
iterator through to the ->issue_read() method (and will pass an iterator
through to the ->issue_write() method in future).  Netfslib takes over
bounce buffering for direct I/O, async I/O and encrypted content, so cifs
doesn't need to do that.  Netfslib also converts IOVEC-type iterators into
BVEC-type iterators if necessary.

Further, cifs needs foliating - and folios may come in a variety of sizes,
so a page list pointing to an array of heterogeneous pages may cause
problems in places such as where crypto is done.

Change the cifs I/O paths to hand iov_iter iterators all the way through
instead.

Notes:

 (1) Some old routines are #if'd out to be removed in a follow up patch so
     as to avoid confusing diff, thereby making the diff output easier to
     follow.  I've removed functions that don't overlap with anything
     added.

 (2) struct smb_rqst loses rq_pages, rq_offset, rq_npages, rq_pagesz and
     rq_tailsz which describe the pages forming the buffer; instead there's
     an rq_iter describing the source buffer and an rq_buffer which is used
     to hold the buffer for encryption.

 (3) struct cifs_readdata and cifs_writedata are similarly modified to
     smb_rqst.  The ->read_into_pages() and ->copy_into_pages() are then
     replaced with passing the iterator directly to the socket.

     The iterators are stored in these structs so that they are persistent
     and don't get deallocated when the function returns (unlike if they
     were stack variables).

 (4) Buffered writeback is overhauled, borrowing the code from the afs
     filesystem to gather up contiguous runs of folios.  The XARRAY-type
     iterator is then used to refer directly to the pagecache and can be
     passed to the socket to transmit data directly from there.

     This includes:

	cifs_extend_writeback()
	cifs_write_back_from_locked_folio()
	cifs_writepages_region()
	cifs_writepages()

 (5) Pages are converted to folios.

 (6) Direct I/O uses netfs_extract_user_iter() to create a BVEC-type
     iterator from an IOBUF/UBUF-type source iterator.

 (7) smb2_get_aead_req() uses netfs_extract_iter_to_sg() to extract page
     fragments from the iterator into the scatterlists that the crypto
     layer prefers.

 (8) smb2_init_transform_rq() attached pages to smb_rqst::rq_buffer, an
     xarray, to use as a bounce buffer for encryption.  An XARRAY-type
     iterator can then be used to pass the bounce buffer to lower layers.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Shyam Prasad N <nspmangalore@gmail.com>
cc: Rohith Surabattula <rohiths.msft@gmail.com>
cc: Paulo Alcantara <pc@cjr.nz>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-cifs@vger.kernel.org

Link: https://lore.kernel.org/r/164311907995.2806745.400147335497304099.stgit@warthog.procyon.org.uk/ # rfc
Link: https://lore.kernel.org/r/164928620163.457102.11602306234438271112.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/165211420279.3154751.15923591172438186144.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/165348880385.2106726.3220789453472800240.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/165364827111.3334034.934805882842932881.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/166126396180.708021.271013668175370826.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/166697259595.61150.5982032408321852414.stgit@warthog.procyon.org.uk/ # rfc
Link: https://lore.kernel.org/r/166732031756.3186319.12528413619888902872.stgit@warthog.procyon.org.uk/ # rfc
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 16541195
...@@ -18,6 +18,7 @@ config CIFS ...@@ -18,6 +18,7 @@ config CIFS
select DNS_RESOLVER select DNS_RESOLVER
select ASN1 select ASN1
select OID_REGISTRY select OID_REGISTRY
select NETFS_SUPPORT
help help
This is the client VFS module for the SMB3 family of network file This is the client VFS module for the SMB3 family of network file
protocols (including the most recent, most secure dialect SMB3.1.1). protocols (including the most recent, most secure dialect SMB3.1.1).
......
...@@ -169,11 +169,11 @@ static int cifs_shash_iter(const struct iov_iter *iter, size_t maxsize, ...@@ -169,11 +169,11 @@ static int cifs_shash_iter(const struct iov_iter *iter, size_t maxsize,
} }
int __cifs_calc_signature(struct smb_rqst *rqst, int __cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature, struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash) struct shash_desc *shash)
{ {
int i; int i;
int rc; ssize_t rc;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec; int n_vec = rqst->rq_nvec;
...@@ -205,25 +205,9 @@ int __cifs_calc_signature(struct smb_rqst *rqst, ...@@ -205,25 +205,9 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
} }
} }
/* now hash over the rq_pages array */ rc = cifs_shash_iter(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter), shash);
for (i = 0; i < rqst->rq_npages; i++) { if (rc < 0)
void *kaddr; return rc;
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 = crypto_shash_final(shash, signature); rc = crypto_shash_final(shash, signature);
if (rc) if (rc)
......
...@@ -212,11 +212,9 @@ static inline void cifs_free_open_info(struct cifs_open_info_data *data) ...@@ -212,11 +212,9 @@ static inline void cifs_free_open_info(struct cifs_open_info_data *data)
struct smb_rqst { struct smb_rqst {
struct kvec *rq_iov; /* array of kvecs */ struct kvec *rq_iov; /* array of kvecs */
unsigned int rq_nvec; /* number of kvecs in array */ unsigned int rq_nvec; /* number of kvecs in array */
struct page **rq_pages; /* pointer to array of page ptrs */ size_t rq_iter_size; /* Amount of data in ->rq_iter */
unsigned int rq_offset; /* the offset to the 1st page */ struct iov_iter rq_iter; /* Data iterator */
unsigned int rq_npages; /* number pages in array */ struct xarray rq_buffer; /* Page buffer for encryption */
unsigned int rq_pagesz; /* page size to use */
unsigned int rq_tailsz; /* length of last page */
}; };
struct mid_q_entry; struct mid_q_entry;
...@@ -1421,10 +1419,11 @@ struct cifs_aio_ctx { ...@@ -1421,10 +1419,11 @@ struct cifs_aio_ctx {
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct bio_vec *bv; struct bio_vec *bv;
loff_t pos; loff_t pos;
unsigned int npages; unsigned int nr_pinned_pages;
ssize_t rc; ssize_t rc;
unsigned int len; unsigned int len;
unsigned int total_len; unsigned int total_len;
unsigned int bv_need_unpin; /* If ->bv[] needs unpinning */
bool should_dirty; bool should_dirty;
/* /*
* Indicates if this aio_ctx is for direct_io, * Indicates if this aio_ctx is for direct_io,
...@@ -1442,28 +1441,18 @@ struct cifs_readdata { ...@@ -1442,28 +1441,18 @@ struct cifs_readdata {
struct address_space *mapping; struct address_space *mapping;
struct cifs_aio_ctx *ctx; struct cifs_aio_ctx *ctx;
__u64 offset; __u64 offset;
ssize_t got_bytes;
unsigned int bytes; unsigned int bytes;
unsigned int got_bytes;
pid_t pid; pid_t pid;
int result; int result;
struct work_struct work; struct work_struct work;
int (*read_into_pages)(struct TCP_Server_Info *server, struct iov_iter iter;
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 kvec iov[2]; struct kvec iov[2];
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
struct smbd_mr *mr; struct smbd_mr *mr;
#endif #endif
unsigned int pagesz;
unsigned int page_offset;
unsigned int tailsz;
struct cifs_credits credits; struct cifs_credits credits;
unsigned int nr_pages;
struct page **pages;
}; };
/* asynchronous write support */ /* asynchronous write support */
...@@ -1475,6 +1464,8 @@ struct cifs_writedata { ...@@ -1475,6 +1464,8 @@ struct cifs_writedata {
struct work_struct work; struct work_struct work;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cifs_aio_ctx *ctx; struct cifs_aio_ctx *ctx;
struct iov_iter iter;
struct bio_vec *bv;
__u64 offset; __u64 offset;
pid_t pid; pid_t pid;
unsigned int bytes; unsigned int bytes;
...@@ -1483,12 +1474,7 @@ struct cifs_writedata { ...@@ -1483,12 +1474,7 @@ struct cifs_writedata {
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
struct smbd_mr *mr; struct smbd_mr *mr;
#endif #endif
unsigned int pagesz;
unsigned int page_offset;
unsigned int tailsz;
struct cifs_credits credits; struct cifs_credits credits;
unsigned int nr_pages;
struct page **pages;
}; };
/* /*
...@@ -2148,9 +2134,9 @@ static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const ...@@ -2148,9 +2134,9 @@ static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const
dst->FileNameLength = src->FileNameLength; dst->FileNameLength = src->FileNameLength;
} }
static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
int num_rqst, int num_rqst,
const u8 *sig) const u8 *sig)
{ {
unsigned int len, skip; unsigned int len, skip;
unsigned int nents = 0; unsigned int nents = 0;
...@@ -2170,6 +2156,19 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -2170,6 +2156,19 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst,
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
*/ */
for (i = 0; i < num_rqst; i++) { for (i = 0; i < num_rqst; i++) {
/* We really don't want a mixture of pinned and unpinned pages
* in the sglist. It's hard to keep track of which is what.
* Instead, we convert to a BVEC-type iterator higher up.
*/
if (WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
return -EIO;
/* We also don't want to have any extra refs or pins to clean
* up in the sglist.
*/
if (WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
return -EIO;
for (j = 0; j < rqst[i].rq_nvec; j++) { for (j = 0; j < rqst[i].rq_nvec; j++) {
struct kvec *iov = &rqst[i].rq_iov[j]; struct kvec *iov = &rqst[i].rq_iov[j];
...@@ -2183,7 +2182,7 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -2183,7 +2182,7 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst,
} }
skip = 0; skip = 0;
} }
nents += rqst[i].rq_npages; nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
} }
nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
return nents; return nents;
...@@ -2192,9 +2191,9 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -2192,9 +2191,9 @@ static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst,
/* We can not use the normal sg_set_buf() as we will sometimes pass a /* We can not use the normal sg_set_buf() as we will sometimes pass a
* stack object as buf. * stack object as buf.
*/ */
static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, static inline void cifs_sg_set_buf(struct sg_table *sgtable,
const void *buf, const void *buf,
unsigned int buflen) unsigned int buflen)
{ {
unsigned long addr = (unsigned long)buf; unsigned long addr = (unsigned long)buf;
unsigned int off = offset_in_page(addr); unsigned int off = offset_in_page(addr);
...@@ -2204,16 +2203,17 @@ static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, ...@@ -2204,16 +2203,17 @@ static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg,
do { do {
unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);
sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off); sg_set_page(&sgtable->sgl[sgtable->nents++],
vmalloc_to_page((void *)addr), len, off);
off = 0; off = 0;
addr += PAGE_SIZE; addr += PAGE_SIZE;
buflen -= len; buflen -= len;
} while (buflen); } while (buflen);
} else { } else {
sg_set_page(sg++, virt_to_page(addr), buflen, off); sg_set_page(&sgtable->sgl[sgtable->nents++],
virt_to_page(addr), buflen, off);
} }
return sg;
} }
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */
...@@ -584,10 +584,7 @@ int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); ...@@ -584,10 +584,7 @@ int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
int cifs_async_writev(struct cifs_writedata *wdata, int cifs_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref)); void (*release)(struct kref *kref));
void cifs_writev_complete(struct work_struct *work); void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, struct cifs_writedata *cifs_writedata_alloc(work_func_t complete);
work_func_t complete);
struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages,
work_func_t complete);
void cifs_writedata_release(struct kref *refcount); void cifs_writedata_release(struct kref *refcount);
int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
...@@ -604,13 +601,10 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, ...@@ -604,13 +601,10 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
enum securityEnum); enum securityEnum);
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
void cifs_aio_ctx_release(struct kref *refcount); void cifs_aio_ctx_release(struct kref *refcount);
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
void cifs_free_hash(struct shash_desc **sdesc); void cifs_free_hash(struct shash_desc **sdesc);
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset);
struct cifs_chan * struct cifs_chan *
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); 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); int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/task_io_accounting_ops.h> #include <linux/task_io_accounting_ops.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsfs.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsacl.h" #include "cifsacl.h"
#include "cifsproto.h" #include "cifsproto.h"
...@@ -1294,11 +1295,8 @@ cifs_readv_callback(struct mid_q_entry *mid) ...@@ -1294,11 +1295,8 @@ cifs_readv_callback(struct mid_q_entry *mid)
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct smb_rqst rqst = { .rq_iov = rdata->iov, struct smb_rqst rqst = { .rq_iov = rdata->iov,
.rq_nvec = 2, .rq_nvec = 2,
.rq_pages = rdata->pages, .rq_iter_size = iov_iter_count(&rdata->iter),
.rq_offset = rdata->page_offset, .rq_iter = rdata->iter };
.rq_npages = rdata->nr_pages,
.rq_pagesz = rdata->pagesz,
.rq_tailsz = rdata->tailsz };
struct cifs_credits credits = { .value = 1, .instance = 0 }; struct cifs_credits credits = { .value = 1, .instance = 0 };
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
...@@ -1737,11 +1735,8 @@ cifs_async_writev(struct cifs_writedata *wdata, ...@@ -1737,11 +1735,8 @@ cifs_async_writev(struct cifs_writedata *wdata,
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 2; rqst.rq_nvec = 2;
rqst.rq_pages = wdata->pages; rqst.rq_iter = wdata->iter;
rqst.rq_offset = wdata->page_offset; rqst.rq_iter_size = iov_iter_count(&wdata->iter);
rqst.rq_npages = wdata->nr_pages;
rqst.rq_pagesz = wdata->pagesz;
rqst.rq_tailsz = wdata->tailsz;
cifs_dbg(FYI, "async write at %llu %u bytes\n", cifs_dbg(FYI, "async write at %llu %u bytes\n",
wdata->offset, wdata->bytes); wdata->offset, wdata->bytes);
......
This diff is collapsed.
...@@ -165,22 +165,16 @@ static int fscache_fallback_read_page(struct inode *inode, struct page *page) ...@@ -165,22 +165,16 @@ static int fscache_fallback_read_page(struct inode *inode, struct page *page)
/* /*
* Fallback page writing interface. * Fallback page writing interface.
*/ */
static int fscache_fallback_write_page(struct inode *inode, struct page *page, static int fscache_fallback_write_pages(struct inode *inode, loff_t start, size_t len,
bool no_space_allocated_yet) bool no_space_allocated_yet)
{ {
struct netfs_cache_resources cres; struct netfs_cache_resources cres;
struct fscache_cookie *cookie = cifs_inode_cookie(inode); struct fscache_cookie *cookie = cifs_inode_cookie(inode);
struct iov_iter iter; struct iov_iter iter;
struct bio_vec bvec[1];
loff_t start = page_offset(page);
size_t len = PAGE_SIZE;
int ret; int ret;
memset(&cres, 0, sizeof(cres)); memset(&cres, 0, sizeof(cres));
bvec[0].bv_page = page; iov_iter_xarray(&iter, ITER_SOURCE, &inode->i_mapping->i_pages, start, len);
bvec[0].bv_offset = 0;
bvec[0].bv_len = PAGE_SIZE;
iov_iter_bvec(&iter, ITER_SOURCE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE);
ret = fscache_begin_write_operation(&cres, cookie); ret = fscache_begin_write_operation(&cres, cookie);
if (ret < 0) if (ret < 0)
...@@ -189,7 +183,7 @@ static int fscache_fallback_write_page(struct inode *inode, struct page *page, ...@@ -189,7 +183,7 @@ static int fscache_fallback_write_page(struct inode *inode, struct page *page,
ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode),
no_space_allocated_yet); no_space_allocated_yet);
if (ret == 0) if (ret == 0)
ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); ret = fscache_write(&cres, start, &iter, NULL, NULL);
fscache_end_operation(&cres); fscache_end_operation(&cres);
return ret; return ret;
} }
...@@ -213,12 +207,12 @@ int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) ...@@ -213,12 +207,12 @@ int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
return 0; return 0;
} }
void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) void __cifs_readahead_to_fscache(struct inode *inode, loff_t pos, size_t len)
{ {
cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", cifs_dbg(FYI, "%s: (fsc: %p, p: %llx, l: %zx, i: %p)\n",
__func__, cifs_inode_cookie(inode), page, inode); __func__, cifs_inode_cookie(inode), pos, len, inode);
fscache_fallback_write_page(inode, page, true); fscache_fallback_write_pages(inode, pos, len, true);
} }
/* /*
......
...@@ -90,7 +90,7 @@ static inline int cifs_fscache_query_occupancy(struct inode *inode, ...@@ -90,7 +90,7 @@ static inline int cifs_fscache_query_occupancy(struct inode *inode,
} }
extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage); extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage);
extern void __cifs_readpage_to_fscache(struct inode *pinode, struct page *ppage); extern void __cifs_readahead_to_fscache(struct inode *pinode, loff_t pos, size_t len);
static inline int cifs_readpage_from_fscache(struct inode *inode, static inline int cifs_readpage_from_fscache(struct inode *inode,
...@@ -101,11 +101,11 @@ static inline int cifs_readpage_from_fscache(struct inode *inode, ...@@ -101,11 +101,11 @@ static inline int cifs_readpage_from_fscache(struct inode *inode,
return -ENOBUFS; return -ENOBUFS;
} }
static inline void cifs_readpage_to_fscache(struct inode *inode, static inline void cifs_readahead_to_fscache(struct inode *inode,
struct page *page) loff_t pos, size_t len)
{ {
if (cifs_inode_cookie(inode)) if (cifs_inode_cookie(inode))
__cifs_readpage_to_fscache(inode, page); __cifs_readahead_to_fscache(inode, pos, len);
} }
#else /* CONFIG_CIFS_FSCACHE */ #else /* CONFIG_CIFS_FSCACHE */
...@@ -141,7 +141,7 @@ cifs_readpage_from_fscache(struct inode *inode, struct page *page) ...@@ -141,7 +141,7 @@ cifs_readpage_from_fscache(struct inode *inode, struct page *page)
} }
static inline static inline
void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} void cifs_readahead_to_fscache(struct inode *inode, loff_t pos, size_t len) {}
#endif /* CONFIG_CIFS_FSCACHE */ #endif /* CONFIG_CIFS_FSCACHE */
......
...@@ -966,16 +966,22 @@ cifs_aio_ctx_release(struct kref *refcount) ...@@ -966,16 +966,22 @@ cifs_aio_ctx_release(struct kref *refcount)
/* /*
* ctx->bv is only set if setup_aio_ctx_iter() was call successfuly * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly
* which means that iov_iter_get_pages() was a success and thus that * which means that iov_iter_extract_pages() was a success and thus
* we have taken reference on pages. * that we may have references or pins on pages that we need to
* release.
*/ */
if (ctx->bv) { if (ctx->bv) {
unsigned i; if (ctx->should_dirty || ctx->bv_need_unpin) {
unsigned int i;
for (i = 0; i < ctx->npages; i++) { for (i = 0; i < ctx->nr_pinned_pages; i++) {
if (ctx->should_dirty) struct page *page = ctx->bv[i].bv_page;
set_page_dirty(ctx->bv[i].bv_page);
put_page(ctx->bv[i].bv_page); if (ctx->should_dirty)
set_page_dirty(page);
if (ctx->bv_need_unpin)
unpin_user_page(page);
}
} }
kvfree(ctx->bv); kvfree(ctx->bv);
} }
...@@ -983,95 +989,6 @@ cifs_aio_ctx_release(struct kref *refcount) ...@@ -983,95 +989,6 @@ cifs_aio_ctx_release(struct kref *refcount)
kfree(ctx); 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_pages2(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;
}
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 * cifs_alloc_hash - allocate hash and hash context together
* @name: The name of the crypto hash algo * @name: The name of the crypto hash algo
...@@ -1129,25 +1046,6 @@ cifs_free_hash(struct shash_desc **sdesc) ...@@ -1129,25 +1046,6 @@ cifs_free_hash(struct shash_desc **sdesc)
*sdesc = NULL; *sdesc = 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(const 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) void extract_unc_hostname(const char *unc, const char **h, size_t *len)
{ {
const char *end; const char *end;
......
This diff is collapsed.
...@@ -4139,10 +4139,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len, ...@@ -4139,10 +4139,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
struct smbd_buffer_descriptor_v1 *v1; struct smbd_buffer_descriptor_v1 *v1;
bool need_invalidate = server->dialect == SMB30_PROT_ID; bool need_invalidate = server->dialect == SMB30_PROT_ID;
rdata->mr = smbd_register_mr( rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->iter,
server->smbd_conn, rdata->pages, true, need_invalidate);
rdata->nr_pages, rdata->page_offset,
rdata->tailsz, true, need_invalidate);
if (!rdata->mr) if (!rdata->mr)
return -EAGAIN; return -EAGAIN;
...@@ -4199,15 +4197,9 @@ smb2_readv_callback(struct mid_q_entry *mid) ...@@ -4199,15 +4197,9 @@ smb2_readv_callback(struct mid_q_entry *mid)
(struct smb2_hdr *)rdata->iov[0].iov_base; (struct smb2_hdr *)rdata->iov[0].iov_base;
struct cifs_credits credits = { .value = 0, .instance = 0 }; struct cifs_credits credits = { .value = 0, .instance = 0 };
struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
.rq_nvec = 1, }; .rq_nvec = 1,
.rq_iter = rdata->iter,
if (rdata->got_bytes) { .rq_iter_size = iov_iter_count(&rdata->iter), };
rqst.rq_pages = rdata->pages;
rqst.rq_offset = rdata->page_offset;
rqst.rq_npages = rdata->nr_pages;
rqst.rq_pagesz = rdata->pagesz;
rqst.rq_tailsz = rdata->tailsz;
}
WARN_ONCE(rdata->server != mid->server, WARN_ONCE(rdata->server != mid->server,
"rdata server %p != mid server %p", "rdata server %p != mid server %p",
...@@ -4225,6 +4217,8 @@ smb2_readv_callback(struct mid_q_entry *mid) ...@@ -4225,6 +4217,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
if (server->sign && !mid->decrypted) { if (server->sign && !mid->decrypted) {
int rc; int rc;
iov_iter_revert(&rqst.rq_iter, rdata->got_bytes);
iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
rc = smb2_verify_signature(&rqst, server); rc = smb2_verify_signature(&rqst, server);
if (rc) if (rc)
cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
...@@ -4567,7 +4561,7 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -4567,7 +4561,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
req->VolatileFileId = io_parms->volatile_fid; req->VolatileFileId = io_parms->volatile_fid;
req->WriteChannelInfoOffset = 0; req->WriteChannelInfoOffset = 0;
req->WriteChannelInfoLength = 0; req->WriteChannelInfoLength = 0;
req->Channel = 0; req->Channel = SMB2_CHANNEL_NONE;
req->Offset = cpu_to_le64(io_parms->offset); req->Offset = cpu_to_le64(io_parms->offset);
req->DataOffset = cpu_to_le16( req->DataOffset = cpu_to_le16(
offsetof(struct smb2_write_req, Buffer)); offsetof(struct smb2_write_req, Buffer));
...@@ -4587,26 +4581,18 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -4587,26 +4581,18 @@ smb2_async_writev(struct cifs_writedata *wdata,
*/ */
if (smb3_use_rdma_offload(io_parms)) { if (smb3_use_rdma_offload(io_parms)) {
struct smbd_buffer_descriptor_v1 *v1; struct smbd_buffer_descriptor_v1 *v1;
size_t data_size = iov_iter_count(&wdata->iter);
bool need_invalidate = server->dialect == SMB30_PROT_ID; bool need_invalidate = server->dialect == SMB30_PROT_ID;
wdata->mr = smbd_register_mr( wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->iter,
server->smbd_conn, wdata->pages, false, need_invalidate);
wdata->nr_pages, wdata->page_offset,
wdata->tailsz, false, need_invalidate);
if (!wdata->mr) { if (!wdata->mr) {
rc = -EAGAIN; rc = -EAGAIN;
goto async_writev_out; goto async_writev_out;
} }
req->Length = 0; req->Length = 0;
req->DataOffset = 0; req->DataOffset = 0;
if (wdata->nr_pages > 1) req->RemainingBytes = cpu_to_le32(data_size);
req->RemainingBytes =
cpu_to_le32(
(wdata->nr_pages - 1) * wdata->pagesz -
wdata->page_offset + wdata->tailsz
);
else
req->RemainingBytes = cpu_to_le32(wdata->tailsz);
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate) if (need_invalidate)
req->Channel = SMB2_CHANNEL_RDMA_V1; req->Channel = SMB2_CHANNEL_RDMA_V1;
...@@ -4625,19 +4611,14 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -4625,19 +4611,14 @@ smb2_async_writev(struct cifs_writedata *wdata,
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
rqst.rq_pages = wdata->pages; rqst.rq_iter = wdata->iter;
rqst.rq_offset = wdata->page_offset; rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter);
rqst.rq_npages = wdata->nr_pages;
rqst.rq_pagesz = wdata->pagesz;
rqst.rq_tailsz = wdata->tailsz;
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
if (wdata->mr) { if (wdata->mr)
iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
rqst.rq_npages = 0;
}
#endif #endif
cifs_dbg(FYI, "async write at %llu %u bytes\n", cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n",
io_parms->offset, io_parms->length); io_parms->offset, io_parms->length, iov_iter_count(&rqst.rq_iter));
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
/* For RDMA read, I/O size is in RemainingBytes not in Length */ /* For RDMA read, I/O size is in RemainingBytes not in Length */
......
This diff is collapsed.
...@@ -302,8 +302,8 @@ struct smbd_mr { ...@@ -302,8 +302,8 @@ struct smbd_mr {
/* Interfaces to register and deregister MR for RDMA read/write */ /* Interfaces to register and deregister MR for RDMA read/write */
struct smbd_mr *smbd_register_mr( struct smbd_mr *smbd_register_mr(
struct smbd_connection *info, struct page *pages[], int num_pages, struct smbd_connection *info, struct iov_iter *iter,
int offset, int tailsz, bool writing, bool need_invalidate); bool writing, bool need_invalidate);
int smbd_deregister_mr(struct smbd_mr *mr); int smbd_deregister_mr(struct smbd_mr *mr);
#else #else
......
...@@ -270,26 +270,7 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) ...@@ -270,26 +270,7 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
for (i = 0; i < nvec; i++) for (i = 0; i < nvec; i++)
buflen += iov[i].iov_len; buflen += iov[i].iov_len;
/* buflen += iov_iter_count(&rqst->rq_iter);
* 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;
}
}
return buflen; return buflen;
} }
...@@ -376,23 +357,15 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, ...@@ -376,23 +357,15 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
total_len += sent; total_len += sent;
/* now walk the page array and send each page in it */ if (iov_iter_count(&rqst[j].rq_iter) > 0) {
for (i = 0; i < rqst[j].rq_npages; i++) { smb_msg.msg_iter = rqst[j].rq_iter;
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, ITER_SOURCE,
&bvec, 1, bvec.bv_len);
rc = smb_send_kvec(server, &smb_msg, &sent); rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0) if (rc < 0)
break; break;
total_len += sent; total_len += sent;
} }
}
}
unmask: unmask:
sigprocmask(SIG_SETMASK, &oldmask, NULL); sigprocmask(SIG_SETMASK, &oldmask, NULL);
...@@ -1640,11 +1613,11 @@ int ...@@ -1640,11 +1613,11 @@ int
cifs_discard_remaining_data(struct TCP_Server_Info *server) cifs_discard_remaining_data(struct TCP_Server_Info *server)
{ {
unsigned int rfclen = server->pdu_size; unsigned int rfclen = server->pdu_size;
int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) - size_t remaining = rfclen + HEADER_PREAMBLE_SIZE(server) -
server->total_read; server->total_read;
while (remaining > 0) { while (remaining > 0) {
int length; ssize_t length;
length = cifs_discard_from_socket(server, length = cifs_discard_from_socket(server,
min_t(size_t, remaining, min_t(size_t, remaining,
...@@ -1790,10 +1763,15 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1790,10 +1763,15 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return cifs_readv_discard(server, mid); return cifs_readv_discard(server, mid);
} }
length = rdata->read_into_pages(server, rdata, data_len); #ifdef CONFIG_CIFS_SMB_DIRECT
if (length < 0) if (rdata->mr)
return length; 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; server->total_read += length;
cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment