Skip to content

Commit 492888d

Browse files
jankaratytso
authored andcommitted
ext4: fix data races when using cached status extents
When using cached extent stored in extent status tree in tree->cache_es another process holding ei->i_es_lock for reading can be racing with us setting new value of tree->cache_es. If the compiler would decide to refetch tree->cache_es at an unfortunate moment, it could result in a bogus in_range() check. Fix the possible race by using READ_ONCE() when using tree->cache_es only under ei->i_es_lock for reading. Cc: [email protected] Reported-by: [email protected] Link: https://lore.kernel.org/all/[email protected] Suggested-by: Dmitry Vyukov <[email protected]> Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 00d873c commit 492888d

File tree

1 file changed

+13
-17
lines changed

1 file changed

+13
-17
lines changed

fs/ext4/extents_status.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,12 @@ static void __es_find_extent_range(struct inode *inode,
267267

268268
/* see if the extent has been cached */
269269
es->es_lblk = es->es_len = es->es_pblk = 0;
270-
if (tree->cache_es) {
271-
es1 = tree->cache_es;
272-
if (in_range(lblk, es1->es_lblk, es1->es_len)) {
273-
es_debug("%u cached by [%u/%u) %llu %x\n",
274-
lblk, es1->es_lblk, es1->es_len,
275-
ext4_es_pblock(es1), ext4_es_status(es1));
276-
goto out;
277-
}
270+
es1 = READ_ONCE(tree->cache_es);
271+
if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
272+
es_debug("%u cached by [%u/%u) %llu %x\n",
273+
lblk, es1->es_lblk, es1->es_len,
274+
ext4_es_pblock(es1), ext4_es_status(es1));
275+
goto out;
278276
}
279277

280278
es1 = __es_tree_search(&tree->root, lblk);
@@ -293,7 +291,7 @@ static void __es_find_extent_range(struct inode *inode,
293291
}
294292

295293
if (es1 && matching_fn(es1)) {
296-
tree->cache_es = es1;
294+
WRITE_ONCE(tree->cache_es, es1);
297295
es->es_lblk = es1->es_lblk;
298296
es->es_len = es1->es_len;
299297
es->es_pblk = es1->es_pblk;
@@ -931,14 +929,12 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
931929

932930
/* find extent in cache firstly */
933931
es->es_lblk = es->es_len = es->es_pblk = 0;
934-
if (tree->cache_es) {
935-
es1 = tree->cache_es;
936-
if (in_range(lblk, es1->es_lblk, es1->es_len)) {
937-
es_debug("%u cached by [%u/%u)\n",
938-
lblk, es1->es_lblk, es1->es_len);
939-
found = 1;
940-
goto out;
941-
}
932+
es1 = READ_ONCE(tree->cache_es);
933+
if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
934+
es_debug("%u cached by [%u/%u)\n",
935+
lblk, es1->es_lblk, es1->es_len);
936+
found = 1;
937+
goto out;
942938
}
943939

944940
node = tree->root.rb_node;

0 commit comments

Comments
 (0)