aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext2
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-04-22 15:06:44 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-05-02 19:47:25 -0400
commitbe5b82dbfec2a900925da4437af3c60b61f4c53d (patch)
treea65508f3456f623f51215581e3b00529a9c20ee7 /fs/ext2
parentb9e1d435fdf4ae0b925070d44b65d608f2707688 (diff)
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/ext2')
-rw-r--r--fs/ext2/dir.c14
1 files changed, 7 insertions, 7 deletions
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 7ff6fcfa685d..f0f4363a15b2 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -110,7 +110,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)
110 return err; 110 return err;
111} 111}
112 112
113static void ext2_check_page(struct page *page, int quiet) 113static bool ext2_check_page(struct page *page, int quiet)
114{ 114{
115 struct inode *dir = page->mapping->host; 115 struct inode *dir = page->mapping->host;
116 struct super_block *sb = dir->i_sb; 116 struct super_block *sb = dir->i_sb;
@@ -148,7 +148,7 @@ static void ext2_check_page(struct page *page, int quiet)
148 goto Eend; 148 goto Eend;
149out: 149out:
150 SetPageChecked(page); 150 SetPageChecked(page);
151 return; 151 return true;
152 152
153 /* Too bad, we had an error */ 153 /* Too bad, we had an error */
154 154
@@ -190,8 +190,8 @@ Eend:
190 (unsigned long) le32_to_cpu(p->inode)); 190 (unsigned long) le32_to_cpu(p->inode));
191 } 191 }
192fail: 192fail:
193 SetPageChecked(page);
194 SetPageError(page); 193 SetPageError(page);
194 return false;
195} 195}
196 196
197static struct page * ext2_get_page(struct inode *dir, unsigned long n, 197static struct page * ext2_get_page(struct inode *dir, unsigned long n,
@@ -201,10 +201,10 @@ static struct page * ext2_get_page(struct inode *dir, unsigned long n,
201 struct page *page = read_mapping_page(mapping, n, NULL); 201 struct page *page = read_mapping_page(mapping, n, NULL);
202 if (!IS_ERR(page)) { 202 if (!IS_ERR(page)) {
203 kmap(page); 203 kmap(page);
204 if (!PageChecked(page)) 204 if (unlikely(!PageChecked(page))) {
205 ext2_check_page(page, quiet); 205 if (PageError(page) || !ext2_check_page(page, quiet))
206 if (PageError(page)) 206 goto fail;
207 goto fail; 207 }
208 } 208 }
209 return page; 209 return page;
210 210