Commit 7e36e03d authored by Jens Axboe's avatar Jens Axboe
Browse files

Merge tag 'md-7.1-20260428' of...

Merge tag 'md-7.1-20260428' of https://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux into block-7.1

Pull MD fixes from Yu Kuai:

"Bug Fixes:
 - Fix a raid5 UAF on IO across the reshape position.
 - Avoid failing RAID1/RAID10 devices for invalid IO errors.
 - Fix RAID10 divide-by-zero when far_copies is zero.
 - Restore bitmap grow through sysfs.

 Cleanups:
 - Use mddev_is_dm() instead of open-coding gendisk checks.
 - Use ATTRIBUTE_GROUPS() for md default sysfs attributes.
 - Replace open-coded wait loops with wait_event helpers."

* tag 'md-7.1-20260428' of https://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux:
  md: use ATTRIBUTE_GROUPS() for md default sysfs attributes
  md: use mddev_is_dm() instead of open-coding gendisk checks
  md/raid1: replace wait loop with wait_event_idle() in raid1_write_request()
  md/md-bitmap: add a none backend for bitmap grow
  md/md-bitmap: split bitmap sysfs groups
  md: factor bitmap creation away from sysfs handling
  md: use mddev_lock_nointr() in mddev_suspend_and_lock_nointr()
  md: replace wait loop with wait_event() in md_handle_request()
  md/raid10: fix divide-by-zero in setup_geo() with zero far_copies
  md/raid1,raid10: don't fail devices for invalid IO errors
  MAINTAINERS: Add Xiao Ni as md/raid reviewer
  md/raid5: Fix UAF on IO across the reshape position
parents 0898a817 3b2f70ea
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -24764,6 +24764,7 @@ SOFTWARE RAID (Multiple Disks) SUPPORT
M:	Song Liu <song@kernel.org>
M:	Yu Kuai <yukuai@fnnas.com>
R:	Li Nan <linan122@huawei.com>
R:	Xiao Ni <xiao@kernel.org>
L:	linux-raid@vger.kernel.org
S:	Supported
Q:	https://patchwork.kernel.org/project/linux-raid/list/
+119 −12
Original line number Diff line number Diff line
@@ -216,6 +216,7 @@ struct bitmap {
};

static struct workqueue_struct *md_bitmap_wq;
static struct attribute_group md_bitmap_internal_group;

static int __bitmap_resize(struct bitmap *bitmap, sector_t blocks,
			   int chunksize, bool init);
@@ -2580,6 +2581,30 @@ static int bitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
	return __bitmap_resize(bitmap, blocks, chunksize, false);
}

static bool bitmap_none_enabled(void *data, bool flush)
{
	return false;
}

static int bitmap_none_create(struct mddev *mddev)
{
	return 0;
}

static int bitmap_none_load(struct mddev *mddev)
{
	return 0;
}

static void bitmap_none_destroy(struct mddev *mddev)
{
}

static int bitmap_none_get_stats(void *data, struct md_bitmap_stats *stats)
{
	return -ENOENT;
}

static ssize_t
location_show(struct mddev *mddev, char *page)
{
@@ -2618,7 +2643,11 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
			goto out;
		}

		bitmap_destroy(mddev);
		sysfs_unmerge_group(&mddev->kobj, &md_bitmap_internal_group);
		md_bitmap_destroy_nosysfs(mddev);
		mddev->bitmap_id = ID_BITMAP_NONE;
		if (!mddev_set_bitmap_ops_nosysfs(mddev))
			goto none_err;
		mddev->bitmap_info.offset = 0;
		if (mddev->bitmap_info.file) {
			struct file *f = mddev->bitmap_info.file;
@@ -2654,16 +2683,25 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
			}

			mddev->bitmap_info.offset = offset;
			rv = bitmap_create(mddev);
			md_bitmap_destroy_nosysfs(mddev);
			mddev->bitmap_id = ID_BITMAP;
			if (!mddev_set_bitmap_ops_nosysfs(mddev))
				goto bitmap_err;

			rv = md_bitmap_create_nosysfs(mddev);
			if (rv)
				goto out;
				goto create_err;

			rv = bitmap_load(mddev);
			rv = mddev->bitmap_ops->load(mddev);
			if (rv) {
				mddev->bitmap_info.offset = 0;
				bitmap_destroy(mddev);
				goto out;
				goto load_err;
			}

			rv = sysfs_merge_group(&mddev->kobj,
					       &md_bitmap_internal_group);
			if (rv)
				goto merge_err;
		}
	}
	if (!mddev->external) {
@@ -2679,6 +2717,22 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
	if (rv)
		return rv;
	return len;

merge_err:
	mddev->bitmap_info.offset = 0;
load_err:
	md_bitmap_destroy_nosysfs(mddev);
create_err:
	mddev->bitmap_info.offset = 0;
	mddev->bitmap_id = ID_BITMAP_NONE;
	if (!mddev_set_bitmap_ops_nosysfs(mddev))
		rv = -ENOENT;
	goto out;
bitmap_err:
	rv = -ENOENT;
none_err:
	mddev->bitmap_info.offset = 0;
	goto out;
}

static struct md_sysfs_entry bitmap_location =
@@ -2955,8 +3009,12 @@ static struct md_sysfs_entry max_backlog_used =
__ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
       behind_writes_used_show, behind_writes_used_reset);

static struct attribute *md_bitmap_attrs[] = {
static struct attribute *md_bitmap_common_attrs[] = {
	&bitmap_location.attr,
	NULL
};

static struct attribute *md_bitmap_internal_attrs[] = {
	&bitmap_space.attr,
	&bitmap_timeout.attr,
	&bitmap_backlog.attr,
@@ -2967,9 +3025,41 @@ static struct attribute *md_bitmap_attrs[] = {
	NULL
};

static struct attribute_group md_bitmap_group = {
static struct attribute_group md_bitmap_common_group = {
	.name = "bitmap",
	.attrs = md_bitmap_common_attrs,
};

static struct attribute_group md_bitmap_internal_group = {
	.name = "bitmap",
	.attrs = md_bitmap_attrs,
	.attrs = md_bitmap_internal_attrs,
};

static const struct attribute_group *bitmap_groups[] = {
	&md_bitmap_common_group,
	&md_bitmap_internal_group,
	NULL,
};

static const struct attribute_group *bitmap_none_groups[] = {
	&md_bitmap_common_group,
	NULL,
};

static struct bitmap_operations bitmap_none_ops = {
	.head = {
		.type	= MD_BITMAP,
		.id	= ID_BITMAP_NONE,
		.name	= "none",
	},

	.enabled		= bitmap_none_enabled,
	.create			= bitmap_none_create,
	.load			= bitmap_none_load,
	.destroy		= bitmap_none_destroy,
	.get_stats		= bitmap_none_get_stats,

	.groups			= bitmap_none_groups,
};

static struct bitmap_operations bitmap_ops = {
@@ -3013,21 +3103,38 @@ static struct bitmap_operations bitmap_ops = {
	.set_pages		= bitmap_set_pages,
	.free			= md_bitmap_free,

	.group			= &md_bitmap_group,
	.groups			= bitmap_groups,
};

int md_bitmap_init(void)
{
	int err;

	md_bitmap_wq = alloc_workqueue("md_bitmap", WQ_MEM_RECLAIM | WQ_UNBOUND,
				       0);
	if (!md_bitmap_wq)
		return -ENOMEM;

	return register_md_submodule(&bitmap_ops.head);
	err = register_md_submodule(&bitmap_none_ops.head);
	if (err)
		goto err_wq;

	err = register_md_submodule(&bitmap_ops.head);
	if (err)
		goto err_none;

	return 0;

err_none:
	unregister_md_submodule(&bitmap_none_ops.head);
err_wq:
	destroy_workqueue(md_bitmap_wq);
	return err;
}

void md_bitmap_exit(void)
{
	destroy_workqueue(md_bitmap_wq);
	unregister_md_submodule(&bitmap_ops.head);
	unregister_md_submodule(&bitmap_none_ops.head);
	destroy_workqueue(md_bitmap_wq);
}
+1 −1
Original line number Diff line number Diff line
@@ -125,7 +125,7 @@ struct bitmap_operations {
	void (*set_pages)(void *data, unsigned long pages);
	void (*free)(void *data);

	struct attribute_group *group;
	const struct attribute_group **groups;
};

/* the bitmap API */
+6 −1
Original line number Diff line number Diff line
@@ -1738,6 +1738,11 @@ static struct attribute_group md_llbitmap_group = {
	.attrs = md_llbitmap_attrs,
};

static const struct attribute_group *md_llbitmap_groups[] = {
	&md_llbitmap_group,
	NULL,
};

static struct bitmap_operations llbitmap_ops = {
	.head = {
		.type	= MD_BITMAP,
@@ -1774,7 +1779,7 @@ static struct bitmap_operations llbitmap_ops = {
	.dirty_bits		= llbitmap_dirty_bits,
	.write_all		= llbitmap_write_all,

	.group			= &md_llbitmap_group,
	.groups			= md_llbitmap_groups,
};

int md_llbitmap_init(void)
+102 −80
Original line number Diff line number Diff line
@@ -396,27 +396,19 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio)
{
check_suspended:
	if (is_suspended(mddev, bio)) {
		DEFINE_WAIT(__wait);
		/* Bail out if REQ_NOWAIT is set for the bio */
		if (bio->bi_opf & REQ_NOWAIT) {
			bio_wouldblock_error(bio);
			return true;
		}
		for (;;) {
			prepare_to_wait(&mddev->sb_wait, &__wait,
					TASK_UNINTERRUPTIBLE);
			if (!is_suspended(mddev, bio))
				break;
			schedule();
		}
		finish_wait(&mddev->sb_wait, &__wait);
		wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
	}
	if (!percpu_ref_tryget_live(&mddev->active_io))
		goto check_suspended;

	if (!mddev->pers->make_request(mddev, bio)) {
		percpu_ref_put(&mddev->active_io);
		if (!mddev->gendisk && mddev->pers->prepare_suspend)
		if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
			return false;
		goto check_suspended;
	}
@@ -687,13 +679,38 @@ static void active_io_release(struct percpu_ref *ref)

static void no_op(struct percpu_ref *r) {}

static bool mddev_set_bitmap_ops(struct mddev *mddev)
static void md_bitmap_sysfs_add(struct mddev *mddev)
{
	if (sysfs_update_groups(&mddev->kobj, mddev->bitmap_ops->groups))
		pr_warn("md: cannot register extra bitmap attributes for %s\n",
			mdname(mddev));
	else
		/*
		 * Inform user with KOBJ_CHANGE about new bitmap
		 * attributes.
		 */
		kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
}

static void md_bitmap_sysfs_del(struct mddev *mddev)
{
	int nr_groups = 0;

	for (nr_groups = 0; mddev->bitmap_ops->groups[nr_groups]; nr_groups++)
		;

	while (--nr_groups >= 1)
		sysfs_unmerge_group(&mddev->kobj,
				    mddev->bitmap_ops->groups[nr_groups]);
	sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->groups[0]);
}

bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev)
{
	struct bitmap_operations *old = mddev->bitmap_ops;
	struct md_submodule_head *head;

	if (mddev->bitmap_id == ID_BITMAP_NONE ||
	    (old && old->head.id == mddev->bitmap_id))
	if (mddev->bitmap_ops &&
	    mddev->bitmap_ops->head.id == mddev->bitmap_id)
		return true;

	xa_lock(&md_submodule);
@@ -711,18 +728,6 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)

	mddev->bitmap_ops = (void *)head;
	xa_unlock(&md_submodule);

	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
		if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group))
			pr_warn("md: cannot register extra bitmap attributes for %s\n",
				mdname(mddev));
		else
			/*
			 * Inform user with KOBJ_CHANGE about new bitmap
			 * attributes.
			 */
			kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
	}
	return true;

err:
@@ -730,15 +735,6 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
	return false;
}

static void mddev_clear_bitmap_ops(struct mddev *mddev)
{
	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
	    mddev->bitmap_ops->group)
		sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);

	mddev->bitmap_ops = NULL;
}

int mddev_init(struct mddev *mddev)
{
	int err = 0;
@@ -4279,7 +4275,7 @@ bitmap_type_show(struct mddev *mddev, char *page)

	xa_lock(&md_submodule);
	xa_for_each(&md_submodule, i, head) {
		if (head->type != MD_BITMAP)
		if (head->type != MD_BITMAP || head->id == ID_BITMAP_NONE)
			continue;

		if (mddev->bitmap_id == head->id)
@@ -6059,10 +6055,7 @@ static struct attribute *md_default_attrs[] = {
	&md_logical_block_size.attr,
	NULL,
};

static const struct attribute_group md_default_group = {
	.attrs = md_default_attrs,
};
ATTRIBUTE_GROUPS(md_default);

static struct attribute *md_redundancy_attrs[] = {
	&md_scan_mode.attr,
@@ -6087,11 +6080,6 @@ static const struct attribute_group md_redundancy_group = {
	.attrs = md_redundancy_attrs,
};

static const struct attribute_group *md_attr_groups[] = {
	&md_default_group,
	NULL,
};

static ssize_t
md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
@@ -6174,7 +6162,7 @@ static const struct sysfs_ops md_sysfs_ops = {
static const struct kobj_type md_ktype = {
	.release	= md_kobj_release,
	.sysfs_ops	= &md_sysfs_ops,
	.default_groups	= md_attr_groups,
	.default_groups	= md_default_groups,
};

int mdp_major = 0;
@@ -6539,7 +6527,7 @@ static enum md_submodule_id md_bitmap_get_id_from_sb(struct mddev *mddev)
	return id;
}

static int md_bitmap_create(struct mddev *mddev)
int md_bitmap_create_nosysfs(struct mddev *mddev)
{
	enum md_submodule_id orig_id = mddev->bitmap_id;
	enum md_submodule_id sb_id;
@@ -6548,8 +6536,10 @@ static int md_bitmap_create(struct mddev *mddev)
	if (mddev->bitmap_id == ID_BITMAP_NONE)
		return -EINVAL;

	if (!mddev_set_bitmap_ops(mddev))
	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
		mddev->bitmap_id = orig_id;
		return -ENOENT;
	}

	err = mddev->bitmap_ops->create(mddev);
	if (!err)
@@ -6560,37 +6550,72 @@ static int md_bitmap_create(struct mddev *mddev)
	 * doesn't match, and mdadm is not the latest version to set
	 * bitmap_type, set bitmap_ops based on the disk version.
	 */
	mddev_clear_bitmap_ops(mddev);
	mddev->bitmap_ops = NULL;

	sb_id = md_bitmap_get_id_from_sb(mddev);
	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
	if (sb_id == ID_BITMAP_NONE || sb_id == orig_id) {
		mddev->bitmap_id = orig_id;
		return err;
	}

	pr_info("md: %s: bitmap version mismatch, switching from %d to %d\n",
		mdname(mddev), orig_id, sb_id);

	mddev->bitmap_id = sb_id;
	if (!mddev_set_bitmap_ops(mddev)) {
	if (!mddev_set_bitmap_ops_nosysfs(mddev)) {
		mddev->bitmap_id = orig_id;
		return -ENOENT;
	}

	err = mddev->bitmap_ops->create(mddev);
	if (err) {
		mddev_clear_bitmap_ops(mddev);
		mddev->bitmap_ops = NULL;
		mddev->bitmap_id = orig_id;
	}

	return err;
}

static void md_bitmap_destroy(struct mddev *mddev)
static int md_bitmap_create(struct mddev *mddev)
{
	int err;

	err = md_bitmap_create_nosysfs(mddev);
	if (err)
		return err;

	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
		md_bitmap_sysfs_add(mddev);

	return 0;
}

void md_bitmap_destroy_nosysfs(struct mddev *mddev)
{
	if (!md_bitmap_registered(mddev))
		return;

	mddev->bitmap_ops->destroy(mddev);
	mddev_clear_bitmap_ops(mddev);
	mddev->bitmap_ops = NULL;
}

static void md_bitmap_destroy(struct mddev *mddev)
{
	if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
	    mddev->bitmap_ops->groups)
		md_bitmap_sysfs_del(mddev);

	md_bitmap_destroy_nosysfs(mddev);
}

static void md_bitmap_set_none(struct mddev *mddev)
{
	mddev->bitmap_id = ID_BITMAP_NONE;
	if (!mddev_set_bitmap_ops_nosysfs(mddev))
		return;

	if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups)
		md_bitmap_sysfs_add(mddev);
}

int md_run(struct mddev *mddev)
@@ -6713,7 +6738,7 @@ int md_run(struct mddev *mddev)
	}

	/* dm-raid expect sync_thread to be frozen until resume */
	if (mddev->gendisk)
	if (!mddev_is_dm(mddev))
		mddev->recovery = 0;

	/* may be over-ridden by personality */
@@ -6802,6 +6827,10 @@ int md_run(struct mddev *mddev)
	if (mddev->sb_flags)
		md_update_sb(mddev, 0);

	if (IS_ENABLED(CONFIG_MD_BITMAP) && !mddev->bitmap_info.file &&
	    !mddev->bitmap_info.offset)
		md_bitmap_set_none(mddev);

	md_new_event();
	return 0;

@@ -7747,7 +7776,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
{
	int err = 0;

	if (!md_bitmap_registered(mddev))
	if (!md_bitmap_registered(mddev) ||
	    mddev->bitmap_id == ID_BITMAP_NONE)
		return -EINVAL;

	if (mddev->pers) {
@@ -7812,10 +7842,12 @@ static int set_bitmap_file(struct mddev *mddev, int fd)

			if (err) {
				md_bitmap_destroy(mddev);
				md_bitmap_set_none(mddev);
				fd = -1;
			}
		} else if (fd < 0) {
			md_bitmap_destroy(mddev);
			md_bitmap_set_none(mddev);
		}
	}

@@ -8122,12 +8154,16 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
				mddev->bitmap_info.default_offset;
			mddev->bitmap_info.space =
				mddev->bitmap_info.default_space;
			mddev->bitmap_id = ID_BITMAP;
			rv = md_bitmap_create(mddev);
			if (!rv)
				rv = mddev->bitmap_ops->load(mddev);

			if (rv)
			if (rv) {
				md_bitmap_destroy(mddev);
				mddev->bitmap_info.offset = 0;
				md_bitmap_set_none(mddev);
			}
		} else {
			struct md_bitmap_stats stats;

@@ -8155,6 +8191,7 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
			}
			md_bitmap_destroy(mddev);
			mddev->bitmap_info.offset = 0;
			md_bitmap_set_none(mddev);
		}
	}
	md_update_sb(mddev, 1);
@@ -9341,9 +9378,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)

static void md_end_clone_io(struct bio *bio)
{
	struct md_io_clone *md_io_clone = bio->bi_private;
	struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
						       bio_clone);
	struct bio *orig_bio = md_io_clone->orig_bio;
	struct mddev *mddev = md_io_clone->mddev;
	struct completion *reshape_completion = bio->bi_private;

	if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
		md_bitmap_end(mddev, md_io_clone);
@@ -9355,6 +9394,9 @@ static void md_end_clone_io(struct bio *bio)
		bio_end_io_acct(orig_bio, md_io_clone->start_time);

	bio_put(bio);
	if (unlikely(reshape_completion))
		complete(reshape_completion);
	else
		bio_endio(orig_bio);
	percpu_ref_put(&mddev->active_io);
}
@@ -9380,7 +9422,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
	}

	clone->bi_end_io = md_end_clone_io;
	clone->bi_private = md_io_clone;
	clone->bi_private = NULL;
	*bio = clone;
}

@@ -9391,26 +9433,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
}
EXPORT_SYMBOL_GPL(md_account_bio);

void md_free_cloned_bio(struct bio *bio)
{
	struct md_io_clone *md_io_clone = bio->bi_private;
	struct bio *orig_bio = md_io_clone->orig_bio;
	struct mddev *mddev = md_io_clone->mddev;

	if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
		md_bitmap_end(mddev, md_io_clone);

	if (bio->bi_status && !orig_bio->bi_status)
		orig_bio->bi_status = bio->bi_status;

	if (md_io_clone->start_time)
		bio_end_io_acct(orig_bio, md_io_clone->start_time);

	bio_put(bio);
	percpu_ref_put(&mddev->active_io);
}
EXPORT_SYMBOL_GPL(md_free_cloned_bio);

/* md_allow_write(mddev)
 * Calling this ensures that the array is marked 'active' so that writes
 * may proceed without blocking.  It is important to call this before
Loading