Commit 02c438bb authored by Linus Torvalds's avatar Linus Torvalds

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
parents dcb9f486 440861b1
...@@ -477,7 +477,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, ...@@ -477,7 +477,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
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;
} }
......
...@@ -468,6 +468,7 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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;
......
...@@ -2100,7 +2100,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx, ...@@ -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);
......
...@@ -119,6 +119,7 @@ enum { ...@@ -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[] = { ...@@ -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) ...@@ -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);
......
...@@ -1290,7 +1290,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment