Commit f9cfe7e7 authored by Yu Kuai's avatar Yu Kuai Committed by Song Liu
Browse files

md: Fix md_seq_ops() regressions



Commit cf1b6d44 ("md: simplify md_seq_ops") introduce following
regressions:

1) If list all_mddevs is emptly, personalities and unused devices won't
   be showed to user anymore.
2) If seq_file buffer overflowed from md_seq_show(), then md_seq_start()
   will be called again, hence personalities will be showed to user
   again.
3) If seq_file buffer overflowed from md_seq_stop(), seq_read_iter()
   doesn't handle this, hence unused devices won't be showed to user.

Fix above problems by printing personalities and unused devices in
md_seq_show().

Fixes: cf1b6d44 ("md: simplify md_seq_ops")
Cc: stable@vger.kernel.org # v6.7+
Signed-off-by: default avatarYu Kuai <yukuai3@huawei.com>
Signed-off-by: default avatarSong Liu <song@kernel.org>
Link: https://lore.kernel.org/r/20240109133957.2975272-1-yukuai1@huaweicloud.com
parent 53889bca
Loading
Loading
Loading
Loading
+27 −13
Original line number Diff line number Diff line
@@ -8135,6 +8135,19 @@ static void status_unused(struct seq_file *seq)
	seq_printf(seq, "\n");
}

static void status_personalities(struct seq_file *seq)
{
	struct md_personality *pers;

	seq_puts(seq, "Personalities : ");
	spin_lock(&pers_lock);
	list_for_each_entry(pers, &pers_list, list)
		seq_printf(seq, "[%s] ", pers->name);

	spin_unlock(&pers_lock);
	seq_puts(seq, "\n");
}

static int status_resync(struct seq_file *seq, struct mddev *mddev)
{
	sector_t max_sectors, resync, res;
@@ -8276,20 +8289,10 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev)
static void *md_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(&all_mddevs_lock)
{
	struct md_personality *pers;

	seq_puts(seq, "Personalities : ");
	spin_lock(&pers_lock);
	list_for_each_entry(pers, &pers_list, list)
		seq_printf(seq, "[%s] ", pers->name);

	spin_unlock(&pers_lock);
	seq_puts(seq, "\n");
	seq->poll_event = atomic_read(&md_event_count);

	spin_lock(&all_mddevs_lock);

	return seq_list_start(&all_mddevs, *pos);
	return seq_list_start_head(&all_mddevs, *pos);
}

static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
@@ -8300,16 +8303,23 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void md_seq_stop(struct seq_file *seq, void *v)
	__releases(&all_mddevs_lock)
{
	status_unused(seq);
	spin_unlock(&all_mddevs_lock);
}

static int md_seq_show(struct seq_file *seq, void *v)
{
	struct mddev *mddev = list_entry(v, struct mddev, all_mddevs);
	struct mddev *mddev;
	sector_t sectors;
	struct md_rdev *rdev;

	if (v == &all_mddevs) {
		status_personalities(seq);
		if (list_empty(&all_mddevs))
			status_unused(seq);
		return 0;
	}

	mddev = list_entry(v, struct mddev, all_mddevs);
	if (!mddev_get(mddev))
		return 0;

@@ -8385,6 +8395,10 @@ static int md_seq_show(struct seq_file *seq, void *v)
	}
	spin_unlock(&mddev->lock);
	spin_lock(&all_mddevs_lock);

	if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs))
		status_unused(seq);

	if (atomic_dec_and_test(&mddev->active))
		__mddev_put(mddev);