Skip to content

Commit 4a4f8fe

Browse files
fdmananakdave
authored andcommitted
btrfs: add and use helpers for reading and writing fs_info->generation
Currently the generation field of struct btrfs_fs_info is always modified while holding fs_info->trans_lock locked. Most readers will access this field without taking that lock but while holding a transaction handle, which is safe to do due to the transaction life cycle. However there are other readers that are neither holding the lock nor holding a transaction handle open: 1) When reading an inode from disk, at btrfs_read_locked_inode(); 2) When reading the generation to expose it to sysfs, at btrfs_generation_show(); 3) Early in the fsync path, at skip_inode_logging(); 4) When creating a hole at btrfs_cont_expand(), during write paths, truncate and reflinking; 5) In the fs_info ioctl (btrfs_ioctl_fs_info()); 6) While mounting the filesystem, in the open_ctree() path. In these cases it's safe to directly read fs_info->generation as no one can concurrently start a transaction and update fs_info->generation. In case of the fsync path, races here should be harmless, and in the worst case they may cause a fsync to log an inode when it's not really needed, so nothing bad from a functional perspective. In the other cases it's not so clear if functional problems may arise, though in case 1 rare things like a load/store tearing [1] may cause the BTRFS_INODE_NEEDS_FULL_SYNC flag not being set on an inode and therefore result in incorrect logging later on in case a fsync call is made. To avoid data race warnings from tools like KCSAN and other issues such as load and store tearing (amongst others, see [1]), create helpers to access the generation field of struct btrfs_fs_info using READ_ONCE() and WRITE_ONCE(), and use these helpers where needed. [1] https://lwn.net/Articles/793253/ Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 6008859 commit 4a4f8fe

6 files changed

Lines changed: 22 additions & 6 deletions

File tree

fs/btrfs/file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1749,7 +1749,7 @@ static inline bool skip_inode_logging(const struct btrfs_log_ctx *ctx)
17491749
struct btrfs_inode *inode = BTRFS_I(ctx->inode);
17501750
struct btrfs_fs_info *fs_info = inode->root->fs_info;
17511751

1752-
if (btrfs_inode_in_log(inode, fs_info->generation) &&
1752+
if (btrfs_inode_in_log(inode, btrfs_get_fs_generation(fs_info)) &&
17531753
list_empty(&ctx->ordered_extents))
17541754
return true;
17551755

fs/btrfs/fs.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,12 @@ struct btrfs_fs_info {
416416

417417
struct btrfs_block_rsv empty_block_rsv;
418418

419+
/*
420+
* Updated while holding the lock 'trans_lock'. Due to the life cycle of
421+
* a transaction, it can be directly read while holding a transaction
422+
* handle, everywhere else must be read with btrfs_get_fs_generation().
423+
* Should always be updated using btrfs_set_fs_generation().
424+
*/
419425
u64 generation;
420426
u64 last_trans_committed;
421427
/*
@@ -817,6 +823,16 @@ struct btrfs_fs_info {
817823
#endif
818824
};
819825

826+
static inline u64 btrfs_get_fs_generation(const struct btrfs_fs_info *fs_info)
827+
{
828+
return READ_ONCE(fs_info->generation);
829+
}
830+
831+
static inline void btrfs_set_fs_generation(struct btrfs_fs_info *fs_info, u64 gen)
832+
{
833+
WRITE_ONCE(fs_info->generation, gen);
834+
}
835+
820836
static inline void btrfs_set_last_root_drop_gen(struct btrfs_fs_info *fs_info,
821837
u64 gen)
822838
{

fs/btrfs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,7 +3800,7 @@ static int btrfs_read_locked_inode(struct inode *inode,
38003800
* This is required for both inode re-read from disk and delayed inode
38013801
* in delayed_nodes_tree.
38023802
*/
3803-
if (BTRFS_I(inode)->last_trans == fs_info->generation)
3803+
if (BTRFS_I(inode)->last_trans == btrfs_get_fs_generation(fs_info))
38043804
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
38053805
&BTRFS_I(inode)->runtime_flags);
38063806

@@ -4923,7 +4923,7 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
49234923
hole_em->orig_block_len = 0;
49244924
hole_em->ram_bytes = hole_size;
49254925
hole_em->compress_type = BTRFS_COMPRESS_NONE;
4926-
hole_em->generation = fs_info->generation;
4926+
hole_em->generation = btrfs_get_fs_generation(fs_info);
49274927

49284928
err = btrfs_replace_extent_map_range(inode, hole_em, true);
49294929
free_extent_map(hole_em);

fs/btrfs/ioctl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2822,7 +2822,7 @@ static long btrfs_ioctl_fs_info(struct btrfs_fs_info *fs_info,
28222822
}
28232823

28242824
if (flags_in & BTRFS_FS_INFO_FLAG_GENERATION) {
2825-
fi_args->generation = fs_info->generation;
2825+
fi_args->generation = btrfs_get_fs_generation(fs_info);
28262826
fi_args->flags |= BTRFS_FS_INFO_FLAG_GENERATION;
28272827
}
28282828

fs/btrfs/sysfs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,7 @@ static ssize_t btrfs_generation_show(struct kobject *kobj,
12011201
{
12021202
struct btrfs_fs_info *fs_info = to_fs_info(kobj);
12031203

1204-
return sysfs_emit(buf, "%llu\n", fs_info->generation);
1204+
return sysfs_emit(buf, "%llu\n", btrfs_get_fs_generation(fs_info));
12051205
}
12061206
BTRFS_ATTR(, generation, btrfs_generation_show);
12071207

fs/btrfs/transaction.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ static noinline int join_transaction(struct btrfs_fs_info *fs_info,
386386
IO_TREE_TRANS_DIRTY_PAGES);
387387
extent_io_tree_init(fs_info, &cur_trans->pinned_extents,
388388
IO_TREE_FS_PINNED_EXTENTS);
389-
fs_info->generation++;
389+
btrfs_set_fs_generation(fs_info, fs_info->generation + 1);
390390
cur_trans->transid = fs_info->generation;
391391
fs_info->running_transaction = cur_trans;
392392
cur_trans->aborted = 0;

0 commit comments

Comments
 (0)