From: Hou Tao <hout...@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 <hout...@huawei.com>
---
 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


Reply via email to