Commit 67bd9413 authored by Bart Van Assche's avatar Bart Van Assche Committed by James Bottomley

[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: default avatarJun'ichi Nomura <j-nomura@ce.jp.nec.com>
Signed-off-by: default avatarBart Van Assche <bvanassche@acm.org>
Reviewed-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Reviewed-by: default avatarTejun Heo <tj@kernel.org>
Cc: <stable@kernel.org>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent e81ca6fe
...@@ -290,6 +290,7 @@ static void scsi_host_dev_release(struct device *dev) ...@@ -290,6 +290,7 @@ static void scsi_host_dev_release(struct device *dev)
struct Scsi_Host *shost = dev_to_shost(dev); struct Scsi_Host *shost = dev_to_shost(dev);
struct device *parent = dev->parent; struct device *parent = dev->parent;
struct request_queue *q; struct request_queue *q;
void *queuedata;
scsi_proc_hostdir_rm(shost->hostt); scsi_proc_hostdir_rm(shost->hostt);
...@@ -299,9 +300,9 @@ static void scsi_host_dev_release(struct device *dev) ...@@ -299,9 +300,9 @@ static void scsi_host_dev_release(struct device *dev)
destroy_workqueue(shost->work_q); destroy_workqueue(shost->work_q);
q = shost->uspace_req_q; q = shost->uspace_req_q;
if (q) { if (q) {
kfree(q->queuedata); queuedata = q->queuedata;
q->queuedata = NULL; blk_cleanup_queue(q);
scsi_free_queue(q); kfree(queuedata);
} }
scsi_destroy_command_freelist(shost); scsi_destroy_command_freelist(shost);
......
...@@ -406,10 +406,6 @@ static void scsi_run_queue(struct request_queue *q) ...@@ -406,10 +406,6 @@ static void scsi_run_queue(struct request_queue *q)
LIST_HEAD(starved_list); LIST_HEAD(starved_list);
unsigned long flags; unsigned long flags;
/* if the device is dead, sdev will be NULL, so no queue to run */
if (!sdev)
return;
shost = sdev->host; shost = sdev->host;
if (scsi_target(sdev)->single_lun) if (scsi_target(sdev)->single_lun)
scsi_single_lun_run(sdev); scsi_single_lun_run(sdev);
...@@ -1371,16 +1367,16 @@ static inline int scsi_host_queue_ready(struct request_queue *q, ...@@ -1371,16 +1367,16 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
* may be changed after request stacking drivers call the function, * may be changed after request stacking drivers call the function,
* regardless of taking lock or not. * regardless of taking lock or not.
* *
* When scsi can't dispatch I/Os anymore and needs to kill I/Os * When scsi can't dispatch I/Os anymore and needs to kill I/Os scsi
* (e.g. !sdev), scsi needs to return 'not busy'. * needs to return 'not busy'. Otherwise, request stacking drivers
* Otherwise, request stacking drivers may hold requests forever. * may hold requests forever.
*/ */
static int scsi_lld_busy(struct request_queue *q) static int scsi_lld_busy(struct request_queue *q)
{ {
struct scsi_device *sdev = q->queuedata; struct scsi_device *sdev = q->queuedata;
struct Scsi_Host *shost; struct Scsi_Host *shost;
if (!sdev) if (blk_queue_dead(q))
return 0; return 0;
shost = sdev->host; shost = sdev->host;
...@@ -1491,12 +1487,6 @@ static void scsi_request_fn(struct request_queue *q) ...@@ -1491,12 +1487,6 @@ static void scsi_request_fn(struct request_queue *q)
struct scsi_cmnd *cmd; struct scsi_cmnd *cmd;
struct request *req; struct request *req;
if (!sdev) {
while ((req = blk_peek_request(q)) != NULL)
scsi_kill_request(req, q);
return;
}
if(!get_device(&sdev->sdev_gendev)) if(!get_device(&sdev->sdev_gendev))
/* We must be tearing the block queue down already */ /* We must be tearing the block queue down already */
return; return;
...@@ -1698,20 +1688,6 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) ...@@ -1698,20 +1688,6 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
return q; return q;
} }
void scsi_free_queue(struct request_queue *q)
{
unsigned long flags;
WARN_ON(q->queuedata);
/* cause scsi_request_fn() to kill all non-finished requests */
spin_lock_irqsave(q->queue_lock, flags);
q->request_fn(q);
spin_unlock_irqrestore(q->queue_lock, flags);
blk_cleanup_queue(q);
}
/* /*
* Function: scsi_block_requests() * Function: scsi_block_requests()
* *
......
...@@ -85,7 +85,6 @@ extern void scsi_next_command(struct scsi_cmnd *cmd); ...@@ -85,7 +85,6 @@ extern void scsi_next_command(struct scsi_cmnd *cmd);
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
extern void scsi_run_host_queues(struct Scsi_Host *shost); extern void scsi_run_host_queues(struct Scsi_Host *shost);
extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev); extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
extern void scsi_free_queue(struct request_queue *q);
extern int scsi_init_queue(void); extern int scsi_init_queue(void);
extern void scsi_exit_queue(void); extern void scsi_exit_queue(void);
struct request_queue; struct request_queue;
......
...@@ -972,11 +972,8 @@ void __scsi_remove_device(struct scsi_device *sdev) ...@@ -972,11 +972,8 @@ void __scsi_remove_device(struct scsi_device *sdev)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
transport_destroy_device(dev); transport_destroy_device(dev);
/* cause the request function to reject all I/O requests */
sdev->request_queue->queuedata = NULL;
/* Freeing the queue signals to block that we're done */ /* Freeing the queue signals to block that we're done */
scsi_free_queue(sdev->request_queue); blk_cleanup_queue(sdev->request_queue);
put_device(dev); put_device(dev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment