[PATCH v2 2/3] scsi: Make scsi_forget_host() wait for request queue removal

Bart Van Assche
2022-06-30T21:37:48Z

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@lst.de>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Hannes Reinecke <hare@suse.de>
Cc: John Garry <john.garry@huawei.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
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;