diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-04-22 22:06:44 +0300 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-05-03 02:47:25 +0300 |
commit | be5b82dbfec2a900925da4437af3c60b61f4c53d (patch) | |
tree | a65508f3456f623f51215581e3b00529a9c20ee7 /fs/exofs | |
parent | b9e1d435fdf4ae0b925070d44b65d608f2707688 (diff) | |
download | linux-be5b82dbfec2a900925da4437af3c60b61f4c53d.tar.xz |
make ext2_get_page() and friends work without external serialization
Right now ext2_get_page() (and its analogues in a bunch of other filesystems)
relies upon the directory being locked - the way it sets and tests Checked and
Error bits would be racy without that. Switch to a slightly different scheme,
_not_ setting Checked in case of failure. That way the logics becomes
if Checked => OK
else if Error => fail
else if !validate => fail
else => OK
with validation setting Checked or Error on success and failure resp. and
returning which one had happened. Equivalent to the current logics, but unlike
the current logics not sensitive to the order of set_bit, test_bit getting
reordered by CPU, etc.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/exofs')
-rw-r--r-- | fs/exofs/dir.c | 14 |
1 files changed, 7 insertions, 7 deletions
diff --git a/fs/exofs/dir.c b/fs/exofs/dir.c index 547b93cbea63..a7661aaead9a 100644 --- a/fs/exofs/dir.c +++ b/fs/exofs/dir.c @@ -79,7 +79,7 @@ static int exofs_commit_chunk(struct page *page, loff_t pos, unsigned len) return err; } -static void exofs_check_page(struct page *page) +static bool exofs_check_page(struct page *page) { struct inode *dir = page->mapping->host; unsigned chunk_size = exofs_chunk_size(dir); @@ -114,7 +114,7 @@ static void exofs_check_page(struct page *page) goto Eend; out: SetPageChecked(page); - return; + return true; Ebadsize: EXOFS_ERR("ERROR [exofs_check_page]: " @@ -150,8 +150,8 @@ Eend: dir->i_ino, (page->index<<PAGE_SHIFT)+offs, _LLU(le64_to_cpu(p->inode_no))); fail: - SetPageChecked(page); SetPageError(page); + return false; } static struct page *exofs_get_page(struct inode *dir, unsigned long n) @@ -161,10 +161,10 @@ static struct page *exofs_get_page(struct inode *dir, unsigned long n) if (!IS_ERR(page)) { kmap(page); - if (!PageChecked(page)) - exofs_check_page(page); - if (PageError(page)) - goto fail; + if (unlikely(!PageChecked(page))) { + if (PageError(page) || !exofs_check_page(page)) + goto fail; + } } return page; |