Prepare for freeing the host tag set earlier by making scsi_forget_host() wait until all activity on the host tag set has stopped. Cc: Christoph Hellwig <hch@xxxxxx> Cc: Ming Lei <ming.lei@xxxxxxxxxx> Cc: Hannes Reinecke <hare@xxxxxxx> Cc: John Garry <john.garry@xxxxxxxxxx> Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- drivers/scsi/scsi_scan.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 5c3bb4ceeac3..c8331ccdde95 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1961,6 +1961,16 @@ void scsi_scan_host(struct Scsi_Host *shost) } EXPORT_SYMBOL(scsi_scan_host); +/** + * scsi_forget_host() - Remove all SCSI devices from a host. + * @shost: SCSI host to remove devices from. + * + * Removes all SCSI devices that have not yet been removed. For the SCSI devices + * for which removal started before scsi_forget_host(), wait until the + * associated request queue has reached the "dead" state. In that state it is + * guaranteed that no new requests will be allocated and also that no requests + * are in progress anymore. + */ void scsi_forget_host(struct Scsi_Host *shost) { struct scsi_device *sdev; @@ -1970,8 +1980,21 @@ void scsi_forget_host(struct Scsi_Host *shost) restart: spin_lock_irq(shost->host_lock); list_for_each_entry(sdev, &shost->__devices, siblings) { - if (sdev->sdev_state == SDEV_DEL) + if (sdev->sdev_state == SDEV_DEL && + blk_queue_dead(sdev->request_queue)) { continue; + } + if (sdev->sdev_state == SDEV_DEL) { + get_device(&sdev->sdev_gendev); + spin_unlock_irq(shost->host_lock); + + while (!blk_queue_dead(sdev->request_queue)) + msleep(10); + + spin_lock_irq(shost->host_lock); + put_device(&sdev->sdev_gendev); + goto restart; + } spin_unlock_irq(shost->host_lock); __scsi_remove_device(sdev); goto restart;