The patch reworks fuse_retrieve() to allocate only so many page pointers
as needed. The core part of the patch is the following calculation:

        num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;

(thanks Miklos for formula). All other changes are moslty shuffling lines.

The patch also contains one-line fix suggested by Miklos: the loop iterating
over pages from inode mapping should interpret 'offset' as offset in the very
first page only.
---
 fs/fuse/dev.c |   26 ++++++++++++++++----------
 1 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 6e38af5..cf69df3 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1562,13 +1562,24 @@ static int fuse_retrieve(struct fuse_conn *fc, struct 
inode *inode,
        unsigned int num;
        unsigned int offset;
        size_t total_len = 0;
+       int num_pages;
 
-       req = fuse_get_req_multipage(fc, FUSE_MAX_PAGES_PER_REQ);
+       offset = outarg->offset & ~PAGE_CACHE_MASK;
+       file_size = i_size_read(inode);
+
+       num = outarg->size;
+       if (outarg->offset > file_size)
+               num = 0;
+       else if (outarg->offset + num > file_size)
+               num = file_size - outarg->offset;
+
+       num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       num_pages = min(num_pages, FUSE_MAX_PAGES_PER_REQ);
+
+       req = fuse_get_req_multipage(fc, num_pages);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       offset = outarg->offset & ~PAGE_CACHE_MASK;
-
        req->in.h.opcode = FUSE_NOTIFY_REPLY;
        req->in.h.nodeid = outarg->nodeid;
        req->in.numargs = 2;
@@ -1577,14 +1588,8 @@ static int fuse_retrieve(struct fuse_conn *fc, struct 
inode *inode,
        req->end = fuse_retrieve_end;
 
        index = outarg->offset >> PAGE_CACHE_SHIFT;
-       file_size = i_size_read(inode);
-       num = outarg->size;
-       if (outarg->offset > file_size)
-               num = 0;
-       else if (outarg->offset + num > file_size)
-               num = file_size - outarg->offset;
 
-       while (num && req->num_pages < FUSE_MAX_PAGES_PER_REQ) {
+       while (num && req->num_pages < num_pages) {
                struct page *page;
                unsigned int this_num;
 
@@ -1596,6 +1601,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct 
inode *inode,
                req->pages[req->num_pages] = page;
                req->num_pages++;
 
+               offset = 0;
                num -= this_num;
                total_len += this_num;
                index++;

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to