Skip to content

Commit dde24c7

Browse files
fdmananakdave
authored andcommitted
btrfs: fix data race when accessing the last_trans field of a root
KCSAN complains about a data race when accessing the last_trans field of a root: [ 199.553628] BUG: KCSAN: data-race in btrfs_record_root_in_trans [btrfs] / record_root_in_trans [btrfs] [ 199.555186] read to 0x000000008801e308 of 8 bytes by task 2812 on cpu 1: [ 199.555210] btrfs_record_root_in_trans+0x9a/0x128 [btrfs] [ 199.555999] start_transaction+0x154/0xcd8 [btrfs] [ 199.556780] btrfs_join_transaction+0x44/0x60 [btrfs] [ 199.557559] btrfs_dirty_inode+0x9c/0x140 [btrfs] [ 199.558339] btrfs_update_time+0x8c/0xb0 [btrfs] [ 199.559123] touch_atime+0x16c/0x1e0 [ 199.559151] pipe_read+0x6a8/0x7d0 [ 199.559179] vfs_read+0x466/0x498 [ 199.559204] ksys_read+0x108/0x150 [ 199.559230] __s390x_sys_read+0x68/0x88 [ 199.559257] do_syscall+0x1c6/0x210 [ 199.559286] __do_syscall+0xc8/0xf0 [ 199.559318] system_call+0x70/0x98 [ 199.559431] write to 0x000000008801e308 of 8 bytes by task 2808 on cpu 0: [ 199.559464] record_root_in_trans+0x196/0x228 [btrfs] [ 199.560236] btrfs_record_root_in_trans+0xfe/0x128 [btrfs] [ 199.561097] start_transaction+0x154/0xcd8 [btrfs] [ 199.561927] btrfs_join_transaction+0x44/0x60 [btrfs] [ 199.562700] btrfs_dirty_inode+0x9c/0x140 [btrfs] [ 199.563493] btrfs_update_time+0x8c/0xb0 [btrfs] [ 199.564277] file_update_time+0xb8/0xf0 [ 199.564301] pipe_write+0x8ac/0xab8 [ 199.564326] vfs_write+0x33c/0x588 [ 199.564349] ksys_write+0x108/0x150 [ 199.564372] __s390x_sys_write+0x68/0x88 [ 199.564397] do_syscall+0x1c6/0x210 [ 199.564424] __do_syscall+0xc8/0xf0 [ 199.564452] system_call+0x70/0x98 This is because we update and read last_trans concurrently without any type of synchronization. This should be generally harmless and in the worst case it can make us do extra locking (btrfs_record_root_in_trans()) trigger some warnings at ctree.c or do extra work during relocation - this would probably only happen in case of load or store tearing. So fix this by always reading and updating the field using READ_ONCE() and WRITE_ONCE(), this silences KCSAN and prevents load and store tearing. Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent a9cc643 commit dde24c7

File tree

6 files changed

+23
-13
lines changed

6 files changed

+23
-13
lines changed

fs/btrfs/ctree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
321321
WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
322322
trans->transid != fs_info->running_transaction->transid);
323323
WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
324-
trans->transid != root->last_trans);
324+
trans->transid != btrfs_get_root_last_trans(root));
325325

326326
level = btrfs_header_level(buf);
327327
if (level == 0)
@@ -556,7 +556,7 @@ int btrfs_force_cow_block(struct btrfs_trans_handle *trans,
556556
WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
557557
trans->transid != fs_info->running_transaction->transid);
558558
WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
559-
trans->transid != root->last_trans);
559+
trans->transid != btrfs_get_root_last_trans(root));
560560

561561
level = btrfs_header_level(buf);
562562

fs/btrfs/ctree.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ static inline void btrfs_set_root_last_log_commit(struct btrfs_root *root, int c
356356
WRITE_ONCE(root->last_log_commit, commit_id);
357357
}
358358

359+
static inline u64 btrfs_get_root_last_trans(const struct btrfs_root *root)
360+
{
361+
return READ_ONCE(root->last_trans);
362+
}
363+
364+
static inline void btrfs_set_root_last_trans(struct btrfs_root *root, u64 transid)
365+
{
366+
WRITE_ONCE(root->last_trans, transid);
367+
}
368+
359369
/*
360370
* Structure that conveys information about an extent that is going to replace
361371
* all the extents in a file range.

fs/btrfs/defrag.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
139139
if (trans)
140140
transid = trans->transid;
141141
else
142-
transid = inode->root->last_trans;
142+
transid = btrfs_get_root_last_trans(root);
143143

144144
defrag = kmem_cache_zalloc(btrfs_inode_defrag_cachep, GFP_NOFS);
145145
if (!defrag)

fs/btrfs/disk-io.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
658658
root->state = 0;
659659
RB_CLEAR_NODE(&root->rb_node);
660660

661-
root->last_trans = 0;
661+
btrfs_set_root_last_trans(root, 0);
662662
root->free_objectid = 0;
663663
root->nr_delalloc_inodes = 0;
664664
root->nr_ordered_extents = 0;
@@ -1002,7 +1002,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
10021002
return ret;
10031003
}
10041004

1005-
log_root->last_trans = trans->transid;
1005+
btrfs_set_root_last_trans(log_root, trans->transid);
10061006
log_root->root_key.offset = btrfs_root_id(root);
10071007

10081008
inode_item = &log_root->root_item.inode;

fs/btrfs/relocation.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
817817
goto abort;
818818
}
819819
set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state);
820-
reloc_root->last_trans = trans->transid;
820+
btrfs_set_root_last_trans(reloc_root, trans->transid);
821821
return reloc_root;
822822
fail:
823823
kfree(root_item);
@@ -864,7 +864,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
864864
*/
865865
if (root->reloc_root) {
866866
reloc_root = root->reloc_root;
867-
reloc_root->last_trans = trans->transid;
867+
btrfs_set_root_last_trans(reloc_root, trans->transid);
868868
return 0;
869869
}
870870

@@ -1739,7 +1739,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
17391739
* btrfs_update_reloc_root() and update our root item
17401740
* appropriately.
17411741
*/
1742-
reloc_root->last_trans = trans->transid;
1742+
btrfs_set_root_last_trans(reloc_root, trans->transid);
17431743
trans->block_rsv = rc->block_rsv;
17441744

17451745
replaced = 0;
@@ -2082,7 +2082,7 @@ static int record_reloc_root_in_trans(struct btrfs_trans_handle *trans,
20822082
struct btrfs_root *root;
20832083
int ret;
20842084

2085-
if (reloc_root->last_trans == trans->transid)
2085+
if (btrfs_get_root_last_trans(reloc_root) == trans->transid)
20862086
return 0;
20872087

20882088
root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset, false);

fs/btrfs/transaction.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans,
405405
int ret = 0;
406406

407407
if ((test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
408-
root->last_trans < trans->transid) || force) {
408+
btrfs_get_root_last_trans(root) < trans->transid) || force) {
409409
WARN_ON(!force && root->commit_root != root->node);
410410

411411
/*
@@ -421,15 +421,15 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans,
421421
smp_wmb();
422422

423423
spin_lock(&fs_info->fs_roots_radix_lock);
424-
if (root->last_trans == trans->transid && !force) {
424+
if (btrfs_get_root_last_trans(root) == trans->transid && !force) {
425425
spin_unlock(&fs_info->fs_roots_radix_lock);
426426
return 0;
427427
}
428428
radix_tree_tag_set(&fs_info->fs_roots_radix,
429429
(unsigned long)btrfs_root_id(root),
430430
BTRFS_ROOT_TRANS_TAG);
431431
spin_unlock(&fs_info->fs_roots_radix_lock);
432-
root->last_trans = trans->transid;
432+
btrfs_set_root_last_trans(root, trans->transid);
433433

434434
/* this is pretty tricky. We don't want to
435435
* take the relocation lock in btrfs_record_root_in_trans
@@ -491,7 +491,7 @@ int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
491491
* and barriers
492492
*/
493493
smp_rmb();
494-
if (root->last_trans == trans->transid &&
494+
if (btrfs_get_root_last_trans(root) == trans->transid &&
495495
!test_bit(BTRFS_ROOT_IN_TRANS_SETUP, &root->state))
496496
return 0;
497497

0 commit comments

Comments
 (0)