Commit 02c438bb authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -477,7 +477,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
		if (path->slots[0] >= nritems) {
			ret = btrfs_next_leaf(root, path);
			if (ret) {
				if (ret > 1)
				if (ret > 0)
					return -ENOENT;
				return ret;
			}
+25 −7
Original line number Diff line number Diff line
@@ -468,6 +468,7 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
		}
		if (!qgroup) {
			struct btrfs_qgroup *prealloc;
			struct btrfs_root *tree_root = fs_info->tree_root;

			prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
			if (!prealloc) {
@@ -475,6 +476,25 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
				goto out;
			}
			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);
		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 */
		if (!(fs_info->qgroup_flags &
		      BTRFS_QGROUP_STATUS_FLAG_RESCAN)) {
			btrfs_warn(fs_info,
			btrfs_debug(fs_info,
			"qgroup rescan init failed, qgroup rescan is not queued");
			ret = -EINVAL;
		} else if (!(fs_info->qgroup_flags &
			     BTRFS_QGROUP_STATUS_FLAG_ON)) {
			btrfs_warn(fs_info,
			btrfs_debug(fs_info,
			"qgroup rescan init failed, qgroup is not enabled");
			ret = -EINVAL;
			ret = -ENOTCONN;
		}

		if (ret)
@@ -3838,14 +3858,12 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,

	if (init_flags) {
		if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
			btrfs_warn(fs_info,
				   "qgroup rescan is already in progress");
			ret = -EINPROGRESS;
		} else if (!(fs_info->qgroup_flags &
			     BTRFS_QGROUP_STATUS_FLAG_ON)) {
			btrfs_warn(fs_info,
			btrfs_debug(fs_info,
			"qgroup rescan init failed, qgroup is not enabled");
			ret = -EINVAL;
			ret = -ENOTCONN;
		} else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED) {
			/* Quota disable is in progress */
			ret = -EBUSY;
+1 −1
Original line number Diff line number Diff line
@@ -2100,7 +2100,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
	struct btrfs_fs_info *fs_info = sctx->fs_info;
	const u64 logical_end = logical_start + logical_length;
	u64 cur_logical = logical_start;
	int ret;
	int ret = 0;

	/* The range must be inside the bg */
	ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length);
+8 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ enum {
	Opt_thread_pool,
	Opt_treelog,
	Opt_user_subvol_rm_allowed,
	Opt_norecovery,

	/* Rescue options */
	Opt_rescue,
@@ -245,6 +246,8 @@ static const struct fs_parameter_spec btrfs_fs_parameters[] = {
	__fsparam(NULL, "nologreplay", Opt_nologreplay, fs_param_deprecated, NULL),
	/* Deprecated, with alias rescue=usebackuproot */
	__fsparam(NULL, "usebackuproot", Opt_usebackuproot, fs_param_deprecated, NULL),
	/* For compatibility only, alias for "rescue=nologreplay". */
	fsparam_flag("norecovery", Opt_norecovery),

	/* Debugging options. */
	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");
		btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
		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:
		if (result.negated)
			btrfs_clear_opt(ctx->mount_opt, FLUSHONCOMMIT);
+10 −3
Original line number Diff line number Diff line
@@ -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_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;
	unsigned int nofs_flag;
	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;

	down_read(&dev_replace->rwsem);
	device = map->stripes[zone_idx].dev;

	if (!device->bdev) {
		up_read(&dev_replace->rwsem);
		info->alloc_offset = WP_MISSING_DEV;
		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);

	if (!btrfs_dev_is_sequential(device, info->physical)) {
		up_read(&dev_replace->rwsem);
		info->alloc_offset = WP_CONVENTIONAL;
		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. */
	btrfs_dev_clear_zone_empty(device, info->physical);

	down_read(&dev_replace->rwsem);
	dev_replace_is_ongoing = btrfs_dev_replace_is_ongoing(dev_replace);
	if (dev_replace_is_ongoing && dev_replace->tgtdev != NULL)
		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
@@ -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);
	memalloc_nofs_restore(nofs_flag);
	if (ret) {
		up_read(&dev_replace->rwsem);
		if (ret != -EIO && ret != -EOPNOTSUPP)
			return ret;
		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)",
			zone.start << SECTOR_SHIFT, rcu_str_deref(device->name),
			device->devid);
		up_read(&dev_replace->rwsem);
		return -EIO;
	}

@@ -1368,6 +1373,8 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
		break;
	}

	up_read(&dev_replace->rwsem);

	return 0;
}