Message ID | 29a0e54c8461e3c25e63d5b7b3e48fa6f4254d3f.1676007519.git.wqu@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs-progs: filesystem-usage: handle missing seed device properly | expand |
An alternative solution is to utilize a kernel interface to obtain the fsid [1]. Previous experiences have shown that attempting to directly read a mounted device's disk is not a reliable method and can result in various problems. As a result, it is advisable to use a kernel interface to read the fsid. [PATCH 2/2] btrfs-progs: read fsid from the sysfs in device_is_seed On 10/02/2023 13:39, Qu Wenruo wrote: > [BUG] > Test case btrfs/249 always fails since its introduction, the failure > comes from "btrfs filesystem usage" subcommand, and the error output > looks like this: > > QA output created by 249 > ERROR: unexpected number of devices: 1 >= 1 > ERROR: if seed device is used, try running this command as root > FAILED: btrfs filesystem usage, ret 1. Check btrfs.ko and btrfs-progs version. > (see /home/adam/xfstests/results//btrfs/249.full for details) > > [CAUSE] > In function load_device_info(), we only allocate enough space for all > *RW* devices, expecting we can rule out all seed devices. > > And in that function, we check if a device is a seed by checking its > super block fsid. > > So if a seed device is missing (it can be an seed device without any > chunks on it, or a degraded RAID1 as seed), then we can not read the > super block. > > In that case, we just assume it's not a seed device, and causing too > many devices than our expectation and cause the above failure. > > [FIX] > Instead of unconditionally assume a missing device is not a seed, we add > a new safe net, is_seed_device_tree_search(), to search chunk tree and > determine if that device is a seed or not. > > And if we found the device is still a seed, then just skip it as usual. > > Now the test case btrfs/249 passes as expected: > > btrfs/249 2s > Ran: btrfs/249 > Passed all 1 tests > > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- > This version is different from the original fix from Anand by: > > - No need for kernel patching > Thus no compatible problems > > And also different from the fix from Flint: > > - No need to search chunk tree unconditionally > Tree search itself is a privileged operation while "filesystem usage" > subcommand is not. > > Now we only needs root privilege if we hit a missing seed device, > which is super rare. > > And we can still fallback to assume the device is not seed. > > - Better commit message > --- > cmds/filesystem-usage.c | 72 +++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 70 insertions(+), 2 deletions(-) > > diff --git a/cmds/filesystem-usage.c b/cmds/filesystem-usage.c > index 5810324f245e..214cad2fa75b 100644 > --- a/cmds/filesystem-usage.c > +++ b/cmds/filesystem-usage.c > @@ -700,6 +700,56 @@ out: > return ret; > } > > +/* > + * Return 0 if this devid is not a seed device. > + * Return 1 if this devid is a seed device. > + * Return <0 if error (IO error or EPERM). > + * > + * Since this is done by tree search, it needs root privilege, and > + * should not be triggered unless we hit a missing device and can not > + * determine if it's a seed one. > + */ > +static int is_seed_device_tree_search(int fd, u64 devid, u8 *fsid) > +{ > + struct btrfs_ioctl_search_args args = {0}; > + struct btrfs_ioctl_search_key *sk = &args.key; > + struct btrfs_ioctl_search_header *sh; > + struct btrfs_dev_item *dev; > + unsigned long off = 0; > + int ret; > + int err; > + > + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; > + sk->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; > + sk->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; > + sk->min_type = BTRFS_DEV_ITEM_KEY; > + sk->max_type = BTRFS_DEV_ITEM_KEY; > + sk->min_offset = devid; > + sk->max_offset = devid; > + sk->max_transid = (u64)-1; > + sk->nr_items = 1; > + > + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); > + err = errno; > + if (err == EPERM) > + return -err; > + if (ret < 0) { > + error("cannot lookup chunk tree info: %m"); > + return ret; > + } > + /* No dev item found. */ > + if (sk->nr_items == 0) > + return -ENOENT; > + > + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); > + off += sizeof(*sh); > + > + dev = (struct btrfs_dev_item *)(args.buf + off); > + if (memcmp(dev->fsid, fsid, BTRFS_UUID_SIZE) == 0) > + return 0; > + return 1; > +} > + > /* > * This function loads the device_info structure and put them in an array > */ > @@ -708,7 +758,6 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, > { > int ret, i, ndevs; > struct btrfs_ioctl_fs_info_args fi_args; > - struct btrfs_ioctl_dev_info_args dev_info; > struct device_info *info; > u8 fsid[BTRFS_UUID_SIZE]; > > @@ -730,6 +779,8 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, > } > > for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { > + struct btrfs_ioctl_dev_info_args dev_info = {0}; > + > if (ndevs >= fi_args.num_devices) { > error("unexpected number of devices: %d >= %llu", ndevs, > fi_args.num_devices); > @@ -737,7 +788,6 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, > "if seed device is used, try running this command as root"); > goto out; > } > - memset(&dev_info, 0, sizeof(dev_info)); > ret = get_device_info(fd, i, &dev_info); > > if (ret == -ENODEV) > @@ -747,6 +797,24 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, > goto out; > } > > + /* > + * A missing device, we can not determing if it's a seed > + * device by reading its super block. > + * Thus we have to go tree-search to make sure if it's a seed > + * device. > + */ > + if (!dev_info.path[0]) { > + ret = is_seed_device_tree_search(fd, i, fi_args.fsid); > + if (ret < 0) { > + errno = -ret; > + warning( > + "unable to determine if devid %u is seed: %m, assuming not", i); > + } > + /* Skip the missing seed device. */ > + if (ret > 0) > + continue; > + } > + > /* > * Skip seed device by checking device's fsid (requires root). > * And we will skip only if dev_to_fsid is successful and dev
OR We could use both methods to accommodate older kernels; switch to disk reading if newer sysfs interface is unavailable. Thanks, Anand On 11/02/2023 00:01, Anand Jain wrote: > > > > > An alternative solution is to utilize a kernel interface to obtain the > fsid [1]. Previous experiences have shown that attempting to directly > read a mounted device's disk is not a reliable method and can result in > various problems. As a result, it is advisable to use a kernel interface > to read the fsid. > > [PATCH 2/2] btrfs-progs: read fsid from the sysfs in device_is_seed > On 10/02/2023 13:39, Qu Wenruo wrote: >> [BUG] >> Test case btrfs/249 always fails since its introduction, the failure >> comes from "btrfs filesystem usage" subcommand, and the error output >> looks like this: >> >> QA output created by 249 >> ERROR: unexpected number of devices: 1 >= 1 >> ERROR: if seed device is used, try running this command as root >> FAILED: btrfs filesystem usage, ret 1. Check btrfs.ko and >> btrfs-progs version. >> (see /home/adam/xfstests/results//btrfs/249.full for details) >> >> [CAUSE] >> In function load_device_info(), we only allocate enough space for all >> *RW* devices, expecting we can rule out all seed devices. >> >> And in that function, we check if a device is a seed by checking its >> super block fsid. >> >> So if a seed device is missing (it can be an seed device without any >> chunks on it, or a degraded RAID1 as seed), then we can not read the >> super block. >> >> In that case, we just assume it's not a seed device, and causing too >> many devices than our expectation and cause the above failure. >> >> [FIX] >> Instead of unconditionally assume a missing device is not a seed, we add >> a new safe net, is_seed_device_tree_search(), to search chunk tree and >> determine if that device is a seed or not. >> >> And if we found the device is still a seed, then just skip it as usual. >> >> Now the test case btrfs/249 passes as expected: >> >> btrfs/249 2s >> Ran: btrfs/249 >> Passed all 1 tests >> >> Signed-off-by: Qu Wenruo <wqu@suse.com> >> --- >> This version is different from the original fix from Anand by: >> >> - No need for kernel patching >> Thus no compatible problems >> >> And also different from the fix from Flint: >> >> - No need to search chunk tree unconditionally >> Tree search itself is a privileged operation while "filesystem usage" >> subcommand is not. >> >> Now we only needs root privilege if we hit a missing seed device, >> which is super rare. >> >> And we can still fallback to assume the device is not seed. >> >> - Better commit message >> --- >> cmds/filesystem-usage.c | 72 +++++++++++++++++++++++++++++++++++++++-- >> 1 file changed, 70 insertions(+), 2 deletions(-) >> >> diff --git a/cmds/filesystem-usage.c b/cmds/filesystem-usage.c >> index 5810324f245e..214cad2fa75b 100644 >> --- a/cmds/filesystem-usage.c >> +++ b/cmds/filesystem-usage.c >> @@ -700,6 +700,56 @@ out: >> return ret; >> } >> +/* >> + * Return 0 if this devid is not a seed device. >> + * Return 1 if this devid is a seed device. >> + * Return <0 if error (IO error or EPERM). >> + * >> + * Since this is done by tree search, it needs root privilege, and >> + * should not be triggered unless we hit a missing device and can not >> + * determine if it's a seed one. >> + */ >> +static int is_seed_device_tree_search(int fd, u64 devid, u8 *fsid) >> +{ >> + struct btrfs_ioctl_search_args args = {0}; >> + struct btrfs_ioctl_search_key *sk = &args.key; >> + struct btrfs_ioctl_search_header *sh; >> + struct btrfs_dev_item *dev; >> + unsigned long off = 0; >> + int ret; >> + int err; >> + >> + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; >> + sk->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; >> + sk->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; >> + sk->min_type = BTRFS_DEV_ITEM_KEY; >> + sk->max_type = BTRFS_DEV_ITEM_KEY; >> + sk->min_offset = devid; >> + sk->max_offset = devid; >> + sk->max_transid = (u64)-1; >> + sk->nr_items = 1; >> + >> + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); >> + err = errno; >> + if (err == EPERM) >> + return -err; >> + if (ret < 0) { >> + error("cannot lookup chunk tree info: %m"); >> + return ret; >> + } >> + /* No dev item found. */ >> + if (sk->nr_items == 0) >> + return -ENOENT; >> + >> + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); >> + off += sizeof(*sh); >> + >> + dev = (struct btrfs_dev_item *)(args.buf + off); >> + if (memcmp(dev->fsid, fsid, BTRFS_UUID_SIZE) == 0) >> + return 0; >> + return 1; >> +} >> + >> /* >> * This function loads the device_info structure and put them in an >> array >> */ >> @@ -708,7 +758,6 @@ static int load_device_info(int fd, struct >> device_info **devinfo_ret, >> { >> int ret, i, ndevs; >> struct btrfs_ioctl_fs_info_args fi_args; >> - struct btrfs_ioctl_dev_info_args dev_info; >> struct device_info *info; >> u8 fsid[BTRFS_UUID_SIZE]; >> @@ -730,6 +779,8 @@ static int load_device_info(int fd, struct >> device_info **devinfo_ret, >> } >> for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { >> + struct btrfs_ioctl_dev_info_args dev_info = {0}; >> + >> if (ndevs >= fi_args.num_devices) { >> error("unexpected number of devices: %d >= %llu", ndevs, >> fi_args.num_devices); >> @@ -737,7 +788,6 @@ static int load_device_info(int fd, struct >> device_info **devinfo_ret, >> "if seed device is used, try running this command as root"); >> goto out; >> } >> - memset(&dev_info, 0, sizeof(dev_info)); >> ret = get_device_info(fd, i, &dev_info); >> if (ret == -ENODEV) >> @@ -747,6 +797,24 @@ static int load_device_info(int fd, struct >> device_info **devinfo_ret, >> goto out; >> } >> + /* >> + * A missing device, we can not determing if it's a seed >> + * device by reading its super block. >> + * Thus we have to go tree-search to make sure if it's a seed >> + * device. >> + */ >> + if (!dev_info.path[0]) { >> + ret = is_seed_device_tree_search(fd, i, fi_args.fsid); >> + if (ret < 0) { >> + errno = -ret; >> + warning( >> + "unable to determine if devid %u is seed: %m, assuming not", i); >> + } >> + /* Skip the missing seed device. */ >> + if (ret > 0) >> + continue; >> + } >> + >> /* >> * Skip seed device by checking device's fsid (requires root). >> * And we will skip only if dev_to_fsid is successful and dev >
On 2023/2/11 00:07, Anand Jain wrote: > > OR > > We could use both methods to accommodate older kernels; switch to disk > reading if newer sysfs interface is unavailable. I believe the tree-search is always needed as a fallback. Thus the patch itself should always be needed. For the sysfs part, the commit message has explained it in details, we need a proper way to get if a device is seed, from the IOC_DEV_INFO ioctl. Thus indeed as you commented, if we can add an fsid field to btrfs_ioctl_dev_info_args, it can indeed solve the problem. Thanks, Qu > > Thanks, Anand > > On 11/02/2023 00:01, Anand Jain wrote: >> >> >> >> >> An alternative solution is to utilize a kernel interface to obtain the >> fsid [1]. Previous experiences have shown that attempting to directly >> read a mounted device's disk is not a reliable method and can result >> in various problems. As a result, it is advisable to use a kernel >> interface to read the fsid. >> >> [PATCH 2/2] btrfs-progs: read fsid from the sysfs in device_is_seed >> On 10/02/2023 13:39, Qu Wenruo wrote: >>> [BUG] >>> Test case btrfs/249 always fails since its introduction, the failure >>> comes from "btrfs filesystem usage" subcommand, and the error output >>> looks like this: >>> >>> QA output created by 249 >>> ERROR: unexpected number of devices: 1 >= 1 >>> ERROR: if seed device is used, try running this command as root >>> FAILED: btrfs filesystem usage, ret 1. Check btrfs.ko and >>> btrfs-progs version. >>> (see /home/adam/xfstests/results//btrfs/249.full for details) >>> >>> [CAUSE] >>> In function load_device_info(), we only allocate enough space for all >>> *RW* devices, expecting we can rule out all seed devices. >>> >>> And in that function, we check if a device is a seed by checking its >>> super block fsid. >>> >>> So if a seed device is missing (it can be an seed device without any >>> chunks on it, or a degraded RAID1 as seed), then we can not read the >>> super block. >>> >>> In that case, we just assume it's not a seed device, and causing too >>> many devices than our expectation and cause the above failure. >>> >>> [FIX] >>> Instead of unconditionally assume a missing device is not a seed, we add >>> a new safe net, is_seed_device_tree_search(), to search chunk tree and >>> determine if that device is a seed or not. >>> >>> And if we found the device is still a seed, then just skip it as usual. >>> >>> Now the test case btrfs/249 passes as expected: >>> >>> btrfs/249 2s >>> Ran: btrfs/249 >>> Passed all 1 tests >>> >>> Signed-off-by: Qu Wenruo <wqu@suse.com> >>> --- >>> This version is different from the original fix from Anand by: >>> >>> - No need for kernel patching >>> Thus no compatible problems >>> >>> And also different from the fix from Flint: >>> >>> - No need to search chunk tree unconditionally >>> Tree search itself is a privileged operation while "filesystem usage" >>> subcommand is not. >>> >>> Now we only needs root privilege if we hit a missing seed device, >>> which is super rare. >>> >>> And we can still fallback to assume the device is not seed. >>> >>> - Better commit message >>> --- >>> cmds/filesystem-usage.c | 72 +++++++++++++++++++++++++++++++++++++++-- >>> 1 file changed, 70 insertions(+), 2 deletions(-) >>> >>> diff --git a/cmds/filesystem-usage.c b/cmds/filesystem-usage.c >>> index 5810324f245e..214cad2fa75b 100644 >>> --- a/cmds/filesystem-usage.c >>> +++ b/cmds/filesystem-usage.c >>> @@ -700,6 +700,56 @@ out: >>> return ret; >>> } >>> +/* >>> + * Return 0 if this devid is not a seed device. >>> + * Return 1 if this devid is a seed device. >>> + * Return <0 if error (IO error or EPERM). >>> + * >>> + * Since this is done by tree search, it needs root privilege, and >>> + * should not be triggered unless we hit a missing device and can not >>> + * determine if it's a seed one. >>> + */ >>> +static int is_seed_device_tree_search(int fd, u64 devid, u8 *fsid) >>> +{ >>> + struct btrfs_ioctl_search_args args = {0}; >>> + struct btrfs_ioctl_search_key *sk = &args.key; >>> + struct btrfs_ioctl_search_header *sh; >>> + struct btrfs_dev_item *dev; >>> + unsigned long off = 0; >>> + int ret; >>> + int err; >>> + >>> + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; >>> + sk->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; >>> + sk->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; >>> + sk->min_type = BTRFS_DEV_ITEM_KEY; >>> + sk->max_type = BTRFS_DEV_ITEM_KEY; >>> + sk->min_offset = devid; >>> + sk->max_offset = devid; >>> + sk->max_transid = (u64)-1; >>> + sk->nr_items = 1; >>> + >>> + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); >>> + err = errno; >>> + if (err == EPERM) >>> + return -err; >>> + if (ret < 0) { >>> + error("cannot lookup chunk tree info: %m"); >>> + return ret; >>> + } >>> + /* No dev item found. */ >>> + if (sk->nr_items == 0) >>> + return -ENOENT; >>> + >>> + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); >>> + off += sizeof(*sh); >>> + >>> + dev = (struct btrfs_dev_item *)(args.buf + off); >>> + if (memcmp(dev->fsid, fsid, BTRFS_UUID_SIZE) == 0) >>> + return 0; >>> + return 1; >>> +} >>> + >>> /* >>> * This function loads the device_info structure and put them in >>> an array >>> */ >>> @@ -708,7 +758,6 @@ static int load_device_info(int fd, struct >>> device_info **devinfo_ret, >>> { >>> int ret, i, ndevs; >>> struct btrfs_ioctl_fs_info_args fi_args; >>> - struct btrfs_ioctl_dev_info_args dev_info; >>> struct device_info *info; >>> u8 fsid[BTRFS_UUID_SIZE]; >>> @@ -730,6 +779,8 @@ static int load_device_info(int fd, struct >>> device_info **devinfo_ret, >>> } >>> for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { >>> + struct btrfs_ioctl_dev_info_args dev_info = {0}; >>> + >>> if (ndevs >= fi_args.num_devices) { >>> error("unexpected number of devices: %d >= %llu", ndevs, >>> fi_args.num_devices); >>> @@ -737,7 +788,6 @@ static int load_device_info(int fd, struct >>> device_info **devinfo_ret, >>> "if seed device is used, try running this command as root"); >>> goto out; >>> } >>> - memset(&dev_info, 0, sizeof(dev_info)); >>> ret = get_device_info(fd, i, &dev_info); >>> if (ret == -ENODEV) >>> @@ -747,6 +797,24 @@ static int load_device_info(int fd, struct >>> device_info **devinfo_ret, >>> goto out; >>> } >>> + /* >>> + * A missing device, we can not determing if it's a seed >>> + * device by reading its super block. >>> + * Thus we have to go tree-search to make sure if it's a seed >>> + * device. >>> + */ >>> + if (!dev_info.path[0]) { >>> + ret = is_seed_device_tree_search(fd, i, fi_args.fsid); >>> + if (ret < 0) { >>> + errno = -ret; >>> + warning( >>> + "unable to determine if devid %u is seed: %m, assuming not", >>> i); >>> + } >>> + /* Skip the missing seed device. */ >>> + if (ret > 0) >>> + continue; >>> + } >>> + >>> /* >>> * Skip seed device by checking device's fsid (requires root). >>> * And we will skip only if dev_to_fsid is successful and dev >>
diff --git a/cmds/filesystem-usage.c b/cmds/filesystem-usage.c index 5810324f245e..214cad2fa75b 100644 --- a/cmds/filesystem-usage.c +++ b/cmds/filesystem-usage.c @@ -700,6 +700,56 @@ out: return ret; } +/* + * Return 0 if this devid is not a seed device. + * Return 1 if this devid is a seed device. + * Return <0 if error (IO error or EPERM). + * + * Since this is done by tree search, it needs root privilege, and + * should not be triggered unless we hit a missing device and can not + * determine if it's a seed one. + */ +static int is_seed_device_tree_search(int fd, u64 devid, u8 *fsid) +{ + struct btrfs_ioctl_search_args args = {0}; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + struct btrfs_dev_item *dev; + unsigned long off = 0; + int ret; + int err; + + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; + sk->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; + sk->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; + sk->min_type = BTRFS_DEV_ITEM_KEY; + sk->max_type = BTRFS_DEV_ITEM_KEY; + sk->min_offset = devid; + sk->max_offset = devid; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + err = errno; + if (err == EPERM) + return -err; + if (ret < 0) { + error("cannot lookup chunk tree info: %m"); + return ret; + } + /* No dev item found. */ + if (sk->nr_items == 0) + return -ENOENT; + + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); + off += sizeof(*sh); + + dev = (struct btrfs_dev_item *)(args.buf + off); + if (memcmp(dev->fsid, fsid, BTRFS_UUID_SIZE) == 0) + return 0; + return 1; +} + /* * This function loads the device_info structure and put them in an array */ @@ -708,7 +758,6 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, { int ret, i, ndevs; struct btrfs_ioctl_fs_info_args fi_args; - struct btrfs_ioctl_dev_info_args dev_info; struct device_info *info; u8 fsid[BTRFS_UUID_SIZE]; @@ -730,6 +779,8 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, } for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { + struct btrfs_ioctl_dev_info_args dev_info = {0}; + if (ndevs >= fi_args.num_devices) { error("unexpected number of devices: %d >= %llu", ndevs, fi_args.num_devices); @@ -737,7 +788,6 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, "if seed device is used, try running this command as root"); goto out; } - memset(&dev_info, 0, sizeof(dev_info)); ret = get_device_info(fd, i, &dev_info); if (ret == -ENODEV) @@ -747,6 +797,24 @@ static int load_device_info(int fd, struct device_info **devinfo_ret, goto out; } + /* + * A missing device, we can not determing if it's a seed + * device by reading its super block. + * Thus we have to go tree-search to make sure if it's a seed + * device. + */ + if (!dev_info.path[0]) { + ret = is_seed_device_tree_search(fd, i, fi_args.fsid); + if (ret < 0) { + errno = -ret; + warning( + "unable to determine if devid %u is seed: %m, assuming not", i); + } + /* Skip the missing seed device. */ + if (ret > 0) + continue; + } + /* * Skip seed device by checking device's fsid (requires root). * And we will skip only if dev_to_fsid is successful and dev
[BUG] Test case btrfs/249 always fails since its introduction, the failure comes from "btrfs filesystem usage" subcommand, and the error output looks like this: QA output created by 249 ERROR: unexpected number of devices: 1 >= 1 ERROR: if seed device is used, try running this command as root FAILED: btrfs filesystem usage, ret 1. Check btrfs.ko and btrfs-progs version. (see /home/adam/xfstests/results//btrfs/249.full for details) [CAUSE] In function load_device_info(), we only allocate enough space for all *RW* devices, expecting we can rule out all seed devices. And in that function, we check if a device is a seed by checking its super block fsid. So if a seed device is missing (it can be an seed device without any chunks on it, or a degraded RAID1 as seed), then we can not read the super block. In that case, we just assume it's not a seed device, and causing too many devices than our expectation and cause the above failure. [FIX] Instead of unconditionally assume a missing device is not a seed, we add a new safe net, is_seed_device_tree_search(), to search chunk tree and determine if that device is a seed or not. And if we found the device is still a seed, then just skip it as usual. Now the test case btrfs/249 passes as expected: btrfs/249 2s Ran: btrfs/249 Passed all 1 tests Signed-off-by: Qu Wenruo <wqu@suse.com> --- This version is different from the original fix from Anand by: - No need for kernel patching Thus no compatible problems And also different from the fix from Flint: - No need to search chunk tree unconditionally Tree search itself is a privileged operation while "filesystem usage" subcommand is not. Now we only needs root privilege if we hit a missing seed device, which is super rare. And we can still fallback to assume the device is not seed. - Better commit message --- cmds/filesystem-usage.c | 72 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-)