summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2010-08-07 21:17:00 +1000
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-13 13:50:36 -0700
commitea77f594f059adbc1f5b92650890a86b8464151b (patch)
tree89328dc1d22a16b61ed520d5bd725047f5c290e1
parent0945d6a3e9242b63be63c31f4759098f94accf9e (diff)
downloadlwn-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.c18
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: