From: Ivan Vecera <ivecera@redhat.com> Date: Tue, 19 Aug 2008 11:44:32 +0200 Subject: [scsi] cciss: possible race condition during init Message-id: 48AA9600.6070400@redhat.com O-Subject: [RHEL5 PATCH] Possible race condition in cciss Bugzilla: 455663 RH-Acked-by: David Milburn <dmilburn@redhat.com> RH-Acked-by: David Milburn <dmilburn@redhat.com> RH-Acked-by: David Milburn <dmilburn@redhat.com> RH-Acked-by: David Milburn <dmilburn@redhat.com> RH-Acked-by: John Feeney <jfeeney@redhat.com> RH-Acked-by: John Feeney <jfeeney@redhat.com> RH-Acked-by: Anton Arapov <aarapov@redhat.com> RH-Acked-by: Tomas Henzl <thenzl@redhat.com> BZ: https://bugzilla.redhat.com/show_bug.cgi?id=455663 Description: There is a potential race condition during init where the interrupt handler may fire on a queue that has not been completely set up. The results is a kernel panic. Testing status: Patched kernel was tested by the bug reporter. No problems found. Upstream status: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=e14ac67026b46e94478134df9521682fc6501ac0 http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=77b96bd7e5ee0b44aed1b77fef5949bc19e8301f Please review the patch below. Regards, Ivan === diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 93a247c..a108669 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1364,6 +1364,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; @@ -1434,6 +1438,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); } @@ -3677,10 +3685,21 @@ 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]); + + /* we must register the controller even if no disks exist */ + if (hba[i]->highest_lun == -1) + add_disk(hba[i]->gendisk[0]); + return 1; clean4: