Commit fa04a40b authored by David Howells's avatar David Howells
Browse files

afs: Fix to take ref on page when PG_private is set



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: 31143d5d ("AFS: implement basic file write support")
Reported-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
parent d383e346
Loading
Loading
Loading
Loading
+4 −8
Original line number Diff line number Diff line
@@ -281,8 +281,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
			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);
}
+2 −4
Original line number Diff line number Diff line
@@ -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);
		}

+2 −6
Original line number Diff line number Diff line
@@ -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 */
+10 −8
Original line number Diff line number Diff line
@@ -151,8 +151,10 @@ 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 (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);
	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)) {