On Tue, Sep 12, 2023 at 6:02 AM Mariusz Tkaczyk <mariusz.tkaczyk@xxxxxxxxxxxxxxx> wrote: > > During working on changes proposed by Kuai [1], I determined that > mddev->active is continusly decremented for array marked by MD_CLOSING. > It brought me to md_seq_next() changed by [2]. I determined the regression > here, if mddev_get() fails we updated mddev pointer and as a result we > _put failed device. > > I isolated the change in md_seq_next() and tested it- issue is no longer > reproducible but I don't see the root cause in this scenario. The bug > is obvious so I proceed with fixing. I will submit MD_CLOSING patches > separatelly. > > Put the device which has been _get with previous md_seq_next() call. > Add guard for inproper mddev_put usage(). It shouldn't be called if > there are less than 1 for mddev->active. > > I didn't convert atomic_t to refcount_t because refcount warns when 0 is > achieved which is likely to happen for mddev->active. > > [1] https://lore.kernel.org/linux-raid/028a21df-4397-80aa-c2a5-7c754560f595@xxxxxxxxx/T/#m6a534677d9654a4236623661c10646d45419ee1b > [2] https://bugzilla.kernel.org/show_bug.cgi?id=217798 > > Fixes: 12a6caf27324 ("md: only delete entries from all_mddevs when the disk is freed") > Cc: Yu Kuai <yukuai3@xxxxxxxxxx> > Cc: AceLan Kao <acelan@xxxxxxxxx> > Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@xxxxxxxxxxxxxxx> > --- > drivers/md/md.c | 20 +++++++++++--------- > 1 file changed, 11 insertions(+), 9 deletions(-) > > diff --git a/drivers/md/md.c b/drivers/md/md.c > index 0fe7ab6e8ab9..bb654ff62765 100644 > --- a/drivers/md/md.c > +++ b/drivers/md/md.c > @@ -618,6 +618,12 @@ static void mddev_delayed_delete(struct work_struct *ws); > > void mddev_put(struct mddev *mddev) > { > + /* Guard for ambiguous put. */ > + if (unlikely(atomic_read(&mddev->active) < 1)) { > + pr_warn("%s: active refcount is lower than 1\n", mdname(mddev)); > + return; > + } > + Could you please explain why we need this guard? We should probably fix the caller that causes this. > if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock)) > return; > if (!mddev->raid_disks && list_empty(&mddev->disks) && > @@ -8227,19 +8233,16 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) > { > struct list_head *tmp; > struct mddev *next_mddev, *mddev = v; > - struct mddev *to_put = NULL; IIUC, all we need is the following: diff --git i/drivers/md/md.c w/drivers/md/md.c index 73758b754127..a104a025084d 100644 --- i/drivers/md/md.c +++ w/drivers/md/md.c @@ -8265,7 +8265,7 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) spin_unlock(&all_mddevs_lock); if (to_put) - mddev_put(mddev); + mddev_put(to_put); return next_mddev; } Is this sufficient? If so, how about we ship this first and refactor the code later in a separate patch? Thanks, Song