summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Brown <neilb@suse.de>2007-08-23 01:02:57 +0200
committerAdrian Bunk <bunk@stusta.de>2007-08-23 01:02:57 +0200
commitcc49f09942ea753b8587d3f01362ffd311c98a00 (patch)
treecfb5a09d889d44da50d17c6828c68ac1662a3deb
parent26d0764d2bf7f6b8dcd5a99d85cc6b1c02b65da6 (diff)
md: fix a plug/unplug race in raid5
When a device is unplugged, requests are moved from one or two (depending on whether a bitmap is in use) queues to the main request queue. So whenever requests are put on either of those queues, we should make sure the raid5 array is 'plugged'. However we don't. We currently plug the raid5 queue just before putting requests on queues, so there is room for a race. If something unplugs the queue at just the wrong time, requests will be left on the queue and nothing will want to unplug them. Normally something else will plug and unplug the queue fairly soon, but there is a risk that nothing will. Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Adrian Bunk <bunk@kernel.org>
-rw-r--r--drivers/md/raid5.c18
1 files changed, 6 insertions, 12 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 2dba305daf3c..fa2e7c526afa 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -77,12 +77,14 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
if (atomic_read(&conf->active_stripes)==0)
BUG();
if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state))
+ if (test_bit(STRIPE_DELAYED, &sh->state)) {
list_add_tail(&sh->lru, &conf->delayed_list);
- else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- conf->seq_write == sh->bm_seq)
+ blk_plug_device(conf->mddev->queue);
+ } else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+ conf->seq_write == sh->bm_seq) {
list_add_tail(&sh->lru, &conf->bitmap_list);
- else {
+ blk_plug_device(conf->mddev->queue);
+ } else {
clear_bit(STRIPE_BIT_DELAY, &sh->state);
list_add_tail(&sh->lru, &conf->handle_list);
}
@@ -1519,13 +1521,6 @@ static int raid5_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
-static inline void raid5_plug_device(raid5_conf_t *conf)
-{
- spin_lock_irq(&conf->device_lock);
- blk_plug_device(conf->mddev->queue);
- spin_unlock_irq(&conf->device_lock);
-}
-
static int make_request (request_queue_t *q, struct bio * bi)
{
mddev_t *mddev = q->queuedata;
@@ -1577,7 +1572,6 @@ static int make_request (request_queue_t *q, struct bio * bi)
goto retry;
}
finish_wait(&conf->wait_for_overlap, &w);
- raid5_plug_device(conf);
handle_stripe(sh);
release_stripe(sh);