btrfs: deny freezing a device while it is being added

btrfs_init_new_device() opens and claims the new device on a live
superblock without holding the write count, so a bdev_freeze() racing the
window between the claim being published and the device becoming a member
could freeze the filesystem through a claim the add may still abort and tear
down.

Add btrfs_open_device_deny_freeze(): it opens the device once
non-exclusively to take the freeze deny, then claims it by the same dev_t,
so the holder is only ever published while the device is already
unfreezable.  Keep it denied until the add is durable: bdev_allow_freeze()
on each success return (the device is now a committed member),
btrfs_release_device_allow_freeze() on the error unwind.  The deny spans the
whole add, including the seeding tail whose late failures still release the
device.  A device already frozen when the add starts is refused with -EBUSY.

Link: https://patch.msgid.link/20260616-work-super-freeze_deny_upstream-v2-4-b3567c7f994b@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-06-16 13:58:17 +02:00
parent ae5067f153
commit 48b1cc0ac9
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
2 changed files with 43 additions and 5 deletions

View File

@ -2865,6 +2865,37 @@ static int btrfs_finish_sprout(struct btrfs_trans_handle *trans)
return 0;
}
/*
* Open @path for @sb with freezing denied before the holder claim is published,
* so a racing bdev_freeze() can never reach a claim a device add or replace may
* still abort. The deny is taken on a throwaway non-holder probe open, then the
* holder is opened by the probe's dev_t. Balanced by the caller.
*/
struct file *btrfs_open_device_deny_freeze(const char *path,
struct super_block *sb)
{
struct file *probe_file, *bdev_file;
int ret;
/* WRITE so bdev_file_open_by_path() rejects a read-only device. */
probe_file = bdev_file_open_by_path(path, BLK_OPEN_WRITE, NULL, NULL);
if (IS_ERR(probe_file))
return probe_file;
ret = bdev_deny_freeze(file_bdev(probe_file));
if (ret) {
bdev_fput(probe_file);
return ERR_PTR(ret);
}
bdev_file = bdev_file_open_by_dev(file_bdev(probe_file)->bd_dev,
BLK_OPEN_WRITE, sb, &fs_holder_ops);
if (IS_ERR(bdev_file))
bdev_allow_freeze(file_bdev(probe_file));
bdev_fput(probe_file);
return bdev_file;
}
int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path)
{
struct btrfs_root *root = fs_info->dev_root;
@ -2883,8 +2914,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
if (sb_rdonly(sb) && !fs_devices->seeding)
return -EROFS;
bdev_file = bdev_file_open_by_path(device_path, BLK_OPEN_WRITE,
fs_info->sb, &fs_holder_ops);
/* Forbid freezing until the device is a committed member (or unwound). */
bdev_file = btrfs_open_device_deny_freeze(device_path, fs_info->sb);
if (IS_ERR(bdev_file))
return PTR_ERR(bdev_file);
@ -3055,8 +3086,10 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
up_write(&sb->s_umount);
locked = false;
if (ret) /* transaction commit */
if (ret) { /* transaction commit */
bdev_allow_freeze(file_bdev(bdev_file));
return ret;
}
ret = btrfs_relocate_sys_chunks(fs_info);
if (ret < 0)
@ -3064,8 +3097,10 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
"Failed to relocate sys chunks after device initialization. This can be fixed using the \"btrfs balance\" command.");
trans = btrfs_attach_transaction(root);
if (IS_ERR(trans)) {
if (PTR_ERR(trans) == -ENOENT)
if (PTR_ERR(trans) == -ENOENT) {
bdev_allow_freeze(file_bdev(bdev_file));
return 0;
}
ret = PTR_ERR(trans);
trans = NULL;
goto error_sysfs;
@ -3085,6 +3120,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
/* Update ctime/mtime for blkid or udev */
update_dev_time(device_path);
bdev_allow_freeze(file_bdev(bdev_file));
return ret;
error_sysfs:
@ -3114,7 +3150,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
error_free_device:
btrfs_free_device(device);
error:
bdev_fput(bdev_file);
btrfs_release_device_allow_freeze(bdev_file);
if (locked) {
mutex_unlock(&uuid_mutex);
up_write(&sb->s_umount);

View File

@ -769,6 +769,8 @@ struct btrfs_device *btrfs_find_device(const struct btrfs_fs_devices *fs_devices
const struct btrfs_dev_lookup_args *args);
int btrfs_shrink_device(struct btrfs_device *device, u64 new_size);
int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *path);
struct file *btrfs_open_device_deny_freeze(const char *path,
struct super_block *sb);
int btrfs_balance(struct btrfs_fs_info *fs_info,
struct btrfs_balance_control *bctl,
struct btrfs_ioctl_balance_args *bargs);