@@ -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);
}
}
@@ -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);
}
@@ -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 */
@@ -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)) {
@@ -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)
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(-)