btrfs: scan but don't register device on single device filesystem

After the commit 5f58d783fd ("btrfs: free device in btrfs_close_devices
for a single device filesystem") we unregister the device from the kernel
memory upon unmounting for a single device.

So, device registration that was performed before mounting if any is no
longer in the kernel memory.

However, in fact, note that device registration is unnecessary for a
single-device btrfs filesystem unless it's a seed device.

So for commands like 'btrfs device scan' or 'btrfs device ready' with a
non-seed single-device btrfs filesystem, they can return success just
after superblock verification and without the actual device scan.  When
'device scan --forget' is called on such device no error is returned.

The seed device must remain in the kernel memory to allow the sprout
device to mount without the need to specify the seed device explicitly.

Signed-off-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Anand Jain 2023-09-09 00:31:55 +08:00 committed by David Sterba
parent ed164802e8
commit bc27d6f0aa
3 changed files with 54 additions and 15 deletions

View File

@ -846,7 +846,7 @@ static int btrfs_parse_device_options(const char *options, blk_mode_t flags)
error = -ENOMEM;
goto out;
}
device = btrfs_scan_one_device(device_name, flags);
device = btrfs_scan_one_device(device_name, flags, false);
kfree(device_name);
if (IS_ERR(device)) {
error = PTR_ERR(device);
@ -1432,7 +1432,12 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
goto error_fs_info;
}
device = btrfs_scan_one_device(device_name, mode);
/*
* With 'true' passed to btrfs_scan_one_device() (mount time) we expect
* either a valid device or an error.
*/
device = btrfs_scan_one_device(device_name, mode, true);
ASSERT(device != NULL);
if (IS_ERR(device)) {
mutex_unlock(&uuid_mutex);
error = PTR_ERR(device);
@ -2144,7 +2149,11 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case BTRFS_IOC_SCAN_DEV:
mutex_lock(&uuid_mutex);
device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ);
/*
* Scanning outside of mount can return NULL which would turn
* into 0 error code.
*/
device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
ret = PTR_ERR_OR_ZERO(device);
mutex_unlock(&uuid_mutex);
break;
@ -2158,8 +2167,12 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
break;
case BTRFS_IOC_DEVICES_READY:
mutex_lock(&uuid_mutex);
device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ);
if (IS_ERR(device)) {
/*
* Scanning outside of mount can return NULL which would turn
* into 0 error code.
*/
device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
if (IS_ERR_OR_NULL(device)) {
mutex_unlock(&uuid_mutex);
ret = PTR_ERR(device);
break;

View File

@ -559,13 +559,13 @@ static int btrfs_free_stale_devices(dev_t devt, struct btrfs_device *skip_device
{
struct btrfs_fs_devices *fs_devices, *tmp_fs_devices;
struct btrfs_device *device, *tmp_device;
int ret = 0;
int ret;
bool freed = false;
lockdep_assert_held(&uuid_mutex);
if (devt)
ret = -ENOENT;
/* Return good status if there is no instance of devt. */
ret = 0;
list_for_each_entry_safe(fs_devices, tmp_fs_devices, &fs_uuids, fs_list) {
mutex_lock(&fs_devices->device_list_mutex);
@ -576,8 +576,7 @@ static int btrfs_free_stale_devices(dev_t devt, struct btrfs_device *skip_device
if (devt && devt != device->devt)
continue;
if (fs_devices->opened) {
/* for an already deleted device return 0 */
if (devt && ret != 0)
if (devt)
ret = -EBUSY;
break;
}
@ -587,7 +586,7 @@ static int btrfs_free_stale_devices(dev_t devt, struct btrfs_device *skip_device
list_del(&device->dev_list);
btrfs_free_device(device);
ret = 0;
freed = true;
}
mutex_unlock(&fs_devices->device_list_mutex);
@ -598,6 +597,10 @@ static int btrfs_free_stale_devices(dev_t devt, struct btrfs_device *skip_device
}
}
/* If there is at least one freed device return 0. */
if (freed)
return 0;
return ret;
}
@ -1356,9 +1359,14 @@ int btrfs_forget_devices(dev_t devt)
/*
* Look for a btrfs signature on a device. This may be called out of the mount path
* and we are not allowed to call set_blocksize during the scan. The superblock
* is read via pagecache
* is read via pagecache.
*
* With @mount_arg_dev it's a scan during mount time that will always register
* the device or return an error. Multi-device and seeding devices are registered
* in both cases.
*/
struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags)
struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
bool mount_arg_dev)
{
struct btrfs_super_block *disk_super;
bool new_device_added = false;
@ -1403,10 +1411,27 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags)
goto error_bdev_put;
}
if (!mount_arg_dev && btrfs_super_num_devices(disk_super) == 1 &&
!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING)) {
dev_t devt;
ret = lookup_bdev(path, &devt);
if (ret)
btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
path, ret);
else
btrfs_free_stale_devices(devt, NULL);
pr_debug("BTRFS: skip registering single non-seed device %s\n", path);
device = NULL;
goto free_disk_super;
}
device = device_list_add(path, disk_super, &new_device_added);
if (!IS_ERR(device) && new_device_added)
btrfs_free_stale_devices(device->devt, device);
free_disk_super:
btrfs_release_disk_super(disk_super);
error_bdev_put:

View File

@ -619,7 +619,8 @@ struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans,
void btrfs_mapping_tree_free(struct extent_map_tree *tree);
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
blk_mode_t flags, void *holder);
struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags);
struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
bool mount_arg_dev);
int btrfs_forget_devices(dev_t devt);
void btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices);