Skip to contentSkip to conversation

CIFS: add support for direct I/O

PATCH Superseded Long Li 3 participants 19 messages Sep 8, 2018 – Sep 15, 2018

Subsystems (3)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Long Li

author
#0 lore raw

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
author
#1 lore raw

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
index 80a34ce..166e140 100644
@@ -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
index 23fd430..1c98293 100644
@@ -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
author
#2 lore raw

PATCH v3 2/16 CIFS: Use offset when reading pages

In reply to Long Li

From: 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
index dc80f84..1f27d8e 100644
@@ -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
index c8e4278..a1af258 100644
@@ -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
index 83b0234..8501da0 100644
@@ -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
index 7c0edd2..1fa1c29 100644
@@ -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
index 47d5331..6c22da8 100644
@@ -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
author
#3 lore raw

PATCH v3 3/16 CIFS: Add support for direct pages in wdata

In reply to Long Li

From: 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
index 166e140..7f62c98 100644
@@ -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
index 1f27d8e..7933c5f 100644
@@ -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
index a1af258..503e0ed 100644
@@ -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
author
#4 lore raw

PATCH v3 4/16 CIFS: pass page offset when issuing SMB write

In reply to Long Li

From: 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
index 503e0ed..0a57c61 100644
@@ -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
index 6c22da8..f603fbe 100644
@@ -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
author
#5 lore raw

PATCH v3 5/16 CIFS: Calculate the correct request length based on page offset and tail size

In reply to Long Li

From: 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
index 927226a..d6b5523 100644
@@ -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
author
#6 lore raw

PATCH v3 6/16 CIFS: Introduce helper function to get page offset and length in smb_rqst

In reply to Long Li

From: 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
index 7933c5f..89dda14 100644
@@ -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
index 96849b5..e951417 100644
@@ -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
author
#7 lore raw

PATCH v3 7/16 CIFS: When sending data on socket, pass the correct page offset

In reply to Long Li

From: 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
index d6b5523..5c96ee8 100644
@@ -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
author
#8 lore raw

PATCH v3 8/16 CIFS: SMBD: Support page offset in RDMA send

In reply to Long Li

From: 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
index c62f7c9..6141e3c 100644
@@ -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
author
#9 lore raw

PATCH v3 9/16 CIFS: SMBD: Support page offset in RDMA recv

In reply to Long Li

From: 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
index 6141e3c..ba53c52 100644
@@ -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
author
#10 lore raw

PATCH v3 10/16 CIFS: SMBD: Do not call ib_dereg_mr on invalidated memory registration

In reply to Long Li

From: 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
index ba53c52..b470cd0 100644
@@ -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
author
#11 lore raw

PATCH v3 11/16 CIFS: SMBD: Support page offset in memory registration

In reply to Long Li

From: 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
index f603fbe..fc30774 100644
@@ -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
index b470cd0..f61daa9 100644
@@ -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
index f9038da..1e419c2 100644
@@ -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
author
#12 lore raw

PATCH v3 12/16 CIFS: Pass page offset for calculating signature

In reply to Long Li

From: 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
index a6ef088..e88303c 100644
@@ -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
author
#13 lore raw

PATCH v3 13/16 CIFS: Pass page offset for encrypting

In reply to Long Li

From: 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
index 1fa1c29..38d19b6 100644
@@ -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
author
#14 lore raw

PATCH v3 14/16 CIFS: Add support for direct I/O read

In reply to Long Li

From: 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
index 5f02318..7fba9aa 100644
@@ -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
index 7f62c98..52248dd 100644
@@ -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
author
#15 lore raw

PATCH v3 15/16 CIFS: Add support for direct I/O write

In reply to Long Li

From: 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
index 7fba9aa..e9c5103 100644
@@ -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
author
#16 lore raw

PATCH v3 16/16 CIFS: Add direct I/O functions to file_operations

In reply to Long Li

From: 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
index 62f1662..f18091b 100644
@@ -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
#17 lore raw
In reply to Long Li

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 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(-)

--
2.7.4

Signature · 3 lines
Thanks,

Steve
#18 lore raw
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/O

could 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 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

Quoted 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