md: Fix md_seq_ops() regressions

Commit cf1b6d4441ff ("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: cf1b6d4441ff ("md: simplify md_seq_ops")
Cc: stable@vger.kernel.org # v6.7+
Signed-off-by: Yu Kuai <yukuai3@huawei.com>
Signed-off-by: Song Liu <song@kernel.org>
Link: https://lore.kernel.org/r/20240109133957.2975272-1-yukuai1@huaweicloud.com
This commit is contained in:
Yu Kuai 2024-01-09 21:39:57 +08:00 committed by Song Liu
parent 53889bcaf5
commit f9cfe7e7f9

View File

@ -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);