[PATCH] fix NMI lockup with CFQ scheduler
<axboe@suse.de> [Tue, 12 Apr 2005 21:22:06 +0000 (16:22 -0500)]
The current problem seen is that the queue lock is actually in the
SCSI device structure, so when that structure is freed on device
release, we go boom if the queue tries to access the lock again.

The fix here is to move the lock from the scsi_device to the queue.

Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>

drivers/block/ll_rw_blk.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_scan.c
include/linux/blkdev.h
include/scsi/scsi_device.h

index 46e54b4..11ef9d9 100644 (file)
@@ -1715,6 +1715,15 @@ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
        if (blk_init_free_list(q))
                goto out_init;
 
+       /*
+        * if caller didn't supply a lock, they get per-queue locking with
+        * our embedded lock
+        */
+       if (!lock) {
+               spin_lock_init(&q->__queue_lock);
+               lock = &q->__queue_lock;
+       }
+
        q->request_fn           = rfn;
        q->back_merge_fn        = ll_back_merge_fn;
        q->front_merge_fn       = ll_front_merge_fn;
index 7cbc412..d230c69 100644 (file)
@@ -360,9 +360,9 @@ void scsi_device_unbusy(struct scsi_device *sdev)
                     shost->host_failed))
                scsi_eh_wakeup(shost);
        spin_unlock(shost->host_lock);
-       spin_lock(&sdev->sdev_lock);
+       spin_lock(sdev->request_queue->queue_lock);
        sdev->device_busy--;
-       spin_unlock_irqrestore(&sdev->sdev_lock, flags);
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 }
 
 /*
@@ -1425,7 +1425,7 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
        struct Scsi_Host *shost = sdev->host;
        struct request_queue *q;
 
-       q = blk_init_queue(scsi_request_fn, &sdev->sdev_lock);
+       q = blk_init_queue(scsi_request_fn, NULL);
        if (!q)
                return NULL;
 
index a8a37a3..287d197 100644 (file)
@@ -249,7 +249,6 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
         */
        sdev->borken = 1;
 
-       spin_lock_init(&sdev->sdev_lock);
        sdev->request_queue = scsi_alloc_queue(sdev);
        if (!sdev->request_queue) {
                /* release fn is set up in scsi_sysfs_device_initialise, so
index 70ac286..ef1afc1 100644 (file)
@@ -355,8 +355,11 @@ struct request_queue
        unsigned long           queue_flags;
 
        /*
-        * protects queue structures from reentrancy
+        * protects queue structures from reentrancy. ->__queue_lock should
+        * _never_ be used directly, it is queue private. always use
+        * ->queue_lock.
         */
+       spinlock_t              __queue_lock;
        spinlock_t              *queue_lock;
 
        /*
index 07d9740..f6d0513 100644 (file)
@@ -44,7 +44,6 @@ struct scsi_device {
        struct list_head    same_target_siblings; /* just the devices sharing same target id */
 
        volatile unsigned short device_busy;    /* commands actually active on low-level */
-       spinlock_t sdev_lock;           /* also the request queue_lock */
        spinlock_t list_lock;
        struct list_head cmd_list;      /* queue of in use SCSI Command structures */
        struct list_head starved_entry;