[SCSI] fix crash in scsi_dispatch_cmd()
authorJames Bottomley <James.Bottomley@HansenPartnership.com>
Thu, 7 Jul 2011 20:45:40 +0000 (15:45 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Thu, 21 Jul 2011 21:21:18 +0000 (14:21 -0700)
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>
block/blk-core.c
block/blk-exec.c
drivers/scsi/scsi_lib.c

index d2f8f4049abddbf76d3414cd85ef36d2d4027d07..1d49e1c7c905cd1dadd8daf286205cee24e055b9 100644 (file)
@@ -839,6 +839,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 8a0e7ec056e7a3bd6db31f19ec72202ae97c581e..a1ebceb332f963ee366aa7ebed60d69c4e1c11cb 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 ec1803a48723dc950f56eb3fddb33456ea8d9a61..28d9c9d6b4b401f926eb62709efe9027e0d4cfaf 100644 (file)
@@ -213,6 +213,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))