Re: [PATCH v2] btrfs: free unlinked replace target on initialization failure
Qu Wenruo <[email protected]>
| Newsgroups | org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
在 2026/8/8 15:43, Guanghui Yang 写道: > btrfs_init_dev_replace_tgtdev() allocates the replacement target > before looking up its dev_t and initializing its zoned device > information. If either lookup_bdev() or btrfs_get_dev_zone_info() > fails, the device has not been linked into fs_devices->devices yet, > but the error path only drops the block device file reference. > > Free the allocated device on this error path to release its name, > allocation state, zone info, and the device itself. > > The issue was found by a failure-path metadata residual analyzer and > verified with targeted failure injection on v6.14. > > Assisted-by: Codex:gpt-5 > Signed-off-by: Guanghui Yang <[email protected]> Reviewed-by: Qu Wenruo <[email protected]> Pushed to for-next branch. > --- > fs/btrfs/dev-replace.c | 10 +++++++--- > fs/btrfs/volumes.c | 2 +- > fs/btrfs/volumes.h | 1 + > 3 files changed, 9 insertions(+), 4 deletions(-) > > diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c > index 318ddb790..3762429b5 100644 > --- a/fs/btrfs/dev-replace.c > +++ b/fs/btrfs/dev-replace.c > @@ -235,7 +235,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, > struct btrfs_device **device_out) > { > struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; > - struct btrfs_device *device; > + struct btrfs_device *device = NULL; > + struct btrfs_device *tmp_device; > struct file *bdev_file; > struct block_device *bdev; > u64 devid = BTRFS_DEV_REPLACE_DEVID; > @@ -264,8 +265,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, > > sync_blockdev(bdev); > > - list_for_each_entry(device, &fs_devices->devices, dev_list) { > - if (device->bdev == bdev) { > + list_for_each_entry(tmp_device, &fs_devices->devices, dev_list) { > + if (tmp_device->bdev == bdev) { > btrfs_err(fs_info, > "target device is in the filesystem!"); > ret = -EEXIST; > @@ -285,6 +286,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, > device = btrfs_alloc_device(NULL, &devid, NULL, device_path); > if (IS_ERR(device)) { > ret = PTR_ERR(device); > + device = NULL; > goto error; > } > > @@ -327,6 +329,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, > return 0; > > error: > + if (device) > + btrfs_free_device(device); > bdev_fput(bdev_file); > return ret; > } > diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c > index a8e27db8e..c479f268a 100644 > --- a/fs/btrfs/volumes.c > +++ b/fs/btrfs/volumes.c > @@ -402,7 +402,7 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid) > return fs_devs; > } > > -static void btrfs_free_device(struct btrfs_device *device) > +void btrfs_free_device(struct btrfs_device *device) > { > WARN_ON(!list_empty(&device->post_commit_list)); > /* > diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h > index eaf23c0dc..ecab3ce3c 100644 > --- a/fs/btrfs/volumes.h > +++ b/fs/btrfs/volumes.h > @@ -795,6 +795,7 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans); > void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_device *srcdev); > void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev); > void btrfs_destroy_dev_replace_tgtdev(struct btrfs_device *tgtdev); > +void btrfs_free_device(struct btrfs_device *device); > unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info, > u64 logical); > u64 btrfs_calc_stripe_length(const struct btrfs_chunk_map *map);