Commit be5b82db authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent b9e1d435
...@@ -128,7 +128,7 @@ struct afs_lookup_cookie { ...@@ -128,7 +128,7 @@ struct afs_lookup_cookie {
/* /*
* check that a directory page is valid * check that a directory page is valid
*/ */
static inline void afs_dir_check_page(struct inode *dir, struct page *page) static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
{ {
struct afs_dir_page *dbuf; struct afs_dir_page *dbuf;
loff_t latter; loff_t latter;
...@@ -168,11 +168,11 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page) ...@@ -168,11 +168,11 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page)
} }
SetPageChecked(page); SetPageChecked(page);
return; return true;
error: error:
SetPageChecked(page);
SetPageError(page); SetPageError(page);
return false;
} }
/* /*
...@@ -196,11 +196,11 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index, ...@@ -196,11 +196,11 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
page = read_cache_page(dir->i_mapping, index, afs_page_filler, key); page = read_cache_page(dir->i_mapping, index, afs_page_filler, key);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (unlikely(!PageChecked(page))) {
afs_dir_check_page(dir, page); if (PageError(page) || !afs_dir_check_page(dir, page))
if (PageError(page))
goto fail; goto fail;
} }
}
return page; return page;
fail: fail:
......
...@@ -79,7 +79,7 @@ static int exofs_commit_chunk(struct page *page, loff_t pos, unsigned len) ...@@ -79,7 +79,7 @@ static int exofs_commit_chunk(struct page *page, loff_t pos, unsigned len)
return err; return err;
} }
static void exofs_check_page(struct page *page) static bool exofs_check_page(struct page *page)
{ {
struct inode *dir = page->mapping->host; struct inode *dir = page->mapping->host;
unsigned chunk_size = exofs_chunk_size(dir); unsigned chunk_size = exofs_chunk_size(dir);
...@@ -114,7 +114,7 @@ static void exofs_check_page(struct page *page) ...@@ -114,7 +114,7 @@ static void exofs_check_page(struct page *page)
goto Eend; goto Eend;
out: out:
SetPageChecked(page); SetPageChecked(page);
return; return true;
Ebadsize: Ebadsize:
EXOFS_ERR("ERROR [exofs_check_page]: " EXOFS_ERR("ERROR [exofs_check_page]: "
...@@ -150,8 +150,8 @@ static void exofs_check_page(struct page *page) ...@@ -150,8 +150,8 @@ static void exofs_check_page(struct page *page)
dir->i_ino, (page->index<<PAGE_SHIFT)+offs, dir->i_ino, (page->index<<PAGE_SHIFT)+offs,
_LLU(le64_to_cpu(p->inode_no))); _LLU(le64_to_cpu(p->inode_no)));
fail: fail:
SetPageChecked(page);
SetPageError(page); SetPageError(page);
return false;
} }
static struct page *exofs_get_page(struct inode *dir, unsigned long n) static struct page *exofs_get_page(struct inode *dir, unsigned long n)
...@@ -161,11 +161,11 @@ static struct page *exofs_get_page(struct inode *dir, unsigned long n) ...@@ -161,11 +161,11 @@ static struct page *exofs_get_page(struct inode *dir, unsigned long n)
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (unlikely(!PageChecked(page))) {
exofs_check_page(page); if (PageError(page) || !exofs_check_page(page))
if (PageError(page))
goto fail; goto fail;
} }
}
return page; return page;
fail: fail:
......
...@@ -110,7 +110,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) ...@@ -110,7 +110,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)
return err; return err;
} }
static void ext2_check_page(struct page *page, int quiet) static bool ext2_check_page(struct page *page, int quiet)
{ {
struct inode *dir = page->mapping->host; struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
...@@ -148,7 +148,7 @@ static void ext2_check_page(struct page *page, int quiet) ...@@ -148,7 +148,7 @@ static void ext2_check_page(struct page *page, int quiet)
goto Eend; goto Eend;
out: out:
SetPageChecked(page); SetPageChecked(page);
return; return true;
/* Too bad, we had an error */ /* Too bad, we had an error */
...@@ -190,8 +190,8 @@ static void ext2_check_page(struct page *page, int quiet) ...@@ -190,8 +190,8 @@ static void ext2_check_page(struct page *page, int quiet)
(unsigned long) le32_to_cpu(p->inode)); (unsigned long) le32_to_cpu(p->inode));
} }
fail: fail:
SetPageChecked(page);
SetPageError(page); SetPageError(page);
return false;
} }
static struct page * ext2_get_page(struct inode *dir, unsigned long n, static struct page * ext2_get_page(struct inode *dir, unsigned long n,
...@@ -201,11 +201,11 @@ static struct page * ext2_get_page(struct inode *dir, unsigned long n, ...@@ -201,11 +201,11 @@ static struct page * ext2_get_page(struct inode *dir, unsigned long n,
struct page *page = read_mapping_page(mapping, n, NULL); struct page *page = read_mapping_page(mapping, n, NULL);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (unlikely(!PageChecked(page))) {
ext2_check_page(page, quiet); if (PageError(page) || !ext2_check_page(page, quiet))
if (PageError(page))
goto fail; goto fail;
} }
}
return page; return page;
fail: fail:
......
...@@ -102,7 +102,7 @@ static void nilfs_commit_chunk(struct page *page, ...@@ -102,7 +102,7 @@ static void nilfs_commit_chunk(struct page *page,
unlock_page(page); unlock_page(page);
} }
static void nilfs_check_page(struct page *page) static bool nilfs_check_page(struct page *page)
{ {
struct inode *dir = page->mapping->host; struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
...@@ -137,7 +137,7 @@ static void nilfs_check_page(struct page *page) ...@@ -137,7 +137,7 @@ static void nilfs_check_page(struct page *page)
goto Eend; goto Eend;
out: out:
SetPageChecked(page); SetPageChecked(page);
return; return true;
/* Too bad, we had an error */ /* Too bad, we had an error */
...@@ -173,8 +173,8 @@ static void nilfs_check_page(struct page *page) ...@@ -173,8 +173,8 @@ static void nilfs_check_page(struct page *page)
dir->i_ino, (page->index<<PAGE_SHIFT)+offs, dir->i_ino, (page->index<<PAGE_SHIFT)+offs,
(unsigned long) le64_to_cpu(p->inode)); (unsigned long) le64_to_cpu(p->inode));
fail: fail:
SetPageChecked(page);
SetPageError(page); SetPageError(page);
return false;
} }
static struct page *nilfs_get_page(struct inode *dir, unsigned long n) static struct page *nilfs_get_page(struct inode *dir, unsigned long n)
...@@ -184,11 +184,11 @@ static struct page *nilfs_get_page(struct inode *dir, unsigned long n) ...@@ -184,11 +184,11 @@ static struct page *nilfs_get_page(struct inode *dir, unsigned long n)
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (unlikely(!PageChecked(page))) {
nilfs_check_page(page); if (PageError(page) || !nilfs_check_page(page))
if (PageError(page))
goto fail; goto fail;
} }
}
return page; return page;
fail: fail:
......
...@@ -105,7 +105,7 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, ...@@ -105,7 +105,7 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
} }
static void ufs_check_page(struct page *page) static bool ufs_check_page(struct page *page)
{ {
struct inode *dir = page->mapping->host; struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
...@@ -143,7 +143,7 @@ static void ufs_check_page(struct page *page) ...@@ -143,7 +143,7 @@ static void ufs_check_page(struct page *page)
goto Eend; goto Eend;
out: out:
SetPageChecked(page); SetPageChecked(page);
return; return true;
/* Too bad, we had an error */ /* Too bad, we had an error */
...@@ -180,8 +180,8 @@ static void ufs_check_page(struct page *page) ...@@ -180,8 +180,8 @@ static void ufs_check_page(struct page *page)
"offset=%lu", "offset=%lu",
dir->i_ino, (page->index<<PAGE_SHIFT)+offs); dir->i_ino, (page->index<<PAGE_SHIFT)+offs);
fail: fail:
SetPageChecked(page);
SetPageError(page); SetPageError(page);
return false;
} }
static struct page *ufs_get_page(struct inode *dir, unsigned long n) static struct page *ufs_get_page(struct inode *dir, unsigned long n)
...@@ -190,11 +190,11 @@ static struct page *ufs_get_page(struct inode *dir, unsigned long n) ...@@ -190,11 +190,11 @@ static struct page *ufs_get_page(struct inode *dir, unsigned long n)
struct page *page = read_mapping_page(mapping, n, NULL); struct page *page = read_mapping_page(mapping, n, NULL);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (unlikely(!PageChecked(page))) {
ufs_check_page(page); if (PageError(page) || !ufs_check_page(page))
if (PageError(page))
goto fail; goto fail;
} }
}
return page; return page;
fail: fail:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment