diff options
author | NeilBrown <neilb@suse.de> | 2010-08-07 21:17:00 +1000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-13 13:50:36 -0700 |
commit | ea77f594f059adbc1f5b92650890a86b8464151b (patch) | |
tree | 89328dc1d22a16b61ed520d5bd725047f5c290e1 | |
parent | 0945d6a3e9242b63be63c31f4759098f94accf9e (diff) | |
download | lwn-ea77f594f059adbc1f5b92650890a86b8464151b.tar.gz lwn-ea77f594f059adbc1f5b92650890a86b8464151b.zip |
md/raid10: fix deadlock with unaligned read during resync
commit 51e9ac77035a3dfcb6fc0a88a0d80b6f99b5edb1 upstream.
If the 'bio_split' path in raid10-read is used while
resync/recovery is happening it is possible to deadlock.
Fix this be elevating ->nr_waiting for the duration of both
parts of the split request.
This fixes a bug that has been present since 2.6.22
but has only started manifesting recently for unknown reasons.
It is suitable for and -stable since then.
Reported-by: Justin Bronder <jsbronder@gentoo.org>
Tested-by: Justin Bronder <jsbronder@gentoo.org>
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/md/raid10.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index dbf51e9f0dc6..6fcedbe31fe8 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -818,11 +818,29 @@ static int make_request(struct request_queue *q, struct bio * bio) */ bp = bio_split(bio, bio_split_pool, chunk_sects - (bio->bi_sector & (chunk_sects - 1)) ); + + /* Each of these 'make_request' calls will call 'wait_barrier'. + * If the first succeeds but the second blocks due to the resync + * thread raising the barrier, we will deadlock because the + * IO to the underlying device will be queued in generic_make_request + * and will never complete, so will never reduce nr_pending. + * So increment nr_waiting here so no new raise_barriers will + * succeed, and so the second wait_barrier cannot block. + */ + spin_lock_irq(&conf->resync_lock); + conf->nr_waiting++; + spin_unlock_irq(&conf->resync_lock); + if (make_request(q, &bp->bio1)) generic_make_request(&bp->bio1); if (make_request(q, &bp->bio2)) generic_make_request(&bp->bio2); + spin_lock_irq(&conf->resync_lock); + conf->nr_waiting--; + wake_up(&conf->wait_barrier); + spin_unlock_irq(&conf->resync_lock); + bio_pair_release(bp); return 0; bad_map: |