aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2011-07-07 16:45:40 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-07-21 17:21:18 -0400
commitbfe159a51203c15d23cb3158fffdc25ec4b4dda1 (patch)
tree553b2d7b1422545032cf3c048db7dff5f5c8dbd3
parent79b9677d885d1a792bc103f2febb06f91f92de43 (diff)
[SCSI] fix crash in scsi_dispatch_cmd()
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>
-rw-r--r--block/blk-core.c3
-rw-r--r--block/blk-exec.c7
-rw-r--r--drivers/scsi/scsi_lib.c2
3 files changed, 12 insertions, 0 deletions
diff --git a/block/blk-core.c b/block/blk-core.c
index d2f8f4049abd..1d49e1c7c905 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -839,6 +839,9 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
839{ 839{
840 struct request *rq; 840 struct request *rq;
841 841
842 if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
843 return NULL;
844
842 BUG_ON(rw != READ && rw != WRITE); 845 BUG_ON(rw != READ && rw != WRITE);
843 846
844 spin_lock_irq(q->queue_lock); 847 spin_lock_irq(q->queue_lock);
diff --git a/block/blk-exec.c b/block/blk-exec.c
index 8a0e7ec056e7..a1ebceb332f9 100644
--- a/block/blk-exec.c
+++ b/block/blk-exec.c
@@ -50,6 +50,13 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
50{ 50{
51 int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; 51 int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
52 52
53 if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
54 rq->errors = -ENXIO;
55 if (rq->end_io)
56 rq->end_io(rq, rq->errors);
57 return;
58 }
59
53 rq->rq_disk = bd_disk; 60 rq->rq_disk = bd_disk;
54 rq->end_io = done; 61 rq->end_io = done;
55 WARN_ON(irqs_disabled()); 62 WARN_ON(irqs_disabled());
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index ec1803a48723..28d9c9d6b4b4 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -213,6 +213,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
213 int ret = DRIVER_ERROR << 24; 213 int ret = DRIVER_ERROR << 24;
214 214
215 req = blk_get_request(sdev->request_queue, write, __GFP_WAIT); 215 req = blk_get_request(sdev->request_queue, write, __GFP_WAIT);
216 if (!req)
217 return ret;
216 218
217 if (bufflen && blk_rq_map_kern(sdev->request_queue, req, 219 if (bufflen && blk_rq_map_kern(sdev->request_queue, req,
218 buffer, bufflen, __GFP_WAIT)) 220 buffer, bufflen, __GFP_WAIT))