]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: add and use helpers for reading and writing fs_info->generation
authorFilipe Manana <fdmanana@suse.com>
Wed, 4 Oct 2023 10:38:50 +0000 (11:38 +0100)
committerDavid Sterba <dsterba@suse.com>
Thu, 12 Oct 2023 14:44:17 +0000 (16:44 +0200)
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>
fs/btrfs/file.c
fs/btrfs/fs.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/sysfs.c
fs/btrfs/transaction.c

index 004c53482f05fa85fb00a5b637f444a32c239a0d..723f0c70452e57d346aa0d8c6ad240edf427b321 100644 (file)
@@ -1749,7 +1749,7 @@ static inline bool skip_inode_logging(const struct btrfs_log_ctx *ctx)
        struct btrfs_inode *inode = BTRFS_I(ctx->inode);
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
-       if (btrfs_inode_in_log(inode, fs_info->generation) &&
+       if (btrfs_inode_in_log(inode, btrfs_get_fs_generation(fs_info)) &&
            list_empty(&ctx->ordered_extents))
                return true;
 
index 2bd9bedc70958067f6579bee1cc341a648d17ac9..d04b729cbdf3353970e066b5b02a8fd118b9a82c 100644 (file)
@@ -416,6 +416,12 @@ struct btrfs_fs_info {
 
        struct btrfs_block_rsv empty_block_rsv;
 
+       /*
+        * Updated while holding the lock 'trans_lock'. Due to the life cycle of
+        * a transaction, it can be directly read while holding a transaction
+        * handle, everywhere else must be read with btrfs_get_fs_generation().
+        * Should always be updated using btrfs_set_fs_generation().
+        */
        u64 generation;
        u64 last_trans_committed;
        /*
@@ -817,6 +823,16 @@ struct btrfs_fs_info {
 #endif
 };
 
+static inline u64 btrfs_get_fs_generation(const struct btrfs_fs_info *fs_info)
+{
+       return READ_ONCE(fs_info->generation);
+}
+
+static inline void btrfs_set_fs_generation(struct btrfs_fs_info *fs_info, u64 gen)
+{
+       WRITE_ONCE(fs_info->generation, gen);
+}
+
 static inline void btrfs_set_last_root_drop_gen(struct btrfs_fs_info *fs_info,
                                                u64 gen)
 {
index 3b3aec302c33c748c8c72452f48a60d0b8c7aceb..c9317c047587fe33e2a32c70d6045b3a4a8867f0 100644 (file)
@@ -3800,7 +3800,7 @@ cache_index:
         * This is required for both inode re-read from disk and delayed inode
         * in delayed_nodes_tree.
         */
-       if (BTRFS_I(inode)->last_trans == fs_info->generation)
+       if (BTRFS_I(inode)->last_trans == btrfs_get_fs_generation(fs_info))
                set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                        &BTRFS_I(inode)->runtime_flags);
 
@@ -4923,7 +4923,7 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
                        hole_em->orig_block_len = 0;
                        hole_em->ram_bytes = hole_size;
                        hole_em->compress_type = BTRFS_COMPRESS_NONE;
-                       hole_em->generation = fs_info->generation;
+                       hole_em->generation = btrfs_get_fs_generation(fs_info);
 
                        err = btrfs_replace_extent_map_range(inode, hole_em, true);
                        free_extent_map(hole_em);
index 848b7e6f6421bee35194ee88f838801ea60327d4..7ab21283fae80a342bd99504f0a2d41f249811e1 100644 (file)
@@ -2822,7 +2822,7 @@ static long btrfs_ioctl_fs_info(struct btrfs_fs_info *fs_info,
        }
 
        if (flags_in & BTRFS_FS_INFO_FLAG_GENERATION) {
-               fi_args->generation = fs_info->generation;
+               fi_args->generation = btrfs_get_fs_generation(fs_info);
                fi_args->flags |= BTRFS_FS_INFO_FLAG_GENERATION;
        }
 
index e07be193323a3118f4e53562a6d9b862df9ad79f..21ab8b9b62ce1f5c56dd6b6d579354ede9b516fe 100644 (file)
@@ -1201,7 +1201,7 @@ static ssize_t btrfs_generation_show(struct kobject *kobj,
 {
        struct btrfs_fs_info *fs_info = to_fs_info(kobj);
 
-       return sysfs_emit(buf, "%llu\n", fs_info->generation);
+       return sysfs_emit(buf, "%llu\n", btrfs_get_fs_generation(fs_info));
 }
 BTRFS_ATTR(, generation, btrfs_generation_show);
 
index 3aa59cfa4ab013f91a4c5d8f1035b04d6ef65b5c..f5db3a483f40544500a0f2a9d54f4e6096cefff6 100644 (file)
@@ -386,7 +386,7 @@ loop:
                        IO_TREE_TRANS_DIRTY_PAGES);
        extent_io_tree_init(fs_info, &cur_trans->pinned_extents,
                        IO_TREE_FS_PINNED_EXTENTS);
-       fs_info->generation++;
+       btrfs_set_fs_generation(fs_info, fs_info->generation + 1);
        cur_trans->transid = fs_info->generation;
        fs_info->running_transaction = cur_trans;
        cur_trans->aborted = 0;