2024-02-28 14:42:33

by Hou Tao

[permalink] [raw]
Subject: [PATCH v2 5/6] virtiofs: use scattered bounce buffer for ITER_KVEC dio

From: Hou Tao <[email protected]>

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 <[email protected]>
---
fs/fuse/virtio_fs.c | 78 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 59 insertions(+), 19 deletions(-)

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;
--
2.29.2