]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
fix crash in scsi_dispatch_cmd()
authorJames Bottomley <James.Bottomley@HansenPartnership.com>
Thu, 7 Jul 2011 20:45:40 +0000 (15:45 -0500)
committerAndi Kleen <ak@linux.intel.com>
Mon, 1 Aug 2011 20:55:00 +0000 (13:55 -0700)
[ upstream commit bfe159a51203c15d23cb3158fffdc25ec4b4dda1 ]

USB surprise removal of sr is triggering an oops in
scsi_dispatch_command().  What seems to be happening is that USB is
hanging on to a queue reference until the last close of the upper
device, so the crash is caused by surprise remove of a mounted CD
followed by attempted unmount.

The problem is that USB doesn't issue its final commands as part of
the SCSI teardown path, but on last close when the block queue is long
gone.  The long term fix is probably to make sr do the teardown in the
same way as sd (so remove all the lower bits on ejection, but keep the
upper disk alive until last close of user space).  However, the
current oops can be simply fixed by not allowing any commands to be
sent to a dead queue.

Cc: stable@kernel.org
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
block/blk-core.c
block/blk-exec.c
drivers/scsi/scsi_lib.c

index 3b3affd0885ba84d85527217a1a86bd5f3daa11e..1818a05d53b8ca9d961fa994559a09da04fa590b 100644 (file)
@@ -893,6 +893,9 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
 {
        struct request *rq;
 
+       if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
+               return NULL;
+
        BUG_ON(rw != READ && rw != WRITE);
 
        spin_lock_irq(q->queue_lock);
index 49557e91f0dab58cda736b556378525dbea497b8..85bd7b445d86a08bb0a6ea829081f06b52b7cb2c 100644 (file)
@@ -50,6 +50,13 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
 {
        int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
 
+       if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
+               rq->errors = -ENXIO;
+               if (rq->end_io)
+                       rq->end_io(rq, rq->errors);
+               return;
+       }
+
        rq->rq_disk = bd_disk;
        rq->end_io = done;
        WARN_ON(irqs_disabled());
index 725f3cdcedcf7ca25ba00c2aefffa56aef412130..56034eecc6e7776fcd4afd0701eba000dd3989c3 100644 (file)
@@ -215,6 +215,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
        int ret = DRIVER_ERROR << 24;
 
        req = blk_get_request(sdev->request_queue, write, __GFP_WAIT);
+       if (!req)
+               return ret;
 
        if (bufflen &&  blk_rq_map_kern(sdev->request_queue, req,
                                        buffer, bufflen, __GFP_WAIT))