CIFS: add support for direct I/O
Subsystems (3)
v3 cover CIFS: add support for direct I/O
From: Long Li <[email protected]>
This patch set implements direct I/O.
In normal code path (even with cache=none), CIFS copies I/O data from user-space to kernel-space for security reasons of possible protocol required signing and encryption on user data.
With this patch set, CIFS passes the I/O data directly from user-space buffer to the transport layer, when file system is mounted with "cache-none".
Patch v2 addressed comments from Christoph Hellwig <[email protected]> and Tom Talpey <[email protected]> to implement direct I/O for both socket and RDMA.
Patch v3 added support for kernel AIO.
Long Li (16):
CIFS: Add support for direct pages in rdata
CIFS: Use offset when reading pages
CIFS: Add support for direct pages in wdata
CIFS: pass page offset when issuing SMB write
CIFS: Calculate the correct request length based on page offset and
tail size
CIFS: Introduce helper function to get page offset and length in
smb_rqst
CIFS: When sending data on socket, pass the correct page offset
CIFS: SMBD: Support page offset in RDMA send
CIFS: SMBD: Support page offset in RDMA recv
CIFS: SMBD: Do not call ib_dereg_mr on invalidated memory registration
CIFS: SMBD: Support page offset in memory registration
CIFS: Pass page offset for calculating signature
CIFS: Pass page offset for encrypting
CIFS: Add support for direct I/O read
CIFS: Add support for direct I/O write
CIFS: Add direct I/O functions to file_operations
fs/cifs/cifsencrypt.c | 9 +- fs/cifs/cifsfs.c | 10 +- fs/cifs/cifsfs.h | 2 + fs/cifs/cifsglob.h | 11 +- fs/cifs/cifsproto.h | 9 +- fs/cifs/cifssmb.c | 19 +- fs/cifs/connect.c | 5 +- fs/cifs/file.c | 477 ++++++++++++++++++++++++++++++++++++++++++-------- fs/cifs/misc.c | 17 ++ fs/cifs/smb2ops.c | 22 ++- fs/cifs/smb2pdu.c | 20 ++- fs/cifs/smbdirect.c | 156 ++++++++++------- fs/cifs/smbdirect.h | 2 +- fs/cifs/transport.c | 34 ++-- 14 files changed, 606 insertions(+), 187 deletions(-)
Signature · 1 line
2.7.4
PATCH v3 1/16 CIFS: Add support for direct pages in rdata
From: Long Li <[email protected]>
Add a function to allocate rdata without allocating pages for data transfer. This gives the caller an option to pass a number of pages that point to the data buffer.
rdata is reponsible for free those pages after it's done.
--- fs/cifs/cifsglob.h | 3 +-- fs/cifs/file.c | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-)
fs/cifs/cifsglob.h +1 -2
@@ -1173,14 +1173,13 @@ struct cifs_readdata {struct kvec iov[2 ];#ifdef CONFIG_CIFS_SMB_DIRECT struct smbd_mr *mr; -struct page **direct_pages;#endif unsigned int pagesz;unsigned int page_offset;unsigned int tailsz;unsigned int credits;unsigned int nr_pages; -struct page *pages[]; +struct page **pages; };struct cifs_writedata ;
fs/cifs/file.c +20 -3
@@ -2880,13 +2880,13 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) }static struct cifs_readdata * -cifs_readdata_alloc (unsigned int nr_pages,work_func_t complete) +cifs_readdata_direct_alloc (struct page **pages,work_func_t complete) {struct cifs_readdata *rdata; - rdata =kzalloc (sizeof (*rdata) + (sizeof (struct page *) * nr_pages), -GFP_KERNEL ); + 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);
@@ -2896,6 +2896,22 @@ cifs_readdata_alloc(unsigned int nr_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 = +kzalloc (sizeof (struct page *) * nr_pages,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) {
@@ -2910,6 +2926,7 @@ cifs_readdata_release(struct kref *refcount)if (rdata->cfile)cifsFileInfo_put (rdata->cfile); +kvfree (rdata->pages);kfree (rdata); }
Signature · 1 line
2.7.4
PATCH v3 2/16 CIFS: Use offset when reading pages
In reply to Long LiFrom: Long Li <[email protected]>
With offset defined in rdata, transport functions need to look at this offset when reading data into the correct places in pages.
--- fs/cifs/cifsproto.h | 4 +++- fs/cifs/cifssmb.c | 1 + fs/cifs/connect.c | 5 +++-- fs/cifs/file.c | 52 +++++++++++++++++++++++++++++++++++++--------------- fs/cifs/smb2ops.c | 2 +- fs/cifs/smb2pdu.c | 1 + 6 files changed, 46 insertions(+), 19 deletions(-)
fs/cifs/cifsproto.h +3 -1
@@ -203,7 +203,9 @@ extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);extern int cifs_read_from_socket (struct TCP_Server_Info *server,char *buf,unsigned int to_read);extern int cifs_read_page_from_socket (struct TCP_Server_Info *server, -struct page *page,unsigned int to_read); +struct page *page, +unsigned int page_offset, +unsigned int to_read);extern int cifs_setup_cifs_sb (struct smb_vol *pvolume_info,struct cifs_sb_info *cifs_sb);extern int cifs_match_super (struct super_block *,void *);
fs/cifs/cifssmb.c +1 -0
@@ -1596,6 +1596,7 @@ cifs_readv_callback(struct mid_q_entry *mid)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 };
fs/cifs/connect.c +3 -2
@@ -594,10 +594,11 @@ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,int cifs_read_page_from_socket (struct TCP_Server_Info *server,struct page *page, -unsigned int to_read) +unsigned int page_offset,unsigned int to_read) {struct msghdr smb_msg; -struct bio_vec bv = {.bv_page = page, .bv_len = to_read}; +struct bio_vec bv = { + .bv_page = page, .bv_len = to_read, .bv_offset = page_offset};iov_iter_bvec (&smb_msg.msg_iter,READ |ITER_BVEC , &bv,1 , to_read);return cifs_readv_from_socket (server, &smb_msg); }
fs/cifs/file.c +37 -15
117 lines not on this page · raw message
fs/cifs/smb2ops.c +1 -1
@@ -2467,7 +2467,7 @@ read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,zero_user (page, len,PAGE_SIZE - len); len =0 ; } - length =cifs_read_page_from_socket (server, page, n); + length =cifs_read_page_from_socket (server, page,0 , n);if (length <0 )return length; server->total_read += length;
fs/cifs/smb2pdu.c +1 -0
@@ -2683,6 +2683,7 @@ smb2_readv_callback(struct mid_q_entry *mid)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 };
Signature · 1 line
2.7.4
PATCH v3 3/16 CIFS: Add support for direct pages in wdata
In reply to Long LiFrom: Long Li <[email protected]>
Add a function to allocate wdata without allocating pages for data transfer. This gives the caller an option to pass a number of pages that point to the data buffer to write to.
wdata is reponsible for free those pages after it's done.
--- fs/cifs/cifsglob.h | 3 +-- fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-)
fs/cifs/cifsglob.h +1 -2
@@ -1199,14 +1199,13 @@ struct cifs_writedata {int result;#ifdef CONFIG_CIFS_SMB_DIRECT struct smbd_mr *mr; -struct page **direct_pages;#endif unsigned int pagesz;unsigned int page_offset;unsigned int tailsz;unsigned int credits;unsigned int nr_pages; -struct page *pages[]; +struct page **pages; };/*
fs/cifs/cifsproto.h +2 -0
@@ -533,6 +533,8 @@ int cifs_async_writev(struct cifs_writedata *wdata,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);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,
fs/cifs/cifssmb.c +14 -3
@@ -1953,6 +1953,7 @@ cifs_writedata_release(struct kref *refcount)if (wdata->cfile)cifsFileInfo_put (wdata->cfile); +kvfree (wdata->pages);kfree (wdata); }
@@ -2076,12 +2077,22 @@ cifs_writev_complete(struct work_struct *work)struct cifs_writedata *cifs_writedata_alloc (unsigned int nr_pages,work_func_t complete) { +struct page **pages = +kzalloc (sizeof (struct page *) * nr_pages,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) +{struct cifs_writedata *wdata; -/* writedata + number of page pointers */ - wdata =kzalloc (sizeof (*wdata) + -sizeof (struct page *) * nr_pages,GFP_NOFS ); + 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);
Signature · 1 line
2.7.4
PATCH v3 4/16 CIFS: pass page offset when issuing SMB write
In reply to Long LiFrom: Long Li <[email protected]>
When issuing SMB writes, pass along the write data page offset to transport.
--- fs/cifs/cifssmb.c | 1 + fs/cifs/smb2pdu.c | 1 + 2 files changed, 2 insertions(+)
fs/cifs/cifssmb.c +1 -0
@@ -2200,6 +2200,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;
fs/cifs/smb2pdu.c +1 -0
@@ -3046,6 +3046,7 @@ smb2_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;
Signature · 1 line
2.7.4
PATCH v3 5/16 CIFS: Calculate the correct request length based on page offset and tail size
In reply to Long LiFrom: Long Li <[email protected]>
It's possible that the page offset is non-zero in the pages in a request, change the function to calculate the correct data buffer length.
--- fs/cifs/transport.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
fs/cifs/transport.c +17 -3
@@ -212,10 +212,24 @@ rqst_len(struct smb_rqst *rqst)for (i =0 ; i < rqst->rq_nvec; i++) buflen += iov[i].iov_len; -/* add in the page array if there is one */ +/* +* 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) { - buflen += rqst->rq_pagesz * (rqst->rq_npages -1 ); - buflen += rqst->rq_tailsz; +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;
Signature · 1 line
2.7.4
PATCH v3 6/16 CIFS: Introduce helper function to get page offset and length in smb_rqst
In reply to Long LiFrom: Long Li <[email protected]>
Introduce a function rqst_page_get_length to return the page offset and length for a given page in smb_rqst. This function is to be used by following patches.
--- fs/cifs/cifsproto.h | 3 +++ fs/cifs/misc.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+)
fs/cifs/cifsproto.h +3 -0
@@ -557,4 +557,7 @@ 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); +#endif /* _CIFSPROTO_H */
fs/cifs/misc.c +17 -0
@@ -905,3 +905,20 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)crypto_free_shash (*shash); *shash =NULL ; } + +/** +* rqst_page_get_length - obtain the length and offset for a page in smb_rqst +* Input: rqst - a smb_rqst, page - a page index for rqst +* Output: *len - the length for this page, *offset - 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; +}
Signature · 1 line
2.7.4
PATCH v3 7/16 CIFS: When sending data on socket, pass the correct page offset
In reply to Long LiFrom: Long Li <[email protected]>
It's possible that the offset is non-zero in the page to send, change the function to pass this offset to socket.
--- fs/cifs/transport.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
fs/cifs/transport.c +6 -8
@@ -288,15 +288,13 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)/* now walk the page array and send each page in it */ for (i =0 ; i < rqst->rq_npages; i++) { -size_t len = i == rqst->rq_npages -1 - ? rqst->rq_tailsz - : rqst->rq_pagesz; -struct bio_vec bvec = { - .bv_page = rqst->rq_pages[i], - .bv_len = len - }; +struct bio_vec bvec; + + bvec.bv_page = rqst->rq_pages[i]; +rqst_page_get_length (rqst, i, &bvec.bv_len, &bvec.bv_offset); +iov_iter_bvec (&smb_msg.msg_iter,WRITE |ITER_BVEC , - &bvec,1 , len); + &bvec,1 , bvec.bv_len); rc =smb_send_kvec (server, &smb_msg, &sent);if (rc <0 )break ;
Signature · 1 line
2.7.4
PATCH v3 8/16 CIFS: SMBD: Support page offset in RDMA send
In reply to Long LiFrom: Long Li <[email protected]>
The RDMA send function needs to look at offset in the request pages, and send data starting from there.
--- fs/cifs/smbdirect.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
fs/cifs/smbdirect.c +19 -8
@@ -17,6 +17,7 @@#include <linux/highmem.h> #include "smbdirect.h" #include "cifs_debug.h" +#include "cifsproto.h" static struct smbd_response *get_empty_queue_buffer (struct smbd_connection *info);
@@ -2082,7 +2083,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)struct kvec vec;int nvecs;int size; -int buflen =0 , remaining_data_length; +unsigned int buflen =0 , remaining_data_length;int start, i, j;int max_iov_size = info->max_send_size -sizeof (struct smbd_data_transfer );
@@ -2113,10 +2114,17 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) buflen += iov[i].iov_len; } -/* add in the page array if there is one */ +/* +* Add in the page array if there is one. The caller needs to set +* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and +* ends at page boundary +*/ if (rqst->rq_npages) { - buflen += rqst->rq_pagesz * (rqst->rq_npages -1 ); - buflen += rqst->rq_tailsz; +if (rqst->rq_npages ==1 ) + buflen += rqst->rq_tailsz; +else + buflen += rqst->rq_pagesz * (rqst->rq_npages -1 ) - + rqst->rq_offset + rqst->rq_tailsz; } if (buflen + sizeof(struct smbd_data_transfer) >
@@ -2213,8 +2221,9 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)/* now sending pages if there are any */ for (i =0 ; i < rqst->rq_npages; i++) { - buflen = (i == rqst->rq_npages-1 ) ? - rqst->rq_tailsz : rqst->rq_pagesz; +unsigned int offset; + +rqst_page_get_length (rqst, i, &buflen, &offset); nvecs = (buflen + max_iov_size -1 ) / max_iov_size;log_write (INFO ,"sending pages buflen=%d nvecs=%d\n" , buflen, nvecs);
@@ -2225,9 +2234,11 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) remaining_data_length -= size;log_write (INFO ,"sending pages i=%d offset=%d size=%d" " remaining_data_length=%d\n" , - i, j*max_iov_size, size, remaining_data_length); + i, j*max_iov_size+offset, size, + remaining_data_length); rc =smbd_post_send_page ( - info, rqst->rq_pages[i], j*max_iov_size, + info, rqst->rq_pages[i], + j*max_iov_size + offset, size, remaining_data_length);if (rc) goto done;
Signature · 1 line
2.7.4
PATCH v3 9/16 CIFS: SMBD: Support page offset in RDMA recv
In reply to Long LiFrom: Long Li <[email protected]>
RDMA recv function needs to place data to the correct place starting at page offset.
--- fs/cifs/smbdirect.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
fs/cifs/smbdirect.c +11 -7
@@ -2004,10 +2004,12 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,* return value: actual data read */ static int smbd_recv_page (struct smbd_connection *info, -struct page *page,unsigned int to_read) +struct page *page,unsigned int page_offset, +unsigned int to_read) {int ret;char *to_address; +void *page_address;/* make sure we have the page ready for read */ ret = wait_event_interruptible(
@@ -2015,16 +2017,17 @@ static int smbd_recv_page(struct smbd_connection *info, info->reassembly_data_length >= to_read || info->transport_status != SMBD_CONNECTED);if (ret) -return 0 ; +return ret;/* now we can read from reassembly queue and not sleep */ - to_address =kmap_atomic (page); + page_address =kmap_atomic (page); + to_address = (char *) page_address + page_offset;log_read (INFO ,"reading from page=%p address=%p to_read=%d\n" , page, to_address, to_read); ret =smbd_recv_buf (info, to_address, to_read); -kunmap_atomic (to_address); +kunmap_atomic (page_address);return ret; }
@@ -2038,7 +2041,7 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {char *buf;struct page *page; -unsigned int to_read; +unsigned int to_read, page_offset;int rc; info->smbd_recv_pending++;
@@ -2052,15 +2055,16 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)case READ |ITER_BVEC : page = msg->msg_iter.bvec->bv_page; + page_offset = msg->msg_iter.bvec->bv_offset; to_read = msg->msg_iter.bvec->bv_len; - rc =smbd_recv_page (info, page, to_read); + rc =smbd_recv_page (info, page, page_offset, to_read);break ;default :/* It's a bug in upper layer to get there */ cifs_dbg (VFS ,"CIFS: invalid msg type %d\n" , msg->msg_iter.type); - rc = -EIO ; + rc = -EINVAL ; } info->smbd_recv_pending--;
Signature · 1 line
2.7.4
PATCH v3 10/16 CIFS: SMBD: Do not call ib_dereg_mr on invalidated memory registration
In reply to Long LiFrom: Long Li <[email protected]>
It is not necessary to deregister a memory registration after it has been successfully invalidated.
--- fs/cifs/smbdirect.c | 82 ++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 41 deletions(-)
fs/cifs/smbdirect.c +41 -41
@@ -2296,50 +2296,50 @@ static void smbd_mr_recovery_work(struct work_struct *work)int rc;list_for_each_entry (smbdirect_mr, &info->mr_list, list) { -if (smbdirect_mr->state ==MR_INVALIDATED || - smbdirect_mr->state ==MR_ERROR ) { - -if (smbdirect_mr->state ==MR_INVALIDATED ) { -ib_dma_unmap_sg ( - info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, - smbdirect_mr->dir); - smbdirect_mr->state =MR_READY ; - }else if (smbdirect_mr->state ==MR_ERROR ) { - -/* recover this MR entry */ - rc =ib_dereg_mr (smbdirect_mr->mr); -if (rc) { -log_rdma_mr (ERR , -"ib_dereg_mr failed rc=%x\n" , - rc); -smbd_disconnect_rdma_connection (info); - } +if (smbdirect_mr->state ==MR_INVALIDATED ) +ib_dma_unmap_sg ( + info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, + smbdirect_mr->dir); +else if (smbdirect_mr->state ==MR_ERROR ) { + +/* recover this MR entry */ + rc =ib_dereg_mr (smbdirect_mr->mr); +if (rc) { +log_rdma_mr (ERR , +"ib_dereg_mr failed rc=%x\n" , + rc); +smbd_disconnect_rdma_connection (info); +continue ; + } - smbdirect_mr->mr =ib_alloc_mr ( - info->pd, info->mr_type, + smbdirect_mr->mr =ib_alloc_mr ( + info->pd, info->mr_type, + info->max_frmr_depth); +if (IS_ERR (smbdirect_mr->mr)) { +log_rdma_mr (ERR , +"ib_alloc_mr failed mr_type=%x " +"max_frmr_depth=%x\n" , + info->mr_type, info->max_frmr_depth); -if (IS_ERR (smbdirect_mr->mr)) { -log_rdma_mr (ERR , -"ib_alloc_mr failed mr_type=%x " -"max_frmr_depth=%x\n" , - info->mr_type, - info->max_frmr_depth); -smbd_disconnect_rdma_connection (info); - } - - smbdirect_mr->state =MR_READY ; +smbd_disconnect_rdma_connection (info); +continue ; } -/* smbdirect_mr->state is updated by this function -* and is read and updated by I/O issuing CPUs trying -* to get a MR, the call to atomic_inc_return -* implicates a memory barrier and guarantees this -* value is updated before waking up any calls to -* get_mr() from the I/O issuing CPUs -*/ -if (atomic_inc_return (&info->mr_ready_count) ==1 ) -wake_up_interruptible (&info->wait_mr); - } + }else +/* This MR is being used, don't recover it */ +continue ; + + smbdirect_mr->state =MR_READY ; + +/* smbdirect_mr->state is updated by this function +* and is read and updated by I/O issuing CPUs trying +* to get a MR, the call to atomic_inc_return +* implicates a memory barrier and guarantees this +* value is updated before waking up any calls to +* get_mr() from the I/O issuing CPUs +*/ +if (atomic_inc_return (&info->mr_ready_count) ==1 ) +wake_up_interruptible (&info->wait_mr); } }
Signature · 1 line
2.7.4
PATCH v3 11/16 CIFS: SMBD: Support page offset in memory registration
In reply to Long LiFrom: Long Li <[email protected]>
Change code to pass the correct page offset during memory registration for RDMA read/write.
--- fs/cifs/smb2pdu.c | 18 ++++++++++++------ fs/cifs/smbdirect.c | 29 +++++++++++++++++++++-------- fs/cifs/smbdirect.h | 2 +- 3 files changed, 34 insertions(+), 15 deletions(-)
fs/cifs/smb2pdu.c +12 -6
@@ -2623,8 +2623,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len, rdata->mr =smbd_register_mr ( server->smbd_conn, rdata->pages, - rdata->nr_pages, rdata->tailsz, - true, need_invalidate); + rdata->nr_pages, rdata->page_offset, + rdata->tailsz, true, need_invalidate);if (!rdata->mr)return -ENOBUFS ;
@@ -3013,16 +3013,22 @@ smb2_async_writev(struct cifs_writedata *wdata, wdata->mr =smbd_register_mr ( server->smbd_conn, wdata->pages, - wdata->nr_pages, wdata->tailsz, - false, need_invalidate); + wdata->nr_pages, wdata->page_offset, + wdata->tailsz, false, need_invalidate);if (!wdata->mr) { rc = -ENOBUFS ; goto async_writev_out; } req->Length =0 ; req->DataOffset =0 ; - req->RemainingBytes = -cpu_to_le32 ((wdata->nr_pages-1 )*PAGE_SIZE + wdata->tailsz); +if (wdata->nr_pages >1 ) + 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 ;if (need_invalidate) req->Channel =SMB2_CHANNEL_RDMA_V1 ;
fs/cifs/smbdirect.c +21 -8
@@ -2475,7 +2475,7 @@ static struct smbd_mr *get_mr(struct smbd_connection *info)*/ struct smbd_mr *smbd_register_mr (struct smbd_connection *info,struct page *pages[],int num_pages, -int tailsz,bool writing,bool need_invalidate) +int offset,int tailsz,bool writing,bool need_invalidate) {struct smbd_mr *smbdirect_mr;int rc, i;
@@ -2498,17 +2498,30 @@ struct smbd_mr *smbd_register_mr( smbdirect_mr->sgl_count = num_pages;sg_init_table (smbdirect_mr->sgl, num_pages); -for (i =0 ; i < num_pages -1 ; i++) -sg_set_page (&smbdirect_mr->sgl[i], pages[i],PAGE_SIZE ,0 ); +log_rdma_mr (INFO ,"num_pages=0x%x offset=0x%x tailsz=0x%x\n" , + num_pages, offset, tailsz); + +if (num_pages ==1 ) { +sg_set_page (&smbdirect_mr->sgl[0 ], pages[0 ], tailsz, offset); + goto skip_multiple_pages; + } -sg_set_page (&smbdirect_mr->sgl[i], pages[i], - tailsz ? tailsz :PAGE_SIZE ,0 ); +/* We have at least two pages to register */ +sg_set_page ( + &smbdirect_mr->sgl[0 ], pages[0 ],PAGE_SIZE - offset, offset); + i =1 ; +while (i < num_pages -1 ) { +sg_set_page (&smbdirect_mr->sgl[i], pages[i],PAGE_SIZE ,0 ); + i++; + } +sg_set_page (&smbdirect_mr->sgl[i], pages[i], tailsz,0 ); +skip_multiple_pages: dir = writing ?DMA_FROM_DEVICE :DMA_TO_DEVICE ; smbdirect_mr->dir = dir; rc =ib_dma_map_sg (info->id->device, smbdirect_mr->sgl, num_pages, dir);if (!rc) { -log_rdma_mr (INFO ,"ib_dma_map_sg num_pages=%x dir=%x rc=%x\n" , +log_rdma_mr (ERR ,"ib_dma_map_sg num_pages=%x dir=%x rc=%x\n" , num_pages, dir, rc); goto dma_map_error; }
@@ -2516,8 +2529,8 @@ struct smbd_mr *smbd_register_mr( rc =ib_map_mr_sg (smbdirect_mr->mr, smbdirect_mr->sgl, num_pages,NULL ,PAGE_SIZE );if (rc != num_pages) { -log_rdma_mr (INFO , -"ib_map_mr_sg failed rc = %x num_pages = %x\n" , +log_rdma_mr (ERR , +"ib_map_mr_sg failed rc = %d num_pages = %x\n" , rc, num_pages); goto map_mr_error; }
fs/cifs/smbdirect.h +1 -1
@@ -321,7 +321,7 @@ struct smbd_mr {/* Interfaces to register and deregister MR for RDMA read/write */ struct smbd_mr *smbd_register_mr (struct smbd_connection *info,struct page *pages[],int num_pages, -int tailsz,bool writing,bool need_invalidate); +int offset,int tailsz,bool writing,bool need_invalidate);int smbd_deregister_mr (struct smbd_mr *mr);#else
Signature · 1 line
2.7.4
PATCH v3 12/16 CIFS: Pass page offset for calculating signature
In reply to Long LiFrom: Long Li <[email protected]>
When calculating signature for the packet, it needs to read into the correct page offset for the data.
--- fs/cifs/cifsencrypt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
fs/cifs/cifsencrypt.c +5 -4
@@ -68,11 +68,12 @@ 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 =kmap (rqst->rq_pages[i]); -size_t len = rqst->rq_pagesz; +void *kaddr; +unsigned int len, offset; -if (i == rqst->rq_npages -1 ) - len = rqst->rq_tailsz; +rqst_page_get_length (rqst, i, &len, &offset); + + kaddr = (char *)kmap (rqst->rq_pages[i]) + offset;crypto_shash_update (shash, kaddr, len);
Signature · 1 line
2.7.4
PATCH v3 13/16 CIFS: Pass page offset for encrypting
In reply to Long LiFrom: Long Li <[email protected]>
Encryption function needs to read data starting page offset from input buffer.
This doesn't affect decryption path since it allocates its own page buffers.
--- fs/cifs/smb2ops.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
fs/cifs/smb2ops.c +13 -7
@@ -2189,9 +2189,10 @@ init_sg(struct smb_rqst *rqst, u8 *sign)smb2_sg_set_buf (&sg[i], rqst->rq_iov[i].iov_base, rqst->rq_iov[i].iov_len);for (j =0 ; i < sg_len -1 ; i++, j++) { -unsigned int len = (j < rqst->rq_npages -1 ) ? rqst->rq_pagesz - : rqst->rq_tailsz; -sg_set_page (&sg[i], rqst->rq_pages[j], len,0 ); +unsigned int len, offset; + +rqst_page_get_length (rqst, j, &len, &offset); +sg_set_page (&sg[i], rqst->rq_pages[j], len, offset); }smb2_sg_set_buf (&sg[sg_len -1 ], sign,SMB2_SIGNATURE_SIZE );return sg;
@@ -2332,6 +2333,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,return rc; new_rq->rq_pages = pages; + new_rq->rq_offset = old_rq->rq_offset; new_rq->rq_npages = old_rq->rq_npages; new_rq->rq_pagesz = old_rq->rq_pagesz; new_rq->rq_tailsz = old_rq->rq_tailsz;
@@ -2363,10 +2365,14 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,/* copy pages form the old */ for (i =0 ; i < npages; i++) { -char *dst =kmap (new_rq->rq_pages[i]); -char *src =kmap (old_rq->rq_pages[i]); -unsigned int len = (i < npages -1 ) ? new_rq->rq_pagesz : - new_rq->rq_tailsz; +char *dst, *src; +unsigned int offset, len; + +rqst_page_get_length (new_rq, i, &len, &offset); + + dst = (char *)kmap (new_rq->rq_pages[i]) + offset; + src = (char *)kmap (old_rq->rq_pages[i]) + offset; +memcpy (dst, src, len);kunmap (new_rq->rq_pages[i]);kunmap (old_rq->rq_pages[i]);
Signature · 1 line
2.7.4
PATCH v3 14/16 CIFS: Add support for direct I/O read
In reply to Long LiFrom: Long Li <[email protected]>
With direct I/O read, we transfer the data directly from transport layer to the user data buffer.
Change in v3: added support for kernel AIO
--- fs/cifs/cifsfs.h | 1 + fs/cifs/cifsglob.h | 5 ++ fs/cifs/file.c | 209 +++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 186 insertions(+), 29 deletions(-)
fs/cifs/cifsfs.h +1 -0
@@ -102,6 +102,7 @@ extern int cifs_open(struct inode *inode, struct file *file);extern int cifs_close (struct inode *inode,struct file *file);extern int cifs_closedir (struct inode *inode,struct file *file);extern ssize_t cifs_user_readv (struct kiocb *iocb,struct iov_iter *to); +extern ssize_t cifs_direct_readv (struct kiocb *iocb,struct iov_iter *to);extern ssize_t cifs_strict_readv (struct kiocb *iocb,struct iov_iter *to);extern ssize_t cifs_user_writev (struct kiocb *iocb,struct iov_iter *from);extern ssize_t cifs_strict_writev (struct kiocb *iocb,struct iov_iter *from);
fs/cifs/cifsglob.h +5 -0
@@ -1146,6 +1146,11 @@ struct cifs_aio_ctx {unsigned int len;unsigned int total_len;bool should_dirty; +/* +* Indicates if this aio_ctx is for direct_io, +* If yes, iter is a copy of the user passed iov_iter +*/ +bool direct_io; };struct cifs_readdata ;
fs/cifs/file.c +180 -29
304 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v3 15/16 CIFS: Add support for direct I/O write
In reply to Long LiFrom: Long Li <[email protected]>
With direct I/O write, user supplied buffers are pinned to the memory and data are transferred directly from user buffers to the transport layer.
Change in v3: added support for kernel AIO
--- fs/cifs/cifsfs.h | 1 + fs/cifs/file.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 165 insertions(+), 31 deletions(-)
fs/cifs/cifsfs.h +1 -0
@@ -105,6 +105,7 @@ extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to);extern ssize_t cifs_direct_readv (struct kiocb *iocb,struct iov_iter *to);extern ssize_t cifs_strict_readv (struct kiocb *iocb,struct iov_iter *to);extern ssize_t cifs_user_writev (struct kiocb *iocb,struct iov_iter *from); +extern ssize_t cifs_direct_writev (struct kiocb *iocb,struct iov_iter *from);extern ssize_t cifs_strict_writev (struct kiocb *iocb,struct iov_iter *from);extern int cifs_lock (struct file *,int ,struct file_lock *);extern int cifs_fsync (struct file *,loff_t ,loff_t ,int );
fs/cifs/file.c +164 -31
229 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v3 16/16 CIFS: Add direct I/O functions to file_operations
In reply to Long LiFrom: Long Li <[email protected]>
With direct read/write functions implemented, add them to file_operations.
Dircet I/O is used under two conditions:
1. When mounting with "cache=none", CIFS uses direct I/O for all user file data transfer.
2. When opening a file with O_DIRECT, CIFS uses direct I/O for all data transfer on this file.
--- fs/cifs/cifsfs.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
fs/cifs/cifsfs.c +4 -6
@@ -1113,9 +1113,8 @@ const struct file_operations cifs_file_strict_ops = { };const struct file_operations cifs_file_direct_ops = { -/* BB reevaluate whether they can be done with directio, no cache */ - .read_iter = cifs_user_readv, - .write_iter = cifs_user_writev, + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, .open = cifs_open, .release = cifs_close, .lock = cifs_lock,
@@ -1169,9 +1168,8 @@ const struct file_operations cifs_file_strict_nobrl_ops = { };const struct file_operations cifs_file_direct_nobrl_ops = { -/* BB reevaluate whether they can be done with directio, no cache */ - .read_iter = cifs_user_readv, - .write_iter = cifs_user_writev, + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, .open = cifs_open, .release = cifs_close, .fsync = cifs_fsync,
Signature · 1 line
2.7.4
could you rebase these, patch 1 was merged quite a while ago, and patch 2 etc. doesn't apply cleanly
Quoted 59 lines · Long Li
From: Long Li <[email protected]>
This patch set implements direct I/O.
In normal code path (even with cache=none), CIFS copies I/O data from user-space to kernel-space for security reasons of possible protocol required signing and encryption on user data.
With this patch set, CIFS passes the I/O data directly from user-space buffer to the transport layer, when file system is mounted with "cache-none".
Patch v2 addressed comments from Christoph Hellwig <[email protected]> and Tom Talpey <[email protected]> to implement direct I/O for both socket and RDMA.
Patch v3 added support for kernel AIO.
Long Li (16):
CIFS: Add support for direct pages in rdata
CIFS: Use offset when reading pages
CIFS: Add support for direct pages in wdata
CIFS: pass page offset when issuing SMB write
CIFS: Calculate the correct request length based on page offset and
tail size
CIFS: Introduce helper function to get page offset and length in
smb_rqst
CIFS: When sending data on socket, pass the correct page offset
CIFS: SMBD: Support page offset in RDMA send
CIFS: SMBD: Support page offset in RDMA recv
CIFS: SMBD: Do not callib_dereg_mron invalidated memory registration
CIFS: SMBD: Support page offset in memory registration
CIFS: Pass page offset for calculating signature
CIFS: Pass page offset for encrypting
CIFS: Add support for direct I/O read
CIFS: Add support for direct I/O write
CIFS: Add direct I/O functions tofile_operationsfs/cifs/cifsencrypt.c | 9 +- fs/cifs/cifsfs.c | 10 +- fs/cifs/cifsfs.h | 2 + fs/cifs/cifsglob.h | 11 +- fs/cifs/cifsproto.h | 9 +- fs/cifs/cifssmb.c | 19 +- fs/cifs/connect.c | 5 +- fs/cifs/file.c | 477 ++++++++++++++++++++++++++++++++++++++++++-------- fs/cifs/misc.c | 17 ++ fs/cifs/smb2ops.c | 22 ++- fs/cifs/smb2pdu.c | 20 ++- fs/cifs/smbdirect.c | 156 ++++++++++------- fs/cifs/smbdirect.h | 2 +- fs/cifs/transport.c | 34 ++-- 14 files changed, 606 insertions(+), 187 deletions(-)--
2.7.4
Signature · 3 lines
Thanks, Steve
Quoted 10 lines
From: Steve French <[email protected]>
Sent: Saturday, September 15, 2018 2:28 AM
To: Long Li <[email protected]>
Cc: Steve French <[email protected]>; CIFS <[email protected]>; samba-technical <[email protected]>; LKML <linux- [email protected]>; [email protected]
Subject: Re: [Patch v3 00/16] CIFS: add support for direct I/Ocould you rebase these, patch 1 was merged quite a while ago, and patch 2 etc. doesn't apply cleanly
Sorry, I will rebase and resend.
On Fri, Sep 7, 2018 at 9:18 PM Long Li
Quoted 67 lines
Quoted 40 lines · <[email protected]> wrote:
From: Long Li <[email protected]>
This patch set implements direct I/O.
In normal code path (even with cache=none), CIFS copies I/O data from user-space to kernel-space for security reasons of possible protocol required signing and encryption on user data.
With this patch set, CIFS passes the I/O data directly from user-space buffer to the transport layer, when file system is mounted with "cache-none".
Patch v2 addressed comments from Christoph Hellwig <[email protected]> and Tom Talpey <[email protected]> to implement direct I/O for both socket and RDMA.
Patch v3 added support for kernel AIO.
Long Li (16):
CIFS: Add support for direct pages in rdata
CIFS: Use offset when reading pages
CIFS: Add support for direct pages in wdata
CIFS: pass page offset when issuing SMB write
CIFS: Calculate the correct request length based on page offset and
tail size
CIFS: Introduce helper function to get page offset and length in
smb_rqst
CIFS: When sending data on socket, pass the correct page offset
CIFS: SMBD: Support page offset in RDMA send
CIFS: SMBD: Support page offset in RDMA recv
CIFS: SMBD: Do not callib_dereg_mron invalidated memory registration
CIFS: SMBD: Support page offset in memory registration
CIFS: Pass page offset for calculating signature
CIFS: Pass page offset for encrypting
CIFS: Add support for direct I/O read
CIFS: Add support for direct I/O write
CIFS: Add direct I/O functions tofile_operationsQuoted 8 lines
fs/cifs/cifsencrypt.c | 9 +- fs/cifs/cifsfs.c | 10 +- fs/cifs/cifsfs.h | 2 + fs/cifs/cifsglob.h | 11 +- fs/cifs/cifsproto.h | 9 +- fs/cifs/cifssmb.c | 19 +- fs/cifs/connect.c | 5 +- fs/cifs/file.c | 477++++++++++++++++++++++++++++++++++++++++++--------
Quoted 11 lines
fs/cifs/misc.c | 17 ++ fs/cifs/smb2ops.c | 22 ++- fs/cifs/smb2pdu.c | 20 ++- fs/cifs/smbdirect.c | 156 ++++++++++------- fs/cifs/smbdirect.h | 2 +- fs/cifs/transport.c | 34 ++-- 14 files changed, 606 insertions(+), 187 deletions(-)--
2.7.4--
Thanks,Steve