diff mbox series

[v2,5/6] virtiofs: use scattered bounce buffer for ITER_KVEC dio

Message ID 20240228144126.2864064-6-houtao@huaweicloud.com (mailing list archive)
State New
Headers show
Series virtiofs: fix the warning for ITER_KVEC dio | expand

Commit Message

Hou Tao Feb. 28, 2024, 2:41 p.m. UTC
From: Hou Tao <houtao1@huawei.com>

To prevent unnecessary request for large contiguous physical memory
chunk, use bounce buffer backed by scattered pages for ITER_KVEC
direct-io read/write when the total size of its args is greater than
PAGE_SIZE.

Signed-off-by: Hou Tao <houtao1@huawei.com>
---
 fs/fuse/virtio_fs.c | 78 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 19 deletions(-)
diff mbox series

Patch

diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index ffea684bd100d..34b9370beba6d 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -458,20 +458,15 @@  static void virtio_fs_argbuf_free(struct virtio_fs_argbuf *argbuf)
 	kfree(argbuf);
 }
 
-static struct virtio_fs_argbuf *virtio_fs_argbuf_new(struct fuse_args *args,
+static struct virtio_fs_argbuf *virtio_fs_argbuf_new(unsigned int in_len,
+						     unsigned int out_len,
 						     gfp_t gfp, bool is_flat)
 {
 	struct virtio_fs_argbuf *argbuf;
-	unsigned int numargs;
-	unsigned int in_len, out_len, len;
+	unsigned int len;
 	unsigned int i, nr;
 
-	numargs = args->in_numargs - args->in_pages;
-	in_len = fuse_len_args(numargs, (struct fuse_arg *) args->in_args);
-	numargs = args->out_numargs - args->out_pages;
-	out_len = fuse_len_args(numargs, args->out_args);
 	len = virtio_fs_argbuf_len(in_len, out_len, is_flat);
-
 	if (is_flat) {
 		argbuf = kmalloc(struct_size(argbuf, f.buf, len), gfp);
 		if (argbuf)
@@ -1222,14 +1217,17 @@  static unsigned int sg_count_fuse_pages(struct fuse_page_desc *page_descs,
 }
 
 /* Return the number of scatter-gather list elements required */
-static unsigned int sg_count_fuse_req(struct fuse_req *req)
+static unsigned int sg_count_fuse_req(struct fuse_req *req,
+				      unsigned int in_args_len,
+				      unsigned int out_args_len,
+				      bool flat_argbuf)
 {
 	struct fuse_args *args = req->args;
 	struct fuse_args_pages *ap = container_of(args, typeof(*ap), args);
 	unsigned int size, total_sgs = 1 /* fuse_in_header */;
+	unsigned int num_in, num_out;
 
-	if (args->in_numargs - args->in_pages)
-		total_sgs += 1;
+	num_in = args->in_numargs - args->in_pages;
 
 	if (args->in_pages) {
 		size = args->in_args[args->in_numargs - 1].size;
@@ -1237,20 +1235,25 @@  static unsigned int sg_count_fuse_req(struct fuse_req *req)
 						 size);
 	}
 
-	if (!test_bit(FR_ISREPLY, &req->flags))
-		return total_sgs;
+	if (!test_bit(FR_ISREPLY, &req->flags)) {
+		num_out = 0;
+		goto done;
+	}
 
 	total_sgs += 1 /* fuse_out_header */;
-
-	if (args->out_numargs - args->out_pages)
-		total_sgs += 1;
+	num_out = args->out_numargs - args->out_pages;
 
 	if (args->out_pages) {
 		size = args->out_args[args->out_numargs - 1].size;
 		total_sgs += sg_count_fuse_pages(ap->descs, ap->num_pages,
 						 size);
 	}
-
+done:
+	if (flat_argbuf)
+		total_sgs += !!num_in + !!num_out;
+	else
+		total_sgs += virtio_fs_argbuf_len(in_args_len, out_args_len,
+						  false) >> PAGE_SHIFT;
 	return total_sgs;
 }
 
@@ -1302,6 +1305,31 @@  static unsigned int sg_init_fuse_args(struct scatterlist *sg,
 	return total_sgs;
 }
 
+static bool use_scattered_argbuf(struct fuse_req *req)
+{
+	struct fuse_args *args = req->args;
+
+	/*
+	 * To prevent unnecessary request for contiguous physical memory chunk,
+	 * use argbuf backed by scattered pages for ITER_KVEC direct-io
+	 * read/write when the total size of its args is greater than PAGE_SIZE.
+	 */
+	if ((req->in.h.opcode == FUSE_WRITE && !args->in_pages) ||
+	    (req->in.h.opcode == FUSE_READ && !args->out_pages)) {
+		unsigned int numargs;
+		unsigned int len;
+
+		numargs = args->in_numargs - args->in_pages;
+		len = fuse_len_args(numargs, (struct fuse_arg *)args->in_args);
+		numargs = args->out_numargs - args->out_pages;
+		len += fuse_len_args(numargs, args->out_args);
+		if (len > PAGE_SIZE)
+			return true;
+	}
+
+	return false;
+}
+
 /* Add a request to a virtqueue and kick the device */
 static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
 				 struct fuse_req *req, bool in_flight)
@@ -1317,13 +1345,24 @@  static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
 	unsigned int out_sgs = 0;
 	unsigned int in_sgs = 0;
 	unsigned int total_sgs;
+	unsigned int numargs;
+	unsigned int in_args_len;
+	unsigned int out_args_len;
 	unsigned int i;
 	int ret;
 	bool notify;
+	bool flat_argbuf;
 	struct fuse_pqueue *fpq;
 
+	flat_argbuf = !use_scattered_argbuf(req);
+	numargs = args->in_numargs - args->in_pages;
+	in_args_len = fuse_len_args(numargs, (struct fuse_arg *) args->in_args);
+	numargs = args->out_numargs - args->out_pages;
+	out_args_len = fuse_len_args(numargs, args->out_args);
+
 	/* Does the sglist fit on the stack? */
-	total_sgs = sg_count_fuse_req(req);
+	total_sgs = sg_count_fuse_req(req, in_args_len, out_args_len,
+				      flat_argbuf);
 	if (total_sgs > ARRAY_SIZE(stack_sgs)) {
 		sgs = kmalloc_array(total_sgs, sizeof(sgs[0]), GFP_ATOMIC);
 		sg = kmalloc_array(total_sgs, sizeof(sg[0]), GFP_ATOMIC);
@@ -1334,7 +1373,8 @@  static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
 	}
 
 	/* Use a bounce buffer since stack args cannot be mapped */
-	req->argbuf = virtio_fs_argbuf_new(args, GFP_ATOMIC, true);
+	req->argbuf = virtio_fs_argbuf_new(in_args_len, out_args_len,
+					   GFP_ATOMIC, flat_argbuf);
 	if (!req->argbuf) {
 		ret = -ENOMEM;
 		goto out;