afs: Fix to take ref on page when PG_private is set
authorDavid Howells <dhowells@redhat.com>
Wed, 21 Oct 2020 12:22:19 +0000 (13:22 +0100)
committerDavid Howells <dhowells@redhat.com>
Thu, 29 Oct 2020 13:53:04 +0000 (13:53 +0000)
Fix afs to take a ref on a page when it sets PG_private on it and to drop
the ref when removing the flag.

Note that in afs_write_begin(), a lot of the time, PG_private is already
set on a page to which we're going to add some data.  In such a case, we
leave the bit set and mustn't increment the page count.

As suggested by Matthew Wilcox, use attach/detach_page_private() where
possible.

Fixes: 31143d5d515e ("AFS: implement basic file write support")
Reported-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
fs/afs/dir.c
fs/afs/dir_edit.c
fs/afs/file.c
fs/afs/write.c

index 1d2e61e..1bb5b9d 100644 (file)
@@ -281,8 +281,7 @@ retry:
                        if (ret < 0)
                                goto error;
 
-                       set_page_private(req->pages[i], 1);
-                       SetPagePrivate(req->pages[i]);
+                       attach_page_private(req->pages[i], (void *)1);
                        unlock_page(req->pages[i]);
                        i++;
                } else {
@@ -1975,8 +1974,7 @@ static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags)
 
        _enter("{{%llx:%llu}[%lu]}", dvnode->fid.vid, dvnode->fid.vnode, page->index);
 
-       set_page_private(page, 0);
-       ClearPagePrivate(page);
+       detach_page_private(page);
 
        /* The directory will need reloading. */
        if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
@@ -2003,8 +2001,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
                afs_stat_v(dvnode, n_inval);
 
        /* we clean up only if the entire page is being invalidated */
-       if (offset == 0 && length == PAGE_SIZE) {
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
-       }
+       if (offset == 0 && length == PAGE_SIZE)
+               detach_page_private(page);
 }
index b108528..2ffe09a 100644 (file)
@@ -243,10 +243,8 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
                                                   index, gfp);
                        if (!page)
                                goto error;
-                       if (!PagePrivate(page)) {
-                               set_page_private(page, 1);
-                               SetPagePrivate(page);
-                       }
+                       if (!PagePrivate(page))
+                               attach_page_private(page, (void *)1);
                        dir_page = kmap(page);
                }
 
index 9122542..322973d 100644 (file)
@@ -627,11 +627,9 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,
 #endif
 
                if (PagePrivate(page)) {
-                       priv = page_private(page);
+                       priv = (unsigned long)detach_page_private(page);
                        trace_afs_page_dirty(vnode, tracepoint_string("inval"),
                                             page->index, priv);
-                       set_page_private(page, 0);
-                       ClearPagePrivate(page);
                }
        }
 
@@ -661,11 +659,9 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
 #endif
 
        if (PagePrivate(page)) {
-               priv = page_private(page);
+               priv = (unsigned long)detach_page_private(page);
                trace_afs_page_dirty(vnode, tracepoint_string("rel"),
                                     page->index, priv);
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
        }
 
        /* indicate that the page can be released */
index b937ec0..02facb1 100644 (file)
@@ -151,8 +151,10 @@ try_again:
        priv |= f;
        trace_afs_page_dirty(vnode, tracepoint_string("begin"),
                             page->index, priv);
-       SetPagePrivate(page);
-       set_page_private(page, priv);
+       if (PagePrivate(page))
+               set_page_private(page, priv);
+       else
+               attach_page_private(page, (void *)priv);
        _leave(" = 0");
        return 0;
 
@@ -334,10 +336,9 @@ static void afs_pages_written_back(struct afs_vnode *vnode,
                ASSERTCMP(pv.nr, ==, count);
 
                for (loop = 0; loop < count; loop++) {
-                       priv = page_private(pv.pages[loop]);
+                       priv = (unsigned long)detach_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]);
                }
                first += count;
@@ -863,8 +864,10 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
        priv |= 0; /* From */
        trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"),
                             vmf->page->index, priv);
-       SetPagePrivate(vmf->page);
-       set_page_private(vmf->page, priv);
+       if (PagePrivate(vmf->page))
+               set_page_private(vmf->page, priv);
+       else
+               attach_page_private(vmf->page, (void *)priv);
        file_update_time(file);
 
        sb_end_pagefault(inode->i_sb);
@@ -926,10 +929,9 @@ int afs_launder_page(struct page *page)
                ret = afs_store_data(mapping, page->index, page->index, t, f, true);
        }
 
+       priv = (unsigned long)detach_page_private(page);
        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)) {