aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscameron@beardog.cca.cpqcorp.net <scameron@beardog.cca.cpqcorp.net>2008-04-17 13:19:03 +0200
committerJens Axboe <jens.axboe@oracle.com>2008-04-21 09:50:09 +0200
commite14ac67026b46e94478134df9521682fc6501ac0 (patch)
treeea6f6e2e818f5fbb930c9f40a9a0431aef713494
parentf18573abcc57844a7c3c12699d40eead8728cd8a (diff)
cciss: Fix race between disk-adding code and interrupt handler
Fix race condition between cciss_init_one(), cciss_update_drive_info(), and cciss_check_queues(). Signed-off-by: Stephen M. Cameron <scameron@beardog.cca.cpqcorp.net> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r--drivers/block/cciss.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 9c9627e8e334..cf6083a1f928 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1349,6 +1349,10 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
h->drv[drv_index].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+ /* deregister_disk sets h->drv[drv_index].queue = NULL */
+ /* which keeps the interrupt handler from starting */
+ /* the queue. */
ret = deregister_disk(h->gendisk[drv_index],
&h->drv[drv_index], 0);
h->drv[drv_index].busy_configuring = 0;
@@ -1419,6 +1423,10 @@ geo_inq:
blk_queue_hardsect_size(disk->queue,
hba[ctlr]->drv[drv_index].block_size);
+ /* Make sure all queue data is written out before */
+ /* setting h->drv[drv_index].queue, as setting this */
+ /* allows the interrupt handler to start the queue */
+ wmb();
h->drv[drv_index].queue = disk->queue;
add_disk(disk);
}
@@ -3520,10 +3528,17 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
continue;
blk_queue_hardsect_size(q, drv->block_size);
set_capacity(disk, drv->nr_blocks);
- add_disk(disk);
j++;
} while (j <= hba[i]->highest_lun);
+ /* Make sure all queue data is written out before */
+ /* interrupt handler, triggered by add_disk, */
+ /* is allowed to start them. */
+ wmb();
+
+ for (j = 0; j <= hba[i]->highest_lun; j++)
+ add_disk(hba[i]->gendisk[j]);
+
return 1;
clean4: