diff mbox series

btrfs: harden identification of the stale device

Message ID 166e39f69a8693e5fe10784cdbd950d68f98d4fb.1638953372.git.anand.jain@oracle.com (mailing list archive)
State New, archived
Headers show
Series btrfs: harden identification of the stale device | expand

Commit Message

Anand Jain Dec. 8, 2021, 2:05 p.m. UTC
Identifying and removing the stale device from the fs_uuids list is done
by the function btrfs_free_stale_devices().
btrfs_free_stale_devices() in turn depends on the function
device_path_matched() to check if the device repeats in more than one
btrfs_device structure.

The matching of the device happens by its path, the device path. However,
when dm mapper is in use, the dm device paths are nothing but a link to
the actual block device, which leads to the device_path_matched() failing
to match.

Fix this by matching the dev_t as provided by lookup_bdev() instead of
plain strcmp() the device paths.

Reported-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
This patch should go to Stable-5.4.y and up but, there is a conflict.
I will send a separate patch for the stable.

 fs/btrfs/volumes.c | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

Comments

Josef Bacik Dec. 8, 2021, 2:29 p.m. UTC | #1
On Wed, Dec 08, 2021 at 10:05:29PM +0800, Anand Jain wrote:
> Identifying and removing the stale device from the fs_uuids list is done
> by the function btrfs_free_stale_devices().
> btrfs_free_stale_devices() in turn depends on the function
> device_path_matched() to check if the device repeats in more than one
> btrfs_device structure.
> 
> The matching of the device happens by its path, the device path. However,
> when dm mapper is in use, the dm device paths are nothing but a link to
> the actual block device, which leads to the device_path_matched() failing
> to match.
> 
> Fix this by matching the dev_t as provided by lookup_bdev() instead of
> plain strcmp() the device paths.
> 
> Reported-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: Anand Jain <anand.jain@oracle.com>

We already have the bdev for the device in most of the callers here, the only
exception is btrfs_forget_devices.  Can we change this up to pass in the dev_t
of the device we're trying to remove, that way we don't have to do the lookup
over and over for the path of the device we're trying to forget?  Thanks,

Josef
kernel test robot Dec. 9, 2021, 3:59 a.m. UTC | #2
Hi Anand,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.16-rc4 next-20211208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Anand-Jain/btrfs-harden-identification-of-the-stale-device/20211208-220757
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: i386-randconfig-s001-20211207 (https://download.01.org/0day-ci/archive/20211209/202112091123.ETjD5KQj-lkp@intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.4-dirty
        # https://github.com/0day-ci/linux/commit/03b597640967afb3d37dc37f0a685fed95594b83
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Anand-Jain/btrfs-harden-identification-of-the-stale-device/20211208-220757
        git checkout 03b597640967afb3d37dc37f0a685fed95594b83
        # save the config file to linux build tree
        mkdir build_dir
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)
   fs/btrfs/volumes.c:402:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct rcu_string *str @@     got struct rcu_string [noderef] __rcu *name @@
   fs/btrfs/volumes.c:402:31: sparse:     expected struct rcu_string *str
   fs/btrfs/volumes.c:402:31: sparse:     got struct rcu_string [noderef] __rcu *name
>> fs/btrfs/volumes.c:549:35: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *pathname @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:549:35: sparse:     expected char const *pathname
   fs/btrfs/volumes.c:549:35: sparse:     got char [noderef] __rcu *
   fs/btrfs/volumes.c:643:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:643:43: sparse:     expected char const *device_path
   fs/btrfs/volumes.c:643:43: sparse:     got char [noderef] __rcu *
   fs/btrfs/volumes.c:904:50: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *cs @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:904:50: sparse:     expected char const *cs
   fs/btrfs/volumes.c:904:50: sparse:     got char [noderef] __rcu *
   fs/btrfs/volumes.c:984:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct rcu_string *str @@     got struct rcu_string [noderef] __rcu *name @@
   fs/btrfs/volumes.c:984:39: sparse:     expected struct rcu_string *str
   fs/btrfs/volumes.c:984:39: sparse:     got struct rcu_string [noderef] __rcu *name
   fs/btrfs/volumes.c:1040:58: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *src @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:1040:58: sparse:     expected char const *src
   fs/btrfs/volumes.c:1040:58: sparse:     got char [noderef] __rcu *
   fs/btrfs/volumes.c:2235:49: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:2235:49: sparse:     expected char const *device_path
   fs/btrfs/volumes.c:2235:49: sparse:     got char [noderef] __rcu *
   fs/btrfs/volumes.c:2350:41: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
   fs/btrfs/volumes.c:2350:41: sparse:     expected char const *device_path
   fs/btrfs/volumes.c:2350:41: sparse:     got char [noderef] __rcu *

vim +549 fs/btrfs/volumes.c

   532	
   533	/*
   534	 * Check if the device in the 'path' matches with the device in the given
   535	 * struct btrfs_device '*device'.
   536	 * Returns:
   537	 *	0	If it is the same device.
   538	 *	1	If it is not the same device.
   539	 *	-errno	For error.
   540	 */
   541	static int device_matched(struct btrfs_device *device, const char *path)
   542	{
   543		dev_t dev_old;
   544		dev_t dev_new;
   545		int error;
   546	
   547		lockdep_assert_held(&device->fs_devices->device_list_mutex);
   548		/* rcu is not required as we are inside the device_list_mutex */
 > 549		error = lookup_bdev(device->name->str, &dev_old);
   550		if (error)
   551			return error;
   552	
   553		error = lookup_bdev(path, &dev_new);
   554		if (error)
   555			return error;
   556	
   557		if (dev_old == dev_new)
   558			return 0;
   559	
   560		return 1;
   561	}
   562	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Anand Jain Dec. 9, 2021, 6:28 a.m. UTC | #3
> sparse warnings: (new ones prefixed by >>)

  The new Warning that this patch created is the same as the other 7 old 
Warnings. It is all about how we have used the device:name.

  I will fix the new Warning. Looks like we need to fix the older 
Warning as well.

Thanks, Anand


>     fs/btrfs/volumes.c:402:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct rcu_string *str @@     got struct rcu_string [noderef] __rcu *name @@
>     fs/btrfs/volumes.c:402:31: sparse:     expected struct rcu_string *str
>     fs/btrfs/volumes.c:402:31: sparse:     got struct rcu_string [noderef] __rcu *name

         rcu_string_free(device->name);


>>> fs/btrfs/volumes.c:549:35: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *pathname @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:549:35: sparse:     expected char const *pathname
>     fs/btrfs/volumes.c:549:35: sparse:     got char [noderef] __rcu *


         error = lookup_bdev(device->name->str, &dev_old);


>     fs/btrfs/volumes.c:643:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:643:43: sparse:     expected char const *device_path
>     fs/btrfs/volumes.c:643:43: sparse:     got char [noderef] __rcu *

         ret = btrfs_get_bdev_and_sb(device->name->str, flags, holder, 1,
                                     &bdev, &disk_super);


>     fs/btrfs/volumes.c:904:50: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *cs @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:904:50: sparse:     expected char const *cs
>     fs/btrfs/volumes.c:904:50: sparse:     got char [noderef] __rcu *

         } else if (!device->name || strcmp(device->name->str, path)) {


>     fs/btrfs/volumes.c:984:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct rcu_string *str @@     got struct rcu_string [noderef] __rcu *name @@
>     fs/btrfs/volumes.c:984:39: sparse:     expected struct rcu_string *str
>     fs/btrfs/volumes.c:984:39: sparse:     got struct rcu_string [noderef] __rcu *name


                 rcu_string_free(device->name);


>     fs/btrfs/volumes.c:1040:58: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected char const *src @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:1040:58: sparse:     expected char const *src
>     fs/btrfs/volumes.c:1040:58: sparse:     got char [noderef] __rcu *

                         name = rcu_string_strdup(orig_dev->name->str,
                                         GFP_KERNEL);

>     fs/btrfs/volumes.c:2235:49: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:2235:49: sparse:     expected char const *device_path
>     fs/btrfs/volumes.c:2235:49: sparse:     got char [noderef] __rcu *

                 btrfs_scratch_superblocks(fs_info, device->bdev,
                                           device->name->str);

>     fs/btrfs/volumes.c:2350:41: sparse: sparse: incorrect type in argument 3 (different address spaces) @@     expected char const *device_path @@     got char [noderef] __rcu * @@
>     fs/btrfs/volumes.c:2350:41: sparse:     expected char const *device_path
>     fs/btrfs/volumes.c:2350:41: sparse:     got char [noderef] __rcu *


         btrfs_scratch_superblocks(tgtdev->fs_info, tgtdev->bdev,
                                   tgtdev->name->str);
Anand Jain Dec. 10, 2021, 12:54 p.m. UTC | #4
On 08/12/2021 22:29, Josef Bacik wrote:
> On Wed, Dec 08, 2021 at 10:05:29PM +0800, Anand Jain wrote:
>> Identifying and removing the stale device from the fs_uuids list is done
>> by the function btrfs_free_stale_devices().
>> btrfs_free_stale_devices() in turn depends on the function
>> device_path_matched() to check if the device repeats in more than one
>> btrfs_device structure.
>>
>> The matching of the device happens by its path, the device path. However,
>> when dm mapper is in use, the dm device paths are nothing but a link to
>> the actual block device, which leads to the device_path_matched() failing
>> to match.
>>
>> Fix this by matching the dev_t as provided by lookup_bdev() instead of
>> plain strcmp() the device paths.
>>
>> Reported-by: Josef Bacik <josef@toxicpanda.com>
>> Signed-off-by: Anand Jain <anand.jain@oracle.com>
> 
> We already have the bdev for the device in most of the callers here, the only
> exception is btrfs_forget_devices.  Can we change this up to pass in the dev_t
> of the device we're trying to remove, that way we don't have to do the lookup
> over and over for the path of the device we're trying to forget?  Thanks,

Right. I have made the related changes and, in v2, this change is on top 
of this patch.

Thanks, Anand


> 
> Josef
diff mbox series

Patch

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 553ee97078f6..cedadc81c851 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -535,15 +535,34 @@  btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder,
 	return ret;
 }
 
-static bool device_path_matched(const char *path, struct btrfs_device *device)
+/*
+ * Check if the device in the 'path' matches with the device in the given
+ * struct btrfs_device '*device'.
+ * Returns:
+ *	0	If it is the same device.
+ *	1	If it is not the same device.
+ *	-errno	For error.
+ */
+static int device_matched(struct btrfs_device *device, const char *path)
 {
-	int found;
+	dev_t dev_old;
+	dev_t dev_new;
+	int error;
 
-	rcu_read_lock();
-	found = strcmp(rcu_str_deref(device->name), path);
-	rcu_read_unlock();
+	lockdep_assert_held(&device->fs_devices->device_list_mutex);
+	/* rcu is not required as we are inside the device_list_mutex */
+	error = lookup_bdev(device->name->str, &dev_old);
+	if (error)
+		return error;
 
-	return found == 0;
+	error = lookup_bdev(path, &dev_new);
+	if (error)
+		return error;
+
+	if (dev_old == dev_new)
+		return 0;
+
+	return 1;
 }
 
 /*
@@ -578,7 +597,7 @@  static int btrfs_free_stale_devices(const char *path,
 				continue;
 			if (path && !device->name)
 				continue;
-			if (path && !device_path_matched(path, device))
+			if (path && device_matched(device, path) != 0)
 				continue;
 			if (fs_devices->opened) {
 				/* for an already deleted device return 0 */