afs: Make afs_fs_fetch_data() take a list of pages
authorDavid Howells <dhowells@redhat.com>
Thu, 5 Jan 2017 10:38:34 +0000 (10:38 +0000)
committerDavid Howells <dhowells@redhat.com>
Fri, 6 Jan 2017 16:54:41 +0000 (16:54 +0000)
Make afs_fs_fetch_data() take a list of pages for bulk data transfer.  This
will allow afs_readpages() to be made more efficient.

Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/file.c
fs/afs/fsclient.c
fs/afs/internal.h
fs/afs/vnode.c
fs/afs/write.c

index 6344aee4ac4bff8e768fc7c344ec7ccea3670b98..6c262ceef32dd182b676fa336be79cae899d7f44 100644 (file)
@@ -101,6 +101,21 @@ int afs_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+/*
+ * Dispose of a ref to a read record.
+ */
+void afs_put_read(struct afs_read *req)
+{
+       int i;
+
+       if (atomic_dec_and_test(&req->usage)) {
+               for (i = 0; i < req->nr_pages; i++)
+                       if (req->pages[i])
+                               put_page(req->pages[i]);
+               kfree(req);
+       }
+}
+
 #ifdef CONFIG_AFS_FSCACHE
 /*
  * deal with notification that a page was read from the cache
@@ -126,9 +141,8 @@ int afs_page_filler(void *data, struct page *page)
 {
        struct inode *inode = page->mapping->host;
        struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct afs_read *req;
        struct key *key = data;
-       size_t len;
-       off_t offset;
        int ret;
 
        _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
@@ -164,12 +178,23 @@ int afs_page_filler(void *data, struct page *page)
                _debug("cache said ENOBUFS");
        default:
        go_on:
-               offset = page->index << PAGE_SHIFT;
-               len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
+               req = kzalloc(sizeof(struct afs_read) + sizeof(struct page *),
+                             GFP_KERNEL);
+               if (!req)
+                       goto enomem;
+
+               atomic_set(&req->usage, 1);
+               req->pos = (loff_t)page->index << PAGE_SHIFT;
+               req->len = min_t(size_t, i_size_read(inode) - req->pos,
+                                PAGE_SIZE);
+               req->nr_pages = 1;
+               req->pages[0] = page;
+               get_page(page);
 
                /* read the contents of the file from the server into the
                 * page */
-               ret = afs_vnode_fetch_data(vnode, key, offset, len, page);
+               ret = afs_vnode_fetch_data(vnode, key, req);
+               afs_put_read(req);
                if (ret < 0) {
                        if (ret == -ENOENT) {
                                _debug("got NOENT from server"
@@ -201,6 +226,8 @@ int afs_page_filler(void *data, struct page *page)
        _leave(" = 0");
        return 0;
 
+enomem:
+       ret = -ENOMEM;
 error:
        SetPageError(page);
        unlock_page(page);
index 31c616ab9b400a66dfbcd39c12dd87665f88acf9..7dc1f6fb36617e3b72a5de238fcf93d562e323f4 100644 (file)
@@ -309,15 +309,19 @@ int afs_fs_fetch_file_status(struct afs_server *server,
 static int afs_deliver_fs_fetch_data(struct afs_call *call)
 {
        struct afs_vnode *vnode = call->reply;
+       struct afs_read *req = call->reply3;
        const __be32 *bp;
-       struct page *page;
+       unsigned int size;
        void *buffer;
        int ret;
 
-       _enter("{%u}", call->unmarshall);
+       _enter("{%u,%zu/%u;%u/%llu}",
+              call->unmarshall, call->offset, call->count,
+              req->remain, req->actual_len);
 
        switch (call->unmarshall) {
        case 0:
+               req->actual_len = 0;
                call->offset = 0;
                call->unmarshall++;
                if (call->operation_ID != FSFETCHDATA64) {
@@ -334,10 +338,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
                if (ret < 0)
                        return ret;
 
-               call->count = ntohl(call->tmp);
-               _debug("DATA length MSW: %u", call->count);
-               if (call->count > 0)
-                       return -EBADMSG;
+               req->actual_len = ntohl(call->tmp);
+               req->actual_len <<= 32;
                call->offset = 0;
                call->unmarshall++;
 
@@ -349,26 +351,52 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
                if (ret < 0)
                        return ret;
 
-               call->count = ntohl(call->tmp);
-               _debug("DATA length: %u", call->count);
-               if (call->count > PAGE_SIZE)
+               req->actual_len |= ntohl(call->tmp);
+               _debug("DATA length: %llu", req->actual_len);
+               /* Check that the server didn't want to send us extra.  We
+                * might want to just discard instead, but that requires
+                * cooperation from AF_RXRPC.
+                */
+               if (req->actual_len > req->len)
                        return -EBADMSG;
-               call->offset = 0;
+
+               req->remain = req->actual_len;
+               call->offset = req->pos & (PAGE_SIZE - 1);
+               req->index = 0;
+               if (req->actual_len == 0)
+                       goto no_more_data;
                call->unmarshall++;
 
+       begin_page:
+               if (req->remain > PAGE_SIZE - call->offset)
+                       size = PAGE_SIZE - call->offset;
+               else
+                       size = req->remain;
+               call->count = call->offset + size;
+               ASSERTCMP(call->count, <=, PAGE_SIZE);
+               req->remain -= size;
+
                /* extract the returned data */
        case 3:
-               _debug("extract data");
-               if (call->count > 0) {
-                       page = call->reply3;
-                       buffer = kmap(page);
-                       ret = afs_extract_data(call, buffer,
-                                              call->count, true);
-                       kunmap(page);
-                       if (ret < 0)
-                               return ret;
+               _debug("extract data %u/%llu %zu/%u",
+                      req->remain, req->actual_len, call->offset, call->count);
+
+               buffer = kmap(req->pages[req->index]);
+               ret = afs_extract_data(call, buffer, call->count, true);
+               kunmap(req->pages[req->index]);
+               if (ret < 0)
+                       return ret;
+               if (call->offset == PAGE_SIZE) {
+                       if (req->page_done)
+                               req->page_done(call, req);
+                       if (req->remain > 0) {
+                               req->index++;
+                               call->offset = 0;
+                               goto begin_page;
+                       }
                }
 
+       no_more_data:
                call->offset = 0;
                call->unmarshall++;
 
@@ -393,17 +421,25 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
        }
 
        if (call->count < PAGE_SIZE) {
-               _debug("clear");
-               page = call->reply3;
-               buffer = kmap(page);
+               buffer = kmap(req->pages[req->index]);
                memset(buffer + call->count, 0, PAGE_SIZE - call->count);
-               kunmap(page);
+               kunmap(req->pages[req->index]);
+               if (req->page_done)
+                       req->page_done(call, req);
        }
 
        _leave(" = 0 [done]");
        return 0;
 }
 
+static void afs_fetch_data_destructor(struct afs_call *call)
+{
+       struct afs_read *req = call->reply3;
+
+       afs_put_read(req);
+       afs_flat_call_destructor(call);
+}
+
 /*
  * FS.FetchData operation type
  */
@@ -411,14 +447,14 @@ static const struct afs_call_type afs_RXFSFetchData = {
        .name           = "FS.FetchData",
        .deliver        = afs_deliver_fs_fetch_data,
        .abort_to_error = afs_abort_to_error,
-       .destructor     = afs_flat_call_destructor,
+       .destructor     = afs_fetch_data_destructor,
 };
 
 static const struct afs_call_type afs_RXFSFetchData64 = {
        .name           = "FS.FetchData64",
        .deliver        = afs_deliver_fs_fetch_data,
        .abort_to_error = afs_abort_to_error,
-       .destructor     = afs_flat_call_destructor,
+       .destructor     = afs_fetch_data_destructor,
 };
 
 /*
@@ -427,8 +463,7 @@ static const struct afs_call_type afs_RXFSFetchData64 = {
 static int afs_fs_fetch_data64(struct afs_server *server,
                               struct key *key,
                               struct afs_vnode *vnode,
-                              off_t offset, size_t length,
-                              struct page *buffer,
+                              struct afs_read *req,
                               const struct afs_wait_mode *wait_mode)
 {
        struct afs_call *call;
@@ -436,8 +471,6 @@ static int afs_fs_fetch_data64(struct afs_server *server,
 
        _enter("");
 
-       ASSERTCMP(length, <, ULONG_MAX);
-
        call = afs_alloc_flat_call(&afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
        if (!call)
                return -ENOMEM;
@@ -445,7 +478,7 @@ static int afs_fs_fetch_data64(struct afs_server *server,
        call->key = key;
        call->reply = vnode;
        call->reply2 = NULL; /* volsync */
-       call->reply3 = buffer;
+       call->reply3 = req;
        call->service_id = FS_SERVICE;
        call->port = htons(AFS_FS_PORT);
        call->operation_ID = FSFETCHDATA64;
@@ -456,11 +489,12 @@ static int afs_fs_fetch_data64(struct afs_server *server,
        bp[1] = htonl(vnode->fid.vid);
        bp[2] = htonl(vnode->fid.vnode);
        bp[3] = htonl(vnode->fid.unique);
-       bp[4] = htonl(upper_32_bits(offset));
-       bp[5] = htonl((u32) offset);
+       bp[4] = htonl(upper_32_bits(req->pos));
+       bp[5] = htonl(lower_32_bits(req->pos));
        bp[6] = 0;
-       bp[7] = htonl((u32) length);
+       bp[7] = htonl(lower_32_bits(req->len));
 
+       atomic_inc(&req->usage);
        return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
 }
 
@@ -470,16 +504,16 @@ static int afs_fs_fetch_data64(struct afs_server *server,
 int afs_fs_fetch_data(struct afs_server *server,
                      struct key *key,
                      struct afs_vnode *vnode,
-                     off_t offset, size_t length,
-                     struct page *buffer,
+                     struct afs_read *req,
                      const struct afs_wait_mode *wait_mode)
 {
        struct afs_call *call;
        __be32 *bp;
 
-       if (upper_32_bits(offset) || upper_32_bits(offset + length))
-               return afs_fs_fetch_data64(server, key, vnode, offset, length,
-                                          buffer, wait_mode);
+       if (upper_32_bits(req->pos) ||
+           upper_32_bits(req->len) ||
+           upper_32_bits(req->pos + req->len))
+               return afs_fs_fetch_data64(server, key, vnode, req, wait_mode);
 
        _enter("");
 
@@ -490,7 +524,7 @@ int afs_fs_fetch_data(struct afs_server *server,
        call->key = key;
        call->reply = vnode;
        call->reply2 = NULL; /* volsync */
-       call->reply3 = buffer;
+       call->reply3 = req;
        call->service_id = FS_SERVICE;
        call->port = htons(AFS_FS_PORT);
        call->operation_ID = FSFETCHDATA;
@@ -501,9 +535,10 @@ int afs_fs_fetch_data(struct afs_server *server,
        bp[1] = htonl(vnode->fid.vid);
        bp[2] = htonl(vnode->fid.vnode);
        bp[3] = htonl(vnode->fid.unique);
-       bp[4] = htonl(offset);
-       bp[5] = htonl(length);
+       bp[4] = htonl(lower_32_bits(req->pos));
+       bp[5] = htonl(lower_32_bits(req->len));
 
+       atomic_inc(&req->usage);
        return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
 }
 
index 535a38d2c1d06f752cd6beae54766c1a9d899527..6f7a9638ba1a40641c6810564c96fc7778e98415 100644 (file)
@@ -133,6 +133,22 @@ struct afs_call_type {
        void (*destructor)(struct afs_call *call);
 };
 
+/*
+ * Record of an outstanding read operation on a vnode.
+ */
+struct afs_read {
+       loff_t                  pos;            /* Where to start reading */
+       loff_t                  len;            /* How much to read */
+       loff_t                  actual_len;     /* How much we're actually getting */
+       atomic_t                usage;
+       unsigned int            remain;         /* Amount remaining */
+       unsigned int            index;          /* Which page we're reading into */
+       unsigned int            pg_offset;      /* Offset in page we're at */
+       unsigned int            nr_pages;
+       void (*page_done)(struct afs_call *, struct afs_read *);
+       struct page             *pages[];
+};
+
 /*
  * record of an outstanding writeback on a vnode
  */
@@ -494,6 +510,7 @@ extern const struct file_operations afs_file_operations;
 extern int afs_open(struct inode *, struct file *);
 extern int afs_release(struct inode *, struct file *);
 extern int afs_page_filler(void *, struct page *);
+extern void afs_put_read(struct afs_read *);
 
 /*
  * flock.c
@@ -513,7 +530,7 @@ extern int afs_fs_fetch_file_status(struct afs_server *, struct key *,
 extern int afs_fs_give_up_callbacks(struct afs_server *,
                                    const struct afs_wait_mode *);
 extern int afs_fs_fetch_data(struct afs_server *, struct key *,
-                            struct afs_vnode *, off_t, size_t, struct page *,
+                            struct afs_vnode *, struct afs_read *,
                             const struct afs_wait_mode *);
 extern int afs_fs_create(struct afs_server *, struct key *,
                         struct afs_vnode *, const char *, umode_t,
@@ -699,7 +716,7 @@ extern void afs_vnode_finalise_status_update(struct afs_vnode *,
 extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *,
                                  struct key *);
 extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *,
-                               off_t, size_t, struct page *);
+                               struct afs_read *);
 extern int afs_vnode_create(struct afs_vnode *, struct key *, const char *,
                            umode_t, struct afs_fid *, struct afs_file_status *,
                            struct afs_callback *, struct afs_server **);
index 25cf4c3f4ff7ded56a2b0f0183b00584668104c7..45aa874f5d32e5e48a21b6cca547bef053a411eb 100644 (file)
@@ -393,7 +393,7 @@ no_server:
  * - TODO implement caching
  */
 int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
-                        off_t offset, size_t length, struct page *page)
+                        struct afs_read *desc)
 {
        struct afs_server *server;
        int ret;
@@ -420,8 +420,8 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
 
                _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 
-               ret = afs_fs_fetch_data(server, key, vnode, offset, length,
-                                       page, &afs_sync_call);
+               ret = afs_fs_fetch_data(server, key, vnode, desc,
+                                       &afs_sync_call);
 
        } while (!afs_volume_release_fileserver(vnode, server, ret));
 
index f865c3f05bea5074a91a547e4682523e8aab88be..c83c1a0e851fb34051c026bcea8e2a561299cf95 100644 (file)
@@ -86,19 +86,30 @@ void afs_put_writeback(struct afs_writeback *wb)
 static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
                         loff_t pos, struct page *page)
 {
+       struct afs_read *req;
        loff_t i_size;
        int ret;
-       int len;
 
        _enter(",,%llu", (unsigned long long)pos);
 
+       req = kzalloc(sizeof(struct afs_read) + sizeof(struct page *),
+                     GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       atomic_set(&req->usage, 1);
+       req->pos = pos;
+       req->nr_pages = 1;
+       req->pages[0] = page;
+
        i_size = i_size_read(&vnode->vfs_inode);
        if (pos + PAGE_SIZE > i_size)
-               len = i_size - pos;
+               req->len = i_size - pos;
        else
-               len = PAGE_SIZE;
+               req->len = PAGE_SIZE;
 
-       ret = afs_vnode_fetch_data(vnode, key, pos, len, page);
+       ret = afs_vnode_fetch_data(vnode, key, req);
+       afs_put_read(req);
        if (ret < 0) {
                if (ret == -ENOENT) {
                        _debug("got NOENT from server"