1

for-6.10-tag

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAmZQjzoACgkQxWXV+ddt
 WDsFaw/+O6lH+rPLhvUoqtnrydC6QLnEW5Qj5EURDt3HkROOsXHszdNGKdsETZ2i
 /s4dDiCRwLv7PP/bWlFfQbHzckoBHI9I/1GxHKQM3OM27BpXvILacXSMJ13zw4vq
 DRQIUdTwfUkegEytZb0ddv6+++R1YyU6nE6LfiF2Pf4XJMQ2WXPRNu6bAa27xUia
 4ITHB6m92zynhATJk0/RpfCU64HWwj919WnJDmoVOJ7Nr8Pslz4jKm7HS1qiehNd
 EbhduQPhj7UvWiL4C9/iFFndgzm1tX1WNlJDu5c0KqwYIHq2+BmDv3Cqhkazkdvu
 veU0wO62bZzV42vmTvQXzyXeXjNXRyLOvK6uHXv0VCO8VVsl2/WnYTRWmH44ECar
 z4tByfBKA7nIL2e23ztkyqnhygDf8Y1/Dy+GfprR6JPhyYGHJDqLcB3Gyw9y/AXO
 b/2MoAEgET9QPM/0HLqdonDJ75D2PF0qmwp1ys79w/BGH0BUoxZs/POL2UT87EJO
 rO5kW0/nZy99sbWFfZRwDUxTj1IlDqdudaHPOdJs/tUb3wPseLm5abQEyk+Dns6K
 3y7OviNVQy0x325JY9RmdfnJv60KHvv5pqws1Nkuhqk1LH8csL6MsYlcybhR+vOk
 G9qkNxg35aNqjNlBi7RacMT8OgwVbVhik8jVr+MfXk30grIevzU=
 =XR4r
 -----END PGP SIGNATURE-----

Merge tag 'for-6.10-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull more btrfs updates from David Sterba:
 "A few more updates, mostly stability fixes or user visible changes:

   - fix race in zoned mode during device replace that can lead to
     use-after-free

   - update return codes and lower message levels for quota rescan where
     it's causing false alerts

   - fix unexpected qgroup id reuse under some conditions

   - fix condition when looking up extent refs

   - add option norecovery (removed in 6.8), the intended replacements
     haven't been used and some aplications still rely on the old one

   - build warning fixes"

* tag 'for-6.10-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: re-introduce 'norecovery' mount option
  btrfs: fix end of tree detection when searching for data extent ref
  btrfs: scrub: initialize ret in scrub_simple_mirror() to fix compilation warning
  btrfs: zoned: fix use-after-free due to race with dev replace
  btrfs: qgroup: fix qgroup id collision across mounts
  btrfs: qgroup: update rescan message levels and error codes
This commit is contained in:
Linus Torvalds 2024-05-24 09:40:31 -07:00
commit 02c438bbff
5 changed files with 45 additions and 12 deletions

View File

@ -477,7 +477,7 @@ again:
if (path->slots[0] >= nritems) { if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path); ret = btrfs_next_leaf(root, path);
if (ret) { if (ret) {
if (ret > 1) if (ret > 0)
return -ENOENT; return -ENOENT;
return ret; return ret;
} }

View File

@ -468,6 +468,7 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
} }
if (!qgroup) { if (!qgroup) {
struct btrfs_qgroup *prealloc; struct btrfs_qgroup *prealloc;
struct btrfs_root *tree_root = fs_info->tree_root;
prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL); prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
if (!prealloc) { if (!prealloc) {
@ -475,6 +476,25 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
goto out; goto out;
} }
qgroup = add_qgroup_rb(fs_info, prealloc, found_key.offset); qgroup = add_qgroup_rb(fs_info, prealloc, found_key.offset);
/*
* If a qgroup exists for a subvolume ID, it is possible
* that subvolume has been deleted, in which case
* re-using that ID would lead to incorrect accounting.
*
* Ensure that we skip any such subvol ids.
*
* We don't need to lock because this is only called
* during mount before we start doing things like creating
* subvolumes.
*/
if (is_fstree(qgroup->qgroupid) &&
qgroup->qgroupid > tree_root->free_objectid)
/*
* Don't need to check against BTRFS_LAST_FREE_OBJECTID,
* as it will get checked on the next call to
* btrfs_get_free_objectid.
*/
tree_root->free_objectid = qgroup->qgroupid + 1;
} }
ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup); ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
if (ret < 0) if (ret < 0)
@ -3820,14 +3840,14 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
/* we're resuming qgroup rescan at mount time */ /* we're resuming qgroup rescan at mount time */
if (!(fs_info->qgroup_flags & if (!(fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAG_RESCAN)) { BTRFS_QGROUP_STATUS_FLAG_RESCAN)) {
btrfs_warn(fs_info, btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup rescan is not queued"); "qgroup rescan init failed, qgroup rescan is not queued");
ret = -EINVAL; ret = -EINVAL;
} else if (!(fs_info->qgroup_flags & } else if (!(fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAG_ON)) { BTRFS_QGROUP_STATUS_FLAG_ON)) {
btrfs_warn(fs_info, btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup is not enabled"); "qgroup rescan init failed, qgroup is not enabled");
ret = -EINVAL; ret = -ENOTCONN;
} }
if (ret) if (ret)
@ -3838,14 +3858,12 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
if (init_flags) { if (init_flags) {
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
btrfs_warn(fs_info,
"qgroup rescan is already in progress");
ret = -EINPROGRESS; ret = -EINPROGRESS;
} else if (!(fs_info->qgroup_flags & } else if (!(fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAG_ON)) { BTRFS_QGROUP_STATUS_FLAG_ON)) {
btrfs_warn(fs_info, btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup is not enabled"); "qgroup rescan init failed, qgroup is not enabled");
ret = -EINVAL; ret = -ENOTCONN;
} else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED) { } else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED) {
/* Quota disable is in progress */ /* Quota disable is in progress */
ret = -EBUSY; ret = -EBUSY;

View File

@ -2100,7 +2100,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
struct btrfs_fs_info *fs_info = sctx->fs_info; struct btrfs_fs_info *fs_info = sctx->fs_info;
const u64 logical_end = logical_start + logical_length; const u64 logical_end = logical_start + logical_length;
u64 cur_logical = logical_start; u64 cur_logical = logical_start;
int ret; int ret = 0;
/* The range must be inside the bg */ /* The range must be inside the bg */
ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length); ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length);

View File

@ -119,6 +119,7 @@ enum {
Opt_thread_pool, Opt_thread_pool,
Opt_treelog, Opt_treelog,
Opt_user_subvol_rm_allowed, Opt_user_subvol_rm_allowed,
Opt_norecovery,
/* Rescue options */ /* Rescue options */
Opt_rescue, Opt_rescue,
@ -245,6 +246,8 @@ static const struct fs_parameter_spec btrfs_fs_parameters[] = {
__fsparam(NULL, "nologreplay", Opt_nologreplay, fs_param_deprecated, NULL), __fsparam(NULL, "nologreplay", Opt_nologreplay, fs_param_deprecated, NULL),
/* Deprecated, with alias rescue=usebackuproot */ /* Deprecated, with alias rescue=usebackuproot */
__fsparam(NULL, "usebackuproot", Opt_usebackuproot, fs_param_deprecated, NULL), __fsparam(NULL, "usebackuproot", Opt_usebackuproot, fs_param_deprecated, NULL),
/* For compatibility only, alias for "rescue=nologreplay". */
fsparam_flag("norecovery", Opt_norecovery),
/* Debugging options. */ /* Debugging options. */
fsparam_flag_no("enospc_debug", Opt_enospc_debug), fsparam_flag_no("enospc_debug", Opt_enospc_debug),
@ -438,6 +441,11 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
"'nologreplay' is deprecated, use 'rescue=nologreplay' instead"); "'nologreplay' is deprecated, use 'rescue=nologreplay' instead");
btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY); btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
break; break;
case Opt_norecovery:
btrfs_info(NULL,
"'norecovery' is for compatibility only, recommended to use 'rescue=nologreplay'");
btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
break;
case Opt_flushoncommit: case Opt_flushoncommit:
if (result.negated) if (result.negated)
btrfs_clear_opt(ctx->mount_opt, FLUSHONCOMMIT); btrfs_clear_opt(ctx->mount_opt, FLUSHONCOMMIT);

View File

@ -1290,7 +1290,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
struct btrfs_chunk_map *map) struct btrfs_chunk_map *map)
{ {
struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
struct btrfs_device *device = map->stripes[zone_idx].dev; struct btrfs_device *device;
int dev_replace_is_ongoing = 0; int dev_replace_is_ongoing = 0;
unsigned int nofs_flag; unsigned int nofs_flag;
struct blk_zone zone; struct blk_zone zone;
@ -1298,7 +1298,11 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
info->physical = map->stripes[zone_idx].physical; info->physical = map->stripes[zone_idx].physical;
down_read(&dev_replace->rwsem);
device = map->stripes[zone_idx].dev;
if (!device->bdev) { if (!device->bdev) {
up_read(&dev_replace->rwsem);
info->alloc_offset = WP_MISSING_DEV; info->alloc_offset = WP_MISSING_DEV;
return 0; return 0;
} }
@ -1308,6 +1312,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
__set_bit(zone_idx, active); __set_bit(zone_idx, active);
if (!btrfs_dev_is_sequential(device, info->physical)) { if (!btrfs_dev_is_sequential(device, info->physical)) {
up_read(&dev_replace->rwsem);
info->alloc_offset = WP_CONVENTIONAL; info->alloc_offset = WP_CONVENTIONAL;
return 0; return 0;
} }
@ -1315,11 +1320,9 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
/* This zone will be used for allocation, so mark this zone non-empty. */ /* This zone will be used for allocation, so mark this zone non-empty. */
btrfs_dev_clear_zone_empty(device, info->physical); btrfs_dev_clear_zone_empty(device, info->physical);
down_read(&dev_replace->rwsem);
dev_replace_is_ongoing = btrfs_dev_replace_is_ongoing(dev_replace); dev_replace_is_ongoing = btrfs_dev_replace_is_ongoing(dev_replace);
if (dev_replace_is_ongoing && dev_replace->tgtdev != NULL) if (dev_replace_is_ongoing && dev_replace->tgtdev != NULL)
btrfs_dev_clear_zone_empty(dev_replace->tgtdev, info->physical); btrfs_dev_clear_zone_empty(dev_replace->tgtdev, info->physical);
up_read(&dev_replace->rwsem);
/* /*
* The group is mapped to a sequential zone. Get the zone write pointer * The group is mapped to a sequential zone. Get the zone write pointer
@ -1330,6 +1333,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
ret = btrfs_get_dev_zone(device, info->physical, &zone); ret = btrfs_get_dev_zone(device, info->physical, &zone);
memalloc_nofs_restore(nofs_flag); memalloc_nofs_restore(nofs_flag);
if (ret) { if (ret) {
up_read(&dev_replace->rwsem);
if (ret != -EIO && ret != -EOPNOTSUPP) if (ret != -EIO && ret != -EOPNOTSUPP)
return ret; return ret;
info->alloc_offset = WP_MISSING_DEV; info->alloc_offset = WP_MISSING_DEV;
@ -1341,6 +1345,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
"zoned: unexpected conventional zone %llu on device %s (devid %llu)", "zoned: unexpected conventional zone %llu on device %s (devid %llu)",
zone.start << SECTOR_SHIFT, rcu_str_deref(device->name), zone.start << SECTOR_SHIFT, rcu_str_deref(device->name),
device->devid); device->devid);
up_read(&dev_replace->rwsem);
return -EIO; return -EIO;
} }
@ -1368,6 +1373,8 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
break; break;
} }
up_read(&dev_replace->rwsem);
return 0; return 0;
} }