Skip to content

Commit fa2bbff

Browse files
YuKuai-huaweiliu-song-6
authored andcommitted
md: synchronize flush io with array reconfiguration
Currently rcu is used to protect iterating rdev from submit_flushes(): submit_flushes remove_and_add_spares synchronize_rcu pers->hot_remove_disk() rcu_read_lock() rdev_for_each_rcu if (rdev->raid_disk >= 0) rdev->radi_disk = -1; atomic_inc(&rdev->nr_pending) rcu_read_unlock() bi = bio_alloc_bioset() bi->bi_end_io = md_end_flush bi->private = rdev submit_bio // issue io for removed rdev Fix this problem by grabbing 'acive_io' before iterating rdev, make sure that remove_and_add_spares() won't concurrent with submit_flushes(). Fixes: a2826aa ("md: support barrier requests on all personalities.") Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 15da990 commit fa2bbff

File tree

1 file changed

+16
-6
lines changed

1 file changed

+16
-6
lines changed

drivers/md/md.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ static void md_end_flush(struct bio *bio)
529529
rdev_dec_pending(rdev, mddev);
530530

531531
if (atomic_dec_and_test(&mddev->flush_pending)) {
532+
/* The pair is percpu_ref_get() from md_flush_request() */
533+
percpu_ref_put(&mddev->active_io);
534+
532535
/* The pre-request flush has finished */
533536
queue_work(md_wq, &mddev->flush_work);
534537
}
@@ -548,12 +551,8 @@ static void submit_flushes(struct work_struct *ws)
548551
rdev_for_each_rcu(rdev, mddev)
549552
if (rdev->raid_disk >= 0 &&
550553
!test_bit(Faulty, &rdev->flags)) {
551-
/* Take two references, one is dropped
552-
* when request finishes, one after
553-
* we reclaim rcu_read_lock
554-
*/
555554
struct bio *bi;
556-
atomic_inc(&rdev->nr_pending);
555+
557556
atomic_inc(&rdev->nr_pending);
558557
rcu_read_unlock();
559558
bi = bio_alloc_bioset(rdev->bdev, 0,
@@ -564,7 +563,6 @@ static void submit_flushes(struct work_struct *ws)
564563
atomic_inc(&mddev->flush_pending);
565564
submit_bio(bi);
566565
rcu_read_lock();
567-
rdev_dec_pending(rdev, mddev);
568566
}
569567
rcu_read_unlock();
570568
if (atomic_dec_and_test(&mddev->flush_pending))
@@ -617,6 +615,18 @@ bool md_flush_request(struct mddev *mddev, struct bio *bio)
617615
/* new request after previous flush is completed */
618616
if (ktime_after(req_start, mddev->prev_flush_start)) {
619617
WARN_ON(mddev->flush_bio);
618+
/*
619+
* Grab a reference to make sure mddev_suspend() will wait for
620+
* this flush to be done.
621+
*
622+
* md_flush_reqeust() is called under md_handle_request() and
623+
* 'active_io' is already grabbed, hence percpu_ref_is_zero()
624+
* won't pass, percpu_ref_tryget_live() can't be used because
625+
* percpu_ref_kill() can be called by mddev_suspend()
626+
* concurrently.
627+
*/
628+
WARN_ON(percpu_ref_is_zero(&mddev->active_io));
629+
percpu_ref_get(&mddev->active_io);
620630
mddev->flush_bio = bio;
621631
bio = NULL;
622632
}

0 commit comments

Comments
 (0)