forked from Minki/linux
scsi: core: Make sure that hosts outlive targets
Fix the race conditions between SCSI LLD kernel module unloading and SCSI device and target removal by making sure that SCSI hosts are destroyed after all associated target and device objects have been freed. Link: https://lore.kernel.org/r/20220728221851.1822295-3-bvanassche@acm.org Cc: Christoph Hellwig <hch@lst.de> Cc: Ming Lei <ming.lei@redhat.com> Cc: Mike Christie <michael.christie@oracle.com> Cc: Hannes Reinecke <hare@suse.de> Cc: John Garry <john.garry@huawei.com> Reviewed-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Ming Lei <ming.lei@redhat.com> Signed-off-by: Bart Van Assche <bvanassche@acm.org> [ bvanassche: Reworked Ming's patch and split it ] Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
fe44260419
commit
16728aaba6
@ -190,6 +190,13 @@ void scsi_remove_host(struct Scsi_Host *shost)
|
||||
transport_unregister_device(&shost->shost_gendev);
|
||||
device_unregister(&shost->shost_dev);
|
||||
device_del(&shost->shost_gendev);
|
||||
|
||||
/*
|
||||
* After scsi_remove_host() has returned the scsi LLD module can be
|
||||
* unloaded and/or the host resources can be released. Hence wait until
|
||||
* the dependent SCSI targets and devices are gone before returning.
|
||||
*/
|
||||
wait_event(shost->targets_wq, atomic_read(&shost->target_count) == 0);
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_remove_host);
|
||||
|
||||
@ -394,6 +401,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
|
||||
INIT_LIST_HEAD(&shost->starved_list);
|
||||
init_waitqueue_head(&shost->host_wait);
|
||||
mutex_init(&shost->scan_mutex);
|
||||
init_waitqueue_head(&shost->targets_wq);
|
||||
|
||||
index = ida_alloc(&host_index_ida, GFP_KERNEL);
|
||||
if (index < 0) {
|
||||
|
@ -406,9 +406,14 @@ static void scsi_target_destroy(struct scsi_target *starget)
|
||||
static void scsi_target_dev_release(struct device *dev)
|
||||
{
|
||||
struct device *parent = dev->parent;
|
||||
struct Scsi_Host *shost = dev_to_shost(parent);
|
||||
struct scsi_target *starget = to_scsi_target(dev);
|
||||
|
||||
kfree(starget);
|
||||
|
||||
if (atomic_dec_return(&shost->target_count) == 0)
|
||||
wake_up(&shost->targets_wq);
|
||||
|
||||
put_device(parent);
|
||||
}
|
||||
|
||||
@ -523,6 +528,8 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
|
||||
starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED;
|
||||
init_waitqueue_head(&starget->sdev_wq);
|
||||
|
||||
atomic_inc(&shost->target_count);
|
||||
|
||||
retry:
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
|
||||
|
@ -689,6 +689,9 @@ struct Scsi_Host {
|
||||
/* ldm bits */
|
||||
struct device shost_gendev, shost_dev;
|
||||
|
||||
atomic_t target_count;
|
||||
wait_queue_head_t targets_wq;
|
||||
|
||||
/*
|
||||
* Points to the transport data (if any) which is allocated
|
||||
* separately
|
||||
|
Loading…
Reference in New Issue
Block a user