diff options
author | Bart Van Assche <bvanassche@acm.org> | 2012-06-29 11:33:22 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-07-20 03:58:40 -0400 |
commit | 67bd94130015c507011af37858989b199c52e1de (patch) | |
tree | 9b4bc8ef5c7180d920eae0f95158dc377c38df8c /drivers/scsi/hosts.c | |
parent | e81ca6fe85b77109a32489a5db82f575d51dfc98 (diff) |
[SCSI] Fix device removal NULL pointer dereference
Use blk_queue_dead() to test whether the queue is dead instead
of !sdev. Since scsi_prep_fn() may be invoked concurrently with
__scsi_remove_device(), keep the queuedata (sdev) pointer in
__scsi_remove_device(). This patch fixes a kernel oops that
can be triggered by USB device removal. See also
http://www.spinics.net/lists/linux-scsi/msg56254.html.
Other changes included in this patch:
- Swap the blk_cleanup_queue() and kfree() calls in
scsi_host_dev_release() to make that code easier to grasp.
- Remove the queue dead check from scsi_run_queue() since the
queue state can change anyway at any point in that function
where the queue lock is not held.
- Remove the queue dead check from the start of scsi_request_fn()
since it is redundant with the scsi_device_online() check.
Reported-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Reviewed-by: Tejun Heo <tj@kernel.org>
Cc: <stable@kernel.org>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 2b6a03de5787..593085a52275 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -290,6 +290,7 @@ static void scsi_host_dev_release(struct device *dev) | |||
290 | struct Scsi_Host *shost = dev_to_shost(dev); | 290 | struct Scsi_Host *shost = dev_to_shost(dev); |
291 | struct device *parent = dev->parent; | 291 | struct device *parent = dev->parent; |
292 | struct request_queue *q; | 292 | struct request_queue *q; |
293 | void *queuedata; | ||
293 | 294 | ||
294 | scsi_proc_hostdir_rm(shost->hostt); | 295 | scsi_proc_hostdir_rm(shost->hostt); |
295 | 296 | ||
@@ -299,9 +300,9 @@ static void scsi_host_dev_release(struct device *dev) | |||
299 | destroy_workqueue(shost->work_q); | 300 | destroy_workqueue(shost->work_q); |
300 | q = shost->uspace_req_q; | 301 | q = shost->uspace_req_q; |
301 | if (q) { | 302 | if (q) { |
302 | kfree(q->queuedata); | 303 | queuedata = q->queuedata; |
303 | q->queuedata = NULL; | 304 | blk_cleanup_queue(q); |
304 | scsi_free_queue(q); | 305 | kfree(queuedata); |
305 | } | 306 | } |
306 | 307 | ||
307 | scsi_destroy_command_freelist(shost); | 308 | scsi_destroy_command_freelist(shost); |