]> Git Repo - linux.git/blobdiff - fs/afs/file.c
net: ethernet: aquantia: Fixes for aq_ndev_change_mtu
[linux.git] / fs / afs / file.c
index 6344aee4ac4bff8e768fc7c344ec7ccea3670b98..ba7b71fba34bcc4cd5f8b8a305ace06a388ac607 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pagemap.h>
 #include <linux/writeback.h>
 #include <linux/gfp.h>
+#include <linux/task_io_accounting_ops.h>
 #include "internal.h"
 
 static int afs_readpage(struct file *file, struct page *page);
@@ -101,6 +102,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 +142,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 +179,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 +227,8 @@ int afs_page_filler(void *data, struct page *page)
        _leave(" = 0");
        return 0;
 
+enomem:
+       ret = -ENOMEM;
 error:
        SetPageError(page);
        unlock_page(page);
@@ -234,6 +262,131 @@ static int afs_readpage(struct file *file, struct page *page)
        return ret;
 }
 
+/*
+ * Make pages available as they're filled.
+ */
+static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req)
+{
+#ifdef CONFIG_AFS_FSCACHE
+       struct afs_vnode *vnode = call->reply;
+#endif
+       struct page *page = req->pages[req->index];
+
+       req->pages[req->index] = NULL;
+       SetPageUptodate(page);
+
+       /* send the page to the cache */
+#ifdef CONFIG_AFS_FSCACHE
+       if (PageFsCache(page) &&
+           fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) {
+               fscache_uncache_page(vnode->cache, page);
+               BUG_ON(PageFsCache(page));
+       }
+#endif
+       unlock_page(page);
+       put_page(page);
+}
+
+/*
+ * Read a contiguous set of pages.
+ */
+static int afs_readpages_one(struct file *file, struct address_space *mapping,
+                            struct list_head *pages)
+{
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
+       struct afs_read *req;
+       struct list_head *p;
+       struct page *first, *page;
+       struct key *key = file->private_data;
+       pgoff_t index;
+       int ret, n, i;
+
+       /* Count the number of contiguous pages at the front of the list.  Note
+        * that the list goes prev-wards rather than next-wards.
+        */
+       first = list_entry(pages->prev, struct page, lru);
+       index = first->index + 1;
+       n = 1;
+       for (p = first->lru.prev; p != pages; p = p->prev) {
+               page = list_entry(p, struct page, lru);
+               if (page->index != index)
+                       break;
+               index++;
+               n++;
+       }
+
+       req = kzalloc(sizeof(struct afs_read) + sizeof(struct page *) * n,
+                     GFP_NOFS);
+       if (!req)
+               return -ENOMEM;
+
+       atomic_set(&req->usage, 1);
+       req->page_done = afs_readpages_page_done;
+       req->pos = first->index;
+       req->pos <<= PAGE_SHIFT;
+
+       /* Transfer the pages to the request.  We add them in until one fails
+        * to add to the LRU and then we stop (as that'll make a hole in the
+        * contiguous run.
+        *
+        * Note that it's possible for the file size to change whilst we're
+        * doing this, but we rely on the server returning less than we asked
+        * for if the file shrank.  We also rely on this to deal with a partial
+        * page at the end of the file.
+        */
+       do {
+               page = list_entry(pages->prev, struct page, lru);
+               list_del(&page->lru);
+               index = page->index;
+               if (add_to_page_cache_lru(page, mapping, index,
+                                         readahead_gfp_mask(mapping))) {
+#ifdef CONFIG_AFS_FSCACHE
+                       fscache_uncache_page(vnode->cache, page);
+#endif
+                       put_page(page);
+                       break;
+               }
+
+               req->pages[req->nr_pages++] = page;
+               req->len += PAGE_SIZE;
+       } while (req->nr_pages < n);
+
+       if (req->nr_pages == 0) {
+               kfree(req);
+               return 0;
+       }
+
+       ret = afs_vnode_fetch_data(vnode, key, req);
+       if (ret < 0)
+               goto error;
+
+       task_io_account_read(PAGE_SIZE * req->nr_pages);
+       afs_put_read(req);
+       return 0;
+
+error:
+       if (ret == -ENOENT) {
+               _debug("got NOENT from server"
+                      " - marking file deleted and stale");
+               set_bit(AFS_VNODE_DELETED, &vnode->flags);
+               ret = -ESTALE;
+       }
+
+       for (i = 0; i < req->nr_pages; i++) {
+               page = req->pages[i];
+               if (page) {
+#ifdef CONFIG_AFS_FSCACHE
+                       fscache_uncache_page(vnode->cache, page);
+#endif
+                       SetPageError(page);
+                       unlock_page(page);
+               }
+       }
+
+       afs_put_read(req);
+       return ret;
+}
+
 /*
  * read a set of pages
  */
@@ -287,8 +440,11 @@ static int afs_readpages(struct file *file, struct address_space *mapping,
                return ret;
        }
 
-       /* load the missing pages from the network */
-       ret = read_cache_pages(mapping, pages, afs_page_filler, key);
+       while (!list_empty(pages)) {
+               ret = afs_readpages_one(file, mapping, pages);
+               if (ret < 0)
+                       break;
+       }
 
        _leave(" = %d [netting]", ret);
        return ret;
This page took 0.043658 seconds and 4 git commands to generate.