btrfs: always update/create the dev stats item when adding a new device

[MINOR PROBLEM]
When adding a new btrfs device, the corresponding DEV_STATS item creation
can only triggered by a mount cycle if there is no other error
triggered:

  # mkfs.btrfs -f $dev1 $mnt
  # mount $dev1 $mnt
  # btrfs dev add $dev2 $mnt
  # sync
  # btrfs ins dump-tree -t dev $dev1
  device tree key (DEV_TREE ROOT_ITEM 0)
  leaf 30588928 items 6 free space 15853 generation 9 owner DEV_TREE
         item 0 key (DEV_STATS PERSISTENT_ITEM 1) itemoff 16243 itemsize 40 <<<
         	persistent item objectid DEV_STATS offset 1
         	device stats
         	write_errs 0 read_errs 0 flush_errs 0 corruption_errs 0 generation 0
         item 1 key (1 DEV_EXTENT 13631488) itemoff 16195 itemsize 48

Only after a mount cycle and a new transaction, the DEV_STATS for devid
2 can show up:

  # umount $mnt
  # mount $dev1 $mnt
  # touch $mnt
  # sync
  # btrfs ins dump-tree -t dev $dev1
  device tree key (DEV_TREE ROOT_ITEM 0)
  leaf 30605312 items 7 free space 15788 generation 10 owner DEV_TREE
         item 0 key (DEV_STATS PERSISTENT_ITEM 1) itemoff 16243 itemsize 40
         	persistent item objectid DEV_STATS offset 1
         	device stats
         	write_errs 0 read_errs 0 flush_errs 0 corruption_errs 0 generation 0
         item 1 key (DEV_STATS PERSISTENT_ITEM 2) itemoff 16203 itemsize 40
         	persistent item objectid DEV_STATS offset 2
         	device stats
         	write_errs 0 read_errs 0 flush_errs 0 corruption_errs 0 generation 0

[CAUSE]
Btrfs only updates the DEV_STATS item when the device->dev_stats_ccnt
counter is not 0.

This is to reduce COW for the device tree. However that dev_stats_ccnt is
only increased at the following call sites:

- btrfs_dev_stat_inc()
  This happens when some IO error happened.

- btrfs_dev_stat_read_and_reset()
  This happens for GET_DEV_STATS ioctl with BTRFS_DEV_STATS_RESET flag.

- btrfs_dev_stat_set()
  This happens inside btrfs_device_init_dev_stats().

So when a new device is added, its dev_stats_ccnt is just initialized to
0, and btrfs won't create nor update the corresponding DEV_STATS item at
all.

[ENHANCEMENT]
When a new device is added, also increase the dev_stats_ccnt by one.
This includes both device add ioctl and dev-replace.

This will force btrfs to create a new DEV_STATS item or update the
existing one with the correct values.

This not only makes the DEV_STATS creation early, but also prevents
old DEV_STATS left from older kernels to cause false alerts for the
newly added device.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo
2026-04-07 19:04:00 +09:30
committed by Johannes Thumshirn
parent 052273b63d
commit 923546d3c3
2 changed files with 8 additions and 0 deletions

View File

@@ -307,6 +307,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
device->bdev_file = bdev_file;
set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state);
set_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state);
/* Check the comment in btrfs_init_new_device() for the reason. */
atomic_inc(&device->dev_stats_ccnt);
device->dev_stats_valid = 1;
set_blocksize(bdev_file, BTRFS_BDEV_BLOCKSIZE);
device->fs_devices = fs_devices;

View File

@@ -2927,6 +2927,12 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
device->commit_total_bytes = device->total_bytes;
set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state);
clear_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state);
/*
* Increase dev_stats_ccnt so that corresponding DEV_STATS item can be
* created at the next transaction commit.
*/
atomic_inc(&device->dev_stats_ccnt);
device->dev_stats_valid = 1;
set_blocksize(device->bdev_file, BTRFS_BDEV_BLOCKSIZE);