Merge tag 'afs-next-20171113' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowel...
[sfrench/cifs-2.6.git] / fs / afs / write.c
index 106e43db11153fc1faed12ab326e25d4a9dc3c7d..18e46e31523ccc0295a35c058baa88d0ebf960ed 100644 (file)
@@ -8,6 +8,7 @@
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  */
+
 #include <linux/backing-dev.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
@@ -16,9 +17,6 @@
 #include <linux/pagevec.h>
 #include "internal.h"
 
-static int afs_write_back_from_locked_page(struct afs_writeback *wb,
-                                          struct page *page);
-
 /*
  * mark a page as having been made dirty and thus needing writeback
  */
@@ -28,58 +26,6 @@ int afs_set_page_dirty(struct page *page)
        return __set_page_dirty_nobuffers(page);
 }
 
-/*
- * unlink a writeback record because its usage has reached zero
- * - must be called with the wb->vnode->writeback_lock held
- */
-static void afs_unlink_writeback(struct afs_writeback *wb)
-{
-       struct afs_writeback *front;
-       struct afs_vnode *vnode = wb->vnode;
-
-       list_del_init(&wb->link);
-       if (!list_empty(&vnode->writebacks)) {
-               /* if an fsync rises to the front of the queue then wake it
-                * up */
-               front = list_entry(vnode->writebacks.next,
-                                  struct afs_writeback, link);
-               if (front->state == AFS_WBACK_SYNCING) {
-                       _debug("wake up sync");
-                       front->state = AFS_WBACK_COMPLETE;
-                       wake_up(&front->waitq);
-               }
-       }
-}
-
-/*
- * free a writeback record
- */
-static void afs_free_writeback(struct afs_writeback *wb)
-{
-       _enter("");
-       key_put(wb->key);
-       kfree(wb);
-}
-
-/*
- * dispose of a reference to a writeback record
- */
-void afs_put_writeback(struct afs_writeback *wb)
-{
-       struct afs_vnode *vnode = wb->vnode;
-
-       _enter("{%d}", wb->usage);
-
-       spin_lock(&vnode->writeback_lock);
-       if (--wb->usage == 0)
-               afs_unlink_writeback(wb);
-       else
-               wb = NULL;
-       spin_unlock(&vnode->writeback_lock);
-       if (wb)
-               afs_free_writeback(wb);
-}
-
 /*
  * partly or wholly fill a page that's under preparation for writing
  */
@@ -103,7 +49,7 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
        req->pages[0] = page;
        get_page(page);
 
-       ret = afs_vnode_fetch_data(vnode, key, req);
+       ret = afs_fetch_data(vnode, key, req);
        afs_put_read(req);
        if (ret < 0) {
                if (ret == -ENOENT) {
@@ -125,42 +71,32 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
                    loff_t pos, unsigned len, unsigned flags,
                    struct page **pagep, void **fsdata)
 {
-       struct afs_writeback *candidate, *wb;
        struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
        struct page *page;
-       struct key *key = file->private_data;
-       unsigned from = pos & (PAGE_SIZE - 1);
-       unsigned to = from + len;
+       struct key *key = afs_file_key(file);
+       unsigned long priv;
+       unsigned f, from = pos & (PAGE_SIZE - 1);
+       unsigned t, to = from + len;
        pgoff_t index = pos >> PAGE_SHIFT;
        int ret;
 
        _enter("{%x:%u},{%lx},%u,%u",
               vnode->fid.vid, vnode->fid.vnode, index, from, to);
 
-       candidate = kzalloc(sizeof(*candidate), GFP_KERNEL);
-       if (!candidate)
-               return -ENOMEM;
-       candidate->vnode = vnode;
-       candidate->first = candidate->last = index;
-       candidate->offset_first = from;
-       candidate->to_last = to;
-       INIT_LIST_HEAD(&candidate->link);
-       candidate->usage = 1;
-       candidate->state = AFS_WBACK_PENDING;
-       init_waitqueue_head(&candidate->waitq);
+       /* We want to store information about how much of a page is altered in
+        * page->private.
+        */
+       BUILD_BUG_ON(PAGE_SIZE > 32768 && sizeof(page->private) < 8);
 
        page = grab_cache_page_write_begin(mapping, index, flags);
-       if (!page) {
-               kfree(candidate);
+       if (!page)
                return -ENOMEM;
-       }
 
        if (!PageUptodate(page) && len != PAGE_SIZE) {
                ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);
                if (ret < 0) {
                        unlock_page(page);
                        put_page(page);
-                       kfree(candidate);
                        _leave(" = %d [prep]", ret);
                        return ret;
                }
@@ -171,79 +107,54 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
        *pagep = page;
 
 try_again:
-       spin_lock(&vnode->writeback_lock);
-
-       /* see if this page is already pending a writeback under a suitable key
-        * - if so we can just join onto that one */
-       wb = (struct afs_writeback *) page_private(page);
-       if (wb) {
-               if (wb->key == key && wb->state == AFS_WBACK_PENDING)
-                       goto subsume_in_current_wb;
-               goto flush_conflicting_wb;
+       /* See if this page is already partially written in a way that we can
+        * merge the new write with.
+        */
+       t = f = 0;
+       if (PagePrivate(page)) {
+               priv = page_private(page);
+               f = priv & AFS_PRIV_MAX;
+               t = priv >> AFS_PRIV_SHIFT;
+               ASSERTCMP(f, <=, t);
        }
 
-       if (index > 0) {
-               /* see if we can find an already pending writeback that we can
-                * append this page to */
-               list_for_each_entry(wb, &vnode->writebacks, link) {
-                       if (wb->last == index - 1 && wb->key == key &&
-                           wb->state == AFS_WBACK_PENDING)
-                               goto append_to_previous_wb;
-               }
+       if (f != t) {
+               if (to < f || from > t)
+                       goto flush_conflicting_write;
+               if (from < f)
+                       f = from;
+               if (to > t)
+                       t = to;
+       } else {
+               f = from;
+               t = to;
        }
 
-       list_add_tail(&candidate->link, &vnode->writebacks);
-       candidate->key = key_get(key);
-       spin_unlock(&vnode->writeback_lock);
-       SetPagePrivate(page);
-       set_page_private(page, (unsigned long) candidate);
-       _leave(" = 0 [new]");
-       return 0;
-
-subsume_in_current_wb:
-       _debug("subsume");
-       ASSERTRANGE(wb->first, <=, index, <=, wb->last);
-       if (index == wb->first && from < wb->offset_first)
-               wb->offset_first = from;
-       if (index == wb->last && to > wb->to_last)
-               wb->to_last = to;
-       spin_unlock(&vnode->writeback_lock);
-       kfree(candidate);
-       _leave(" = 0 [sub]");
-       return 0;
-
-append_to_previous_wb:
-       _debug("append into %lx-%lx", wb->first, wb->last);
-       wb->usage++;
-       wb->last++;
-       wb->to_last = to;
-       spin_unlock(&vnode->writeback_lock);
+       priv = (unsigned long)t << AFS_PRIV_SHIFT;
+       priv |= f;
+       trace_afs_page_dirty(vnode, tracepoint_string("begin"),
+                            page->index, priv);
        SetPagePrivate(page);
-       set_page_private(page, (unsigned long) wb);
-       kfree(candidate);
-       _leave(" = 0 [app]");
+       set_page_private(page, priv);
+       _leave(" = 0");
        return 0;
 
-       /* the page is currently bound to another context, so if it's dirty we
-        * need to flush it before we can use the new context */
-flush_conflicting_wb:
+       /* The previous write and this write aren't adjacent or overlapping, so
+        * flush the page out.
+        */
+flush_conflicting_write:
        _debug("flush conflict");
-       if (wb->state == AFS_WBACK_PENDING)
-               wb->state = AFS_WBACK_CONFLICTING;
-       spin_unlock(&vnode->writeback_lock);
-       if (clear_page_dirty_for_io(page)) {
-               ret = afs_write_back_from_locked_page(wb, page);
-               if (ret < 0) {
-                       afs_put_writeback(candidate);
-                       _leave(" = %d", ret);
-                       return ret;
-               }
+       ret = write_one_page(page);
+       if (ret < 0) {
+               _leave(" = %d", ret);
+               return ret;
        }
 
-       /* the page holds a ref on the writeback record */
-       afs_put_writeback(wb);
-       set_page_private(page, 0);
-       ClearPagePrivate(page);
+       ret = lock_page_killable(page);
+       if (ret < 0) {
+               _leave(" = %d", ret);
+               return ret;
+       }
        goto try_again;
 }
 
@@ -255,7 +166,7 @@ int afs_write_end(struct file *file, struct address_space *mapping,
                  struct page *page, void *fsdata)
 {
        struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        loff_t i_size, maybe_i_size;
        int ret;
 
@@ -266,11 +177,11 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 
        i_size = i_size_read(&vnode->vfs_inode);
        if (maybe_i_size > i_size) {
-               spin_lock(&vnode->writeback_lock);
+               spin_lock(&vnode->wb_lock);
                i_size = i_size_read(&vnode->vfs_inode);
                if (maybe_i_size > i_size)
                        i_size_write(&vnode->vfs_inode, maybe_i_size);
-               spin_unlock(&vnode->writeback_lock);
+               spin_unlock(&vnode->wb_lock);
        }
 
        if (!PageUptodate(page)) {
@@ -299,16 +210,17 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 /*
  * kill all the pages in the given range
  */
-static void afs_kill_pages(struct afs_vnode *vnode, bool error,
+static void afs_kill_pages(struct address_space *mapping,
                           pgoff_t first, pgoff_t last)
 {
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
        struct pagevec pv;
        unsigned count, loop;
 
        _enter("{%x:%u},%lx-%lx",
               vnode->fid.vid, vnode->fid.vnode, first, last);
 
-       pagevec_init(&pv, 0);
+       pagevec_init(&pv);
 
        do {
                _debug("kill %lx-%lx", first, last);
@@ -316,37 +228,157 @@ static void afs_kill_pages(struct afs_vnode *vnode, bool error,
                count = last - first + 1;
                if (count > PAGEVEC_SIZE)
                        count = PAGEVEC_SIZE;
-               pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
-                                             first, count, pv.pages);
+               pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
                ASSERTCMP(pv.nr, ==, count);
 
                for (loop = 0; loop < count; loop++) {
                        struct page *page = pv.pages[loop];
                        ClearPageUptodate(page);
-                       if (error)
-                               SetPageError(page);
-                       if (PageWriteback(page))
-                               end_page_writeback(page);
+                       SetPageError(page);
+                       end_page_writeback(page);
+                       if (page->index >= first)
+                               first = page->index + 1;
+                       lock_page(page);
+                       generic_error_remove_page(mapping, page);
+               }
+
+               __pagevec_release(&pv);
+       } while (first <= last);
+
+       _leave("");
+}
+
+/*
+ * Redirty all the pages in a given range.
+ */
+static void afs_redirty_pages(struct writeback_control *wbc,
+                             struct address_space *mapping,
+                             pgoff_t first, pgoff_t last)
+{
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
+       struct pagevec pv;
+       unsigned count, loop;
+
+       _enter("{%x:%u},%lx-%lx",
+              vnode->fid.vid, vnode->fid.vnode, first, last);
+
+       pagevec_init(&pv);
+
+       do {
+               _debug("redirty %lx-%lx", first, last);
+
+               count = last - first + 1;
+               if (count > PAGEVEC_SIZE)
+                       count = PAGEVEC_SIZE;
+               pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
+               ASSERTCMP(pv.nr, ==, count);
+
+               for (loop = 0; loop < count; loop++) {
+                       struct page *page = pv.pages[loop];
+
+                       redirty_page_for_writepage(wbc, page);
+                       end_page_writeback(page);
                        if (page->index >= first)
                                first = page->index + 1;
                }
 
                __pagevec_release(&pv);
-       } while (first < last);
+       } while (first <= last);
 
        _leave("");
 }
 
 /*
- * synchronously write back the locked page and any subsequent non-locked dirty
- * pages also covered by the same writeback record
+ * write to a file
  */
-static int afs_write_back_from_locked_page(struct afs_writeback *wb,
-                                          struct page *primary_page)
+static int afs_store_data(struct address_space *mapping,
+                         pgoff_t first, pgoff_t last,
+                         unsigned offset, unsigned to)
 {
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
+       struct afs_fs_cursor fc;
+       struct afs_wb_key *wbk = NULL;
+       struct list_head *p;
+       int ret = -ENOKEY, ret2;
+
+       _enter("%s{%x:%u.%u},%lx,%lx,%x,%x",
+              vnode->volume->name,
+              vnode->fid.vid,
+              vnode->fid.vnode,
+              vnode->fid.unique,
+              first, last, offset, to);
+
+       spin_lock(&vnode->wb_lock);
+       p = vnode->wb_keys.next;
+
+       /* Iterate through the list looking for a valid key to use. */
+try_next_key:
+       while (p != &vnode->wb_keys) {
+               wbk = list_entry(p, struct afs_wb_key, vnode_link);
+               _debug("wbk %u", key_serial(wbk->key));
+               ret2 = key_validate(wbk->key);
+               if (ret2 == 0)
+                       goto found_key;
+               if (ret == -ENOKEY)
+                       ret = ret2;
+               p = p->next;
+       }
+
+       spin_unlock(&vnode->wb_lock);
+       afs_put_wb_key(wbk);
+       _leave(" = %d [no keys]", ret);
+       return ret;
+
+found_key:
+       refcount_inc(&wbk->usage);
+       spin_unlock(&vnode->wb_lock);
+
+       _debug("USE WB KEY %u", key_serial(wbk->key));
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_store_data(&fc, mapping, first, last, offset, to);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       switch (ret) {
+       case -EACCES:
+       case -EPERM:
+       case -ENOKEY:
+       case -EKEYEXPIRED:
+       case -EKEYREJECTED:
+       case -EKEYREVOKED:
+               _debug("next");
+               spin_lock(&vnode->wb_lock);
+               p = wbk->vnode_link.next;
+               afs_put_wb_key(wbk);
+               goto try_next_key;
+       }
+
+       afs_put_wb_key(wbk);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+/*
+ * Synchronously write back the locked page and any subsequent non-locked dirty
+ * pages.
+ */
+static int afs_write_back_from_locked_page(struct address_space *mapping,
+                                          struct writeback_control *wbc,
+                                          struct page *primary_page,
+                                          pgoff_t final_page)
+{
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
        struct page *pages[8], *page;
-       unsigned long count;
-       unsigned n, offset, to;
+       unsigned long count, priv;
+       unsigned n, offset, to, f, t;
        pgoff_t start, first, last;
        int loop, ret;
 
@@ -356,20 +388,33 @@ static int afs_write_back_from_locked_page(struct afs_writeback *wb,
        if (test_set_page_writeback(primary_page))
                BUG();
 
-       /* find all consecutive lockable dirty pages, stopping when we find a
-        * page that is not immediately lockable, is not dirty or is missing,
-        * or we reach the end of the range */
+       /* Find all consecutive lockable dirty pages that have contiguous
+        * written regions, stopping when we find a page that is not
+        * immediately lockable, is not dirty or is missing, or we reach the
+        * end of the range.
+        */
        start = primary_page->index;
-       if (start >= wb->last)
+       priv = page_private(primary_page);
+       offset = priv & AFS_PRIV_MAX;
+       to = priv >> AFS_PRIV_SHIFT;
+       trace_afs_page_dirty(vnode, tracepoint_string("store"),
+                            primary_page->index, priv);
+
+       WARN_ON(offset == to);
+       if (offset == to)
+               trace_afs_page_dirty(vnode, tracepoint_string("WARN"),
+                                    primary_page->index, priv);
+
+       if (start >= final_page || to < PAGE_SIZE)
                goto no_more;
+
        start++;
        do {
                _debug("more %lx [%lx]", start, count);
-               n = wb->last - start + 1;
+               n = final_page - start + 1;
                if (n > ARRAY_SIZE(pages))
                        n = ARRAY_SIZE(pages);
-               n = find_get_pages_contig(wb->vnode->vfs_inode.i_mapping,
-                                         start, n, pages);
+               n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages);
                _debug("fgpc %u", n);
                if (n == 0)
                        goto no_more;
@@ -381,16 +426,30 @@ static int afs_write_back_from_locked_page(struct afs_writeback *wb,
                }
 
                for (loop = 0; loop < n; loop++) {
+                       if (to != PAGE_SIZE)
+                               break;
                        page = pages[loop];
-                       if (page->index > wb->last)
+                       if (page->index > final_page)
                                break;
                        if (!trylock_page(page))
                                break;
-                       if (!PageDirty(page) ||
-                           page_private(page) != (unsigned long) wb) {
+                       if (!PageDirty(page) || PageWriteback(page)) {
                                unlock_page(page);
                                break;
                        }
+
+                       priv = page_private(page);
+                       f = priv & AFS_PRIV_MAX;
+                       t = priv >> AFS_PRIV_SHIFT;
+                       if (f != 0) {
+                               unlock_page(page);
+                               break;
+                       }
+                       to = t;
+
+                       trace_afs_page_dirty(vnode, tracepoint_string("store+"),
+                                            page->index, priv);
+
                        if (!clear_page_dirty_for_io(page))
                                BUG();
                        if (test_set_page_writeback(page))
@@ -406,50 +465,55 @@ static int afs_write_back_from_locked_page(struct afs_writeback *wb,
                }
 
                start += loop;
-       } while (start <= wb->last && count < 65536);
+       } while (start <= final_page && count < 65536);
 
 no_more:
-       /* we now have a contiguous set of dirty pages, each with writeback set
-        * and the dirty mark cleared; the first page is locked and must remain
-        * so, all the rest are unlocked */
+       /* We now have a contiguous set of dirty pages, each with writeback
+        * set; the first page is still locked at this point, but all the rest
+        * have been unlocked.
+        */
+       unlock_page(primary_page);
+
        first = primary_page->index;
        last = first + count - 1;
 
-       offset = (first == wb->first) ? wb->offset_first : 0;
-       to = (last == wb->last) ? wb->to_last : PAGE_SIZE;
-
        _debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to);
 
-       ret = afs_vnode_store_data(wb, first, last, offset, to);
-       if (ret < 0) {
-               switch (ret) {
-               case -EDQUOT:
-               case -ENOSPC:
-                       mapping_set_error(wb->vnode->vfs_inode.i_mapping, -ENOSPC);
-                       break;
-               case -EROFS:
-               case -EIO:
-               case -EREMOTEIO:
-               case -EFBIG:
-               case -ENOENT:
-               case -ENOMEDIUM:
-               case -ENXIO:
-                       afs_kill_pages(wb->vnode, true, first, last);
-                       mapping_set_error(wb->vnode->vfs_inode.i_mapping, -EIO);
-                       break;
-               case -EACCES:
-               case -EPERM:
-               case -ENOKEY:
-               case -EKEYEXPIRED:
-               case -EKEYREJECTED:
-               case -EKEYREVOKED:
-                       afs_kill_pages(wb->vnode, false, first, last);
-                       break;
-               default:
-                       break;
-               }
-       } else {
+       ret = afs_store_data(mapping, first, last, offset, to);
+       switch (ret) {
+       case 0:
                ret = count;
+               break;
+
+       default:
+               pr_notice("kAFS: Unexpected error from FS.StoreData %d\n", ret);
+               /* Fall through */
+       case -EACCES:
+       case -EPERM:
+       case -ENOKEY:
+       case -EKEYEXPIRED:
+       case -EKEYREJECTED:
+       case -EKEYREVOKED:
+               afs_redirty_pages(wbc, mapping, first, last);
+               mapping_set_error(mapping, ret);
+               break;
+
+       case -EDQUOT:
+       case -ENOSPC:
+               afs_redirty_pages(wbc, mapping, first, last);
+               mapping_set_error(mapping, -ENOSPC);
+               break;
+
+       case -EROFS:
+       case -EIO:
+       case -EREMOTEIO:
+       case -EFBIG:
+       case -ENOENT:
+       case -ENOMEDIUM:
+       case -ENXIO:
+               afs_kill_pages(mapping, first, last);
+               mapping_set_error(mapping, ret);
+               break;
        }
 
        _leave(" = %d", ret);
@@ -462,16 +526,12 @@ no_more:
  */
 int afs_writepage(struct page *page, struct writeback_control *wbc)
 {
-       struct afs_writeback *wb;
        int ret;
 
        _enter("{%lx},", page->index);
 
-       wb = (struct afs_writeback *) page_private(page);
-       ASSERT(wb != NULL);
-
-       ret = afs_write_back_from_locked_page(wb, page);
-       unlock_page(page);
+       ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
+                                             wbc->range_end >> PAGE_SHIFT);
        if (ret < 0) {
                _leave(" = %d", ret);
                return 0;
@@ -490,33 +550,30 @@ static int afs_writepages_region(struct address_space *mapping,
                                 struct writeback_control *wbc,
                                 pgoff_t index, pgoff_t end, pgoff_t *_next)
 {
-       struct afs_writeback *wb;
        struct page *page;
        int ret, n;
 
        _enter(",,%lx,%lx,", index, end);
 
        do {
-               n = find_get_pages_tag(mapping, &index, PAGECACHE_TAG_DIRTY,
-                                      1, &page);
+               n = find_get_pages_range_tag(mapping, &index, end,
+                                       PAGECACHE_TAG_DIRTY, 1, &page);
                if (!n)
                        break;
 
                _debug("wback %lx", page->index);
 
-               if (page->index > end) {
-                       *_next = index;
-                       put_page(page);
-                       _leave(" = 0 [%lx]", *_next);
-                       return 0;
-               }
-
                /* at this point we hold neither mapping->tree_lock nor lock on
                 * the page itself: the page may be truncated or invalidated
                 * (changing page->mapping to NULL), or even swizzled back from
                 * swapper_space to tmpfs file mapping
                 */
-               lock_page(page);
+               ret = lock_page_killable(page);
+               if (ret < 0) {
+                       put_page(page);
+                       _leave(" = %d", ret);
+                       return ret;
+               }
 
                if (page->mapping != mapping || !PageDirty(page)) {
                        unlock_page(page);
@@ -532,17 +589,9 @@ static int afs_writepages_region(struct address_space *mapping,
                        continue;
                }
 
-               wb = (struct afs_writeback *) page_private(page);
-               ASSERT(wb != NULL);
-
-               spin_lock(&wb->vnode->writeback_lock);
-               wb->state = AFS_WBACK_WRITING;
-               spin_unlock(&wb->vnode->writeback_lock);
-
                if (!clear_page_dirty_for_io(page))
                        BUG();
-               ret = afs_write_back_from_locked_page(wb, page);
-               unlock_page(page);
+               ret = afs_write_back_from_locked_page(mapping, wbc, page, end);
                put_page(page);
                if (ret < 0) {
                        _leave(" = %d", ret);
@@ -598,18 +647,15 @@ int afs_writepages(struct address_space *mapping,
  */
 void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call)
 {
-       struct afs_writeback *wb = call->wb;
        struct pagevec pv;
+       unsigned long priv;
        unsigned count, loop;
        pgoff_t first = call->first, last = call->last;
-       bool free_wb;
 
        _enter("{%x:%u},{%lx-%lx}",
               vnode->fid.vid, vnode->fid.vnode, first, last);
 
-       ASSERT(wb != NULL);
-
-       pagevec_init(&pv, 0);
+       pagevec_init(&pv);
 
        do {
                _debug("done %lx-%lx", first, last);
@@ -617,35 +663,22 @@ void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call)
                count = last - first + 1;
                if (count > PAGEVEC_SIZE)
                        count = PAGEVEC_SIZE;
-               pv.nr = find_get_pages_contig(call->mapping, first, count,
-                                             pv.pages);
+               pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
+                                             first, count, pv.pages);
                ASSERTCMP(pv.nr, ==, count);
 
-               spin_lock(&vnode->writeback_lock);
                for (loop = 0; loop < count; loop++) {
-                       struct page *page = pv.pages[loop];
-                       end_page_writeback(page);
-                       if (page_private(page) == (unsigned long) wb) {
-                               set_page_private(page, 0);
-                               ClearPagePrivate(page);
-                               wb->usage--;
-                       }
-               }
-               free_wb = false;
-               if (wb->usage == 0) {
-                       afs_unlink_writeback(wb);
-                       free_wb = true;
+                       priv = page_private(pv.pages[loop]);
+                       trace_afs_page_dirty(vnode, tracepoint_string("clear"),
+                                            pv.pages[loop]->index, priv);
+                       set_page_private(pv.pages[loop], 0);
+                       end_page_writeback(pv.pages[loop]);
                }
-               spin_unlock(&vnode->writeback_lock);
                first += count;
-               if (free_wb) {
-                       afs_free_writeback(wb);
-                       wb = NULL;
-               }
-
                __pagevec_release(&pv);
        } while (first <= last);
 
+       afs_prune_wb_keys(vnode);
        _leave("");
 }
 
@@ -676,28 +709,6 @@ ssize_t afs_file_write(struct kiocb *iocb, struct iov_iter *from)
        return result;
 }
 
-/*
- * flush the vnode to the fileserver
- */
-int afs_writeback_all(struct afs_vnode *vnode)
-{
-       struct address_space *mapping = vnode->vfs_inode.i_mapping;
-       struct writeback_control wbc = {
-               .sync_mode      = WB_SYNC_ALL,
-               .nr_to_write    = LONG_MAX,
-               .range_cyclic   = 1,
-       };
-       int ret;
-
-       _enter("");
-
-       ret = mapping->a_ops->writepages(mapping, &wbc);
-       __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-
-       _leave(" = %d", ret);
-       return ret;
-}
-
 /*
  * flush any dirty pages for this process, and check for write errors.
  * - the return status from this call provides a reliable indication of
@@ -706,61 +717,13 @@ int afs_writeback_all(struct afs_vnode *vnode)
 int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct inode *inode = file_inode(file);
-       struct afs_writeback *wb, *xwb;
        struct afs_vnode *vnode = AFS_FS_I(inode);
-       int ret;
 
        _enter("{%x:%u},{n=%pD},%d",
               vnode->fid.vid, vnode->fid.vnode, file,
               datasync);
 
-       ret = file_write_and_wait_range(file, start, end);
-       if (ret)
-               return ret;
-       inode_lock(inode);
-
-       /* use a writeback record as a marker in the queue - when this reaches
-        * the front of the queue, all the outstanding writes are either
-        * completed or rejected */
-       wb = kzalloc(sizeof(*wb), GFP_KERNEL);
-       if (!wb) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       wb->vnode = vnode;
-       wb->first = 0;
-       wb->last = -1;
-       wb->offset_first = 0;
-       wb->to_last = PAGE_SIZE;
-       wb->usage = 1;
-       wb->state = AFS_WBACK_SYNCING;
-       init_waitqueue_head(&wb->waitq);
-
-       spin_lock(&vnode->writeback_lock);
-       list_for_each_entry(xwb, &vnode->writebacks, link) {
-               if (xwb->state == AFS_WBACK_PENDING)
-                       xwb->state = AFS_WBACK_CONFLICTING;
-       }
-       list_add_tail(&wb->link, &vnode->writebacks);
-       spin_unlock(&vnode->writeback_lock);
-
-       /* push all the outstanding writebacks to the server */
-       ret = afs_writeback_all(vnode);
-       if (ret < 0) {
-               afs_put_writeback(wb);
-               _leave(" = %d [wb]", ret);
-               goto out;
-       }
-
-       /* wait for the preceding writes to actually complete */
-       ret = wait_event_interruptible(wb->waitq,
-                                      wb->state == AFS_WBACK_COMPLETE ||
-                                      vnode->writebacks.next == &wb->link);
-       afs_put_writeback(wb);
-       _leave(" = %d", ret);
-out:
-       inode_unlock(inode);
-       return ret;
+       return file_write_and_wait_range(file, start, end);
 }
 
 /*
@@ -781,19 +744,114 @@ int afs_flush(struct file *file, fl_owner_t id)
  * notification that a previously read-only page is about to become writable
  * - if it returns an error, the caller will deliver a bus error signal
  */
-int afs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
+int afs_page_mkwrite(struct vm_fault *vmf)
 {
-       struct afs_vnode *vnode = AFS_FS_I(vma->vm_file->f_mapping->host);
+       struct file *file = vmf->vma->vm_file;
+       struct inode *inode = file_inode(file);
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       unsigned long priv;
 
        _enter("{{%x:%u}},{%lx}",
-              vnode->fid.vid, vnode->fid.vnode, page->index);
+              vnode->fid.vid, vnode->fid.vnode, vmf->page->index);
+
+       sb_start_pagefault(inode->i_sb);
 
-       /* wait for the page to be written to the cache before we allow it to
-        * be modified */
+       /* Wait for the page to be written to the cache before we allow it to
+        * be modified.  We then assume the entire page will need writing back.
+        */
 #ifdef CONFIG_AFS_FSCACHE
-       fscache_wait_on_page_write(vnode->cache, page);
+       fscache_wait_on_page_write(vnode->cache, vmf->page);
 #endif
 
-       _leave(" = 0");
-       return 0;
+       if (PageWriteback(vmf->page) &&
+           wait_on_page_bit_killable(vmf->page, PG_writeback) < 0)
+               return VM_FAULT_RETRY;
+
+       if (lock_page_killable(vmf->page) < 0)
+               return VM_FAULT_RETRY;
+
+       /* We mustn't change page->private until writeback is complete as that
+        * details the portion of the page we need to write back and we might
+        * need to redirty the page if there's a problem.
+        */
+       wait_on_page_writeback(vmf->page);
+
+       priv = (unsigned long)PAGE_SIZE << AFS_PRIV_SHIFT; /* To */
+       priv |= 0; /* From */
+       trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"),
+                            vmf->page->index, priv);
+       SetPagePrivate(vmf->page);
+       set_page_private(vmf->page, priv);
+
+       sb_end_pagefault(inode->i_sb);
+       return VM_FAULT_LOCKED;
+}
+
+/*
+ * Prune the keys cached for writeback.  The caller must hold vnode->wb_lock.
+ */
+void afs_prune_wb_keys(struct afs_vnode *vnode)
+{
+       LIST_HEAD(graveyard);
+       struct afs_wb_key *wbk, *tmp;
+
+       /* Discard unused keys */
+       spin_lock(&vnode->wb_lock);
+
+       if (!mapping_tagged(&vnode->vfs_inode.i_data, PAGECACHE_TAG_WRITEBACK) &&
+           !mapping_tagged(&vnode->vfs_inode.i_data, PAGECACHE_TAG_DIRTY)) {
+               list_for_each_entry_safe(wbk, tmp, &vnode->wb_keys, vnode_link) {
+                       if (refcount_read(&wbk->usage) == 1)
+                               list_move(&wbk->vnode_link, &graveyard);
+               }
+       }
+
+       spin_unlock(&vnode->wb_lock);
+
+       while (!list_empty(&graveyard)) {
+               wbk = list_entry(graveyard.next, struct afs_wb_key, vnode_link);
+               list_del(&wbk->vnode_link);
+               afs_put_wb_key(wbk);
+       }
+}
+
+/*
+ * Clean up a page during invalidation.
+ */
+int afs_launder_page(struct page *page)
+{
+       struct address_space *mapping = page->mapping;
+       struct afs_vnode *vnode = AFS_FS_I(mapping->host);
+       unsigned long priv;
+       unsigned int f, t;
+       int ret = 0;
+
+       _enter("{%lx}", page->index);
+
+       priv = page_private(page);
+       if (clear_page_dirty_for_io(page)) {
+               f = 0;
+               t = PAGE_SIZE;
+               if (PagePrivate(page)) {
+                       f = priv & AFS_PRIV_MAX;
+                       t = priv >> AFS_PRIV_SHIFT;
+               }
+
+               trace_afs_page_dirty(vnode, tracepoint_string("launder"),
+                                    page->index, priv);
+               ret = afs_store_data(mapping, page->index, page->index, t, f);
+       }
+
+       trace_afs_page_dirty(vnode, tracepoint_string("laundered"),
+                            page->index, priv);
+       set_page_private(page, 0);
+       ClearPagePrivate(page);
+
+#ifdef CONFIG_AFS_FSCACHE
+       if (PageFsCache(page)) {
+               fscache_wait_on_page_write(vnode->cache, page);
+               fscache_uncache_page(vnode->cache, page);
+       }
+#endif
+       return ret;
 }