diff mbox series

[v2] btrfs: pseudo device-scan for single-device filesystems

Message ID de8d71b1b08f2c6ce75e3c45ee801659ecd4dc43.1694164368.git.anand.jain@oracle.com (mailing list archive)
State New, archived
Headers show
Series [v2] btrfs: pseudo device-scan for single-device filesystems | expand

Commit Message

Anand Jain Sept. 8, 2023, 4:31 p.m. UTC
After the commit 5f58d783fd78 ("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.

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>
---
Needs fstests fix in the mailing list:
  [PATCH] fstests: btrfs/185 update for single device pseudo device-scan

V2:
. Commit log updated
. Handle 'device == NULL' separately.
. Convert 'pr_info()' to 'pr_debug()'.
. Btrfs/245 test_forget() was failing because it checked if the scan was
  successful by calling 'forget' and ensuring it returned success. As we
  aren't actually scanning, similarly do the same in forget aswell check
  if device present free it, and always return success.

 fs/btrfs/super.c   | 17 ++++++++++++-----
 fs/btrfs/volumes.c | 21 ++++++++++++++++++++-
 fs/btrfs/volumes.h |  3 ++-
 3 files changed, 34 insertions(+), 7 deletions(-)

Comments

David Sterba Sept. 11, 2023, 5:36 p.m. UTC | #1
On Sat, Sep 09, 2023 at 12:31:55AM +0800, Anand Jain wrote:
> After the commit 5f58d783fd78 ("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.
> 
> 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>
> ---
> Needs fstests fix in the mailing list:
>   [PATCH] fstests: btrfs/185 update for single device pseudo device-scan
> 
> V2:
> . Commit log updated
> . Handle 'device == NULL' separately.
> . Convert 'pr_info()' to 'pr_debug()'.
> . Btrfs/245 test_forget() was failing because it checked if the scan was
>   successful by calling 'forget' and ensuring it returned success. As we
>   aren't actually scanning, similarly do the same in forget aswell check
>   if device present free it, and always return success.
> 
>  fs/btrfs/super.c   | 17 ++++++++++++-----
>  fs/btrfs/volumes.c | 21 ++++++++++++++++++++-
>  fs/btrfs/volumes.h |  3 ++-
>  3 files changed, 34 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index 32ff441d2c13..39be36096640 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -891,7 +891,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);
> @@ -1486,7 +1486,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);
> +	device = btrfs_scan_one_device(device_name, mode, true);
> +	/*
> +	 * As we passed 'true' in 3rd the argument, we need proper error code,
> +	 * not null.
> +	 */
> +	ASSERT(device != NULL);
>  	if (IS_ERR(device)) {
>  		mutex_unlock(&uuid_mutex);
>  		error = PTR_ERR(device);
> @@ -2199,7 +2204,8 @@ 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);
> +		device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
> +		/* Return success i.e. 0 for device == NULL */
>  		ret = PTR_ERR_OR_ZERO(device);
>  		mutex_unlock(&uuid_mutex);
>  		break;
> @@ -2213,9 +2219,10 @@ 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)) {
> +		device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
> +		if (IS_ERR_OR_NULL(device)) {
>  			mutex_unlock(&uuid_mutex);
> +			/* Return success i.e. 0 for device == NULL */
>  			ret = PTR_ERR(device);

Should this also be PTR_ERR_OR_ZERO(device) like in the other case?

>  			break;
>  		}
diff mbox series

Patch

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 32ff441d2c13..39be36096640 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -891,7 +891,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);
@@ -1486,7 +1486,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);
+	device = btrfs_scan_one_device(device_name, mode, true);
+	/*
+	 * As we passed 'true' in 3rd the argument, we need proper error code,
+	 * not null.
+	 */
+	ASSERT(device != NULL);
 	if (IS_ERR(device)) {
 		mutex_unlock(&uuid_mutex);
 		error = PTR_ERR(device);
@@ -2199,7 +2204,8 @@  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);
+		device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
+		/* Return success i.e. 0 for device == NULL */
 		ret = PTR_ERR_OR_ZERO(device);
 		mutex_unlock(&uuid_mutex);
 		break;
@@ -2213,9 +2219,10 @@  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)) {
+		device = btrfs_scan_one_device(vol->name, BLK_OPEN_READ, false);
+		if (IS_ERR_OR_NULL(device)) {
 			mutex_unlock(&uuid_mutex);
+			/* Return success i.e. 0 for device == NULL */
 			ret = PTR_ERR(device);
 			break;
 		}
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 63bd5f47c698..07e3f9c2bfa5 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1218,7 +1218,8 @@  int btrfs_forget_devices(dev_t devt)
  * and we are not allowed to call set_blocksize during the scan. The superblock
  * is read via pagecache
  */
-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;
@@ -1263,10 +1264,28 @@  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);
+		}
+		btrfs_free_stale_devices(devt, NULL);
+
+		pr_debug("BTRFS (%s) skip registering single non seed device\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:
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index d2a04ede41dd..e4a3470814c5 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -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);