Rediffed for RHEL7.4.z.
Conflicts: The return value of md_make_request is different from RHEL to upstream. And RHEL7 MD code does not use bio->bi_opf, but bio->bi_rw. -Nigel Croxon
commit 393debc23c7820211d1c8253dd6a8408a7628fe7 Author: Shaohua Li shli@fb.com Date: Thu Sep 21 10:23:35 2017 -0700
md: separate request handling
With commit cc27b0c78c79, pers->make_request could bail out without handling the bio. If that happens, we should retry. The commit fixes md_make_request but not other call sites. Separate the request handling part, so other call sites can use it.
Reported-by: Nate Dailey nate.dailey@stratus.com Fix: cc27b0c78c79(md: fix deadlock between mddev_suspend() and md_write_start()) Cc: stable@vger.kernel.org Reviewed-by: NeilBrown neilb@suse.com Signed-off-by: Shaohua Li shli@fb.com Signed-off-by: Denys Vlasenko dvlasenk@redhat.com
diff --git a/drivers/md/md.c b/drivers/md/md.c index 85fe7a99290..407e15f4bfe 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -248,21 +248,8 @@ static DEFINE_SPINLOCK(all_mddevs_lock); * call has finished, the bio has been linked into some internal structure * and so is visible to ->quiesce(), so we don't need the refcount any more. */ -static void md_make_request(struct request_queue *q, struct bio *bio) +void md_handle_request(struct mddev *mddev, struct bio *bio) { - const int rw = bio_data_dir(bio); - struct mddev *mddev = q->queuedata; - int cpu; - unsigned int sectors; - - if (mddev == NULL || mddev->pers == NULL) { - bio_io_error(bio); - return; - } - if (mddev->ro == 1 && unlikely(rw == WRITE)) { - bio_endio(bio, bio_sectors(bio) == 0 ? 0 : -EROFS); - return; - } check_suspended: smp_rmb(); /* Ensure implications of 'active' are visible */ rcu_read_lock(); @@ -282,24 +269,45 @@ check_suspended: atomic_inc(&mddev->active_io); rcu_read_unlock();
- /* - * save the sectors now since our bio can - * go away inside make_request - */ - sectors = bio_sectors(bio); if (!mddev->pers->make_request(mddev, bio)) { atomic_dec(&mddev->active_io); wake_up(&mddev->sb_wait); goto check_suspended; }
+ if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended) + wake_up(&mddev->sb_wait); +} +EXPORT_SYMBOL(md_handle_request); + +static void md_make_request(struct request_queue *q, struct bio *bio) +{ + const int rw = bio_data_dir(bio); + struct mddev *mddev = q->queuedata; + int cpu; + unsigned int sectors; + + if (mddev == NULL || mddev->pers == NULL) { + bio_io_error(bio); + return; + } + if (mddev->ro == 1 && unlikely(rw == WRITE)) { + bio_endio(bio, bio_sectors(bio) == 0 ? 0 : -EROFS); + return; + } + + /* + * save the sectors now since our bio can + * go away inside make_request + */ + sectors = bio_sectors(bio); + + md_handle_request(mddev, bio); + cpu = part_stat_lock(); part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], sectors); part_stat_unlock(); - - if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended) - wake_up(&mddev->sb_wait); }
/* mddev_suspend makes sure no new requests are submitted diff --git a/drivers/md/md.h b/drivers/md/md.h index 0d13bf88f41..12e19d6d373 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -679,6 +679,7 @@ extern void md_stop_writes(struct mddev *mddev); extern int md_rdev_init(struct md_rdev *rdev); extern void md_rdev_clear(struct md_rdev *rdev);
+extern void md_handle_request(struct mddev *mddev, struct bio *bio); extern void mddev_suspend(struct mddev *mddev); extern void mddev_resume(struct mddev *mddev); extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,