lkml.org 
[lkml]   [2020]   [Oct]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 05/11] afs: Fix to take ref on page when PG_private is set
    From
    Date
    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. To this end, make
    TestSetPagePrivate() available.

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

    fs/afs/dir.c | 3 +++
    fs/afs/dir_edit.c | 1 +
    fs/afs/file.c | 2 ++
    fs/afs/write.c | 9 +++++++--
    include/linux/page-flags.h | 1 +
    5 files changed, 14 insertions(+), 2 deletions(-)

    diff --git a/fs/afs/dir.c b/fs/afs/dir.c
    index 1d2e61e0ab04..064eb66c33e9 100644
    --- a/fs/afs/dir.c
    +++ b/fs/afs/dir.c
    @@ -283,6 +283,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)

    set_page_private(req->pages[i], 1);
    SetPagePrivate(req->pages[i]);
    + get_page(req->pages[i]);
    unlock_page(req->pages[i]);
    i++;
    } else {
    @@ -1977,6 +1978,7 @@ static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags)

    set_page_private(page, 0);
    ClearPagePrivate(page);
    + put_page(page);

    /* The directory will need reloading. */
    if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
    @@ -2006,5 +2008,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
    if (offset == 0 && length == PAGE_SIZE) {
    set_page_private(page, 0);
    ClearPagePrivate(page);
    + put_page(page);
    }
    }
    diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c
    index b108528bf010..997f6798beee 100644
    --- a/fs/afs/dir_edit.c
    +++ b/fs/afs/dir_edit.c
    @@ -246,6 +246,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
    if (!PagePrivate(page)) {
    set_page_private(page, 1);
    SetPagePrivate(page);
    + get_page(page);
    }
    dir_page = kmap(page);
    }
    diff --git a/fs/afs/file.c b/fs/afs/file.c
    index 91225421ad37..7dafa2266048 100644
    --- a/fs/afs/file.c
    +++ b/fs/afs/file.c
    @@ -632,6 +632,7 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,
    page->index, priv);
    set_page_private(page, 0);
    ClearPagePrivate(page);
    + put_page(page);
    }
    }

    @@ -666,6 +667,7 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
    page->index, priv);
    set_page_private(page, 0);
    ClearPagePrivate(page);
    + put_page(page);
    }

    /* indicate that the page can be released */
    diff --git a/fs/afs/write.c b/fs/afs/write.c
    index b937ec047ec9..29685947324e 100644
    --- a/fs/afs/write.c
    +++ b/fs/afs/write.c
    @@ -151,7 +151,8 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
    priv |= f;
    trace_afs_page_dirty(vnode, tracepoint_string("begin"),
    page->index, priv);
    - SetPagePrivate(page);
    + if (!TestSetPagePrivate(page))
    + get_page(page);
    set_page_private(page, priv);
    _leave(" = 0");
    return 0;
    @@ -338,6 +339,8 @@ static void afs_pages_written_back(struct afs_vnode *vnode,
    trace_afs_page_dirty(vnode, tracepoint_string("clear"),
    pv.pages[loop]->index, priv);
    set_page_private(pv.pages[loop], 0);
    + ClearPagePrivate(pv.pages[loop]);
    + put_page(pv.pages[loop]);
    end_page_writeback(pv.pages[loop]);
    }
    first += count;
    @@ -863,7 +866,8 @@ 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);
    + if (!TestSetPagePrivate(vmf->page))
    + get_page(vmf->page);
    set_page_private(vmf->page, priv);
    file_update_time(file);

    @@ -930,6 +934,7 @@ int afs_launder_page(struct page *page)
    page->index, priv);
    set_page_private(page, 0);
    ClearPagePrivate(page);
    + put_page(page);

    #ifdef CONFIG_AFS_FSCACHE
    if (PageFsCache(page)) {
    diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
    index 4f6ba9379112..37d65b55a6c6 100644
    --- a/include/linux/page-flags.h
    +++ b/include/linux/page-flags.h
    @@ -365,6 +365,7 @@ PAGEFLAG(SwapBacked, swapbacked, PF_NO_TAIL)
    */
    PAGEFLAG(Private, private, PF_ANY) __SETPAGEFLAG(Private, private, PF_ANY)
    __CLEARPAGEFLAG(Private, private, PF_ANY)
    + TESTSETFLAG(Private, private, PF_ANY)
    PAGEFLAG(Private2, private_2, PF_ANY) TESTSCFLAG(Private2, private_2, PF_ANY)
    PAGEFLAG(OwnerPriv1, owner_priv_1, PF_ANY)
    TESTCLEARFLAG(OwnerPriv1, owner_priv_1, PF_ANY)

    \
     
     \ /
      Last update: 2020-10-29 02:05    [W:8.572 / U:0.176 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site