From ef4f7e4bf1dc26aaa86cf8e5a13013684139be51 Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Thu, 28 Jul 2022 00:41:25 +0300 Subject: scsi: target: core: De-RCU of se_lun and se_lun acl se_lun and se_lun_acl are immutable pointers of struct se_dev_entry. Remove RCU usage for access to those pointers. Link: https://lore.kernel.org/r/20220727214125.19647-3-d.bogdanov@yadro.com Reviewed-by: Mike Christie Signed-off-by: Dmitry Bogdanov Signed-off-by: Martin K. Petersen --- include/target/target_core_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index c2b36f7d917d..8c920456edd9 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -665,9 +665,9 @@ struct se_dev_entry { /* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */ struct kref pr_kref; struct completion pr_comp; - struct se_lun_acl __rcu *se_lun_acl; + struct se_lun_acl *se_lun_acl; spinlock_t ua_lock; - struct se_lun __rcu *se_lun; + struct se_lun *se_lun; #define DEF_PR_REG_ACTIVE 1 unsigned long deve_flags; struct list_head alua_port_list; -- cgit v1.2.3 From fe442604199ed3e60d5411137159f9623534e956 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 28 Jul 2022 15:18:48 -0700 Subject: scsi: core: Make sure that targets outlive devices This commit prevents that the following sequence triggers a kernel crash: - Deletion of a SCSI device is requested via sysfs. Device removal takes some time because blk_cleanup_queue() is waiting for the SCSI error handler. - The SCSI target associated with that SCSI device is removed. - scsi_remove_target() returns and its caller frees the resources associated with the SCSI target. - The error handler makes progress and invokes an LLD callback that dereferences the SCSI target pointer. Link: https://lore.kernel.org/r/20220728221851.1822295-2-bvanassche@acm.org Cc: Christoph Hellwig Cc: Mike Christie Cc: Hannes Reinecke Cc: John Garry Cc: Li Zhijian Reported-by: Mike Christie Reviewed-by: Ming Lei Reviewed-by: Mike Christie Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 2 ++ drivers/scsi/scsi_sysfs.c | 20 +++++++++++++++++--- include/scsi/scsi_device.h | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 91ac901a6682..4c1efd6a3b0c 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -521,6 +521,8 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, starget->state = STARGET_CREATED; starget->scsi_level = SCSI_2; starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED; + init_waitqueue_head(&starget->sdev_wq); + retry: spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 43949798a2e4..1bc9c26fe1d4 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -443,7 +443,9 @@ static void scsi_device_cls_release(struct device *class_dev) static void scsi_device_dev_release_usercontext(struct work_struct *work) { - struct scsi_device *sdev; + struct scsi_device *sdev = container_of(work, struct scsi_device, + ew.work); + struct scsi_target *starget = sdev->sdev_target; struct device *parent; struct list_head *this, *tmp; struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL; @@ -452,8 +454,6 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) unsigned long flags; struct module *mod; - sdev = container_of(work, struct scsi_device, ew.work); - mod = sdev->host->hostt->module; scsi_dh_release_device(sdev); @@ -516,6 +516,9 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) kfree(sdev->inquiry); kfree(sdev); + if (starget && atomic_dec_return(&starget->sdev_count) == 0) + wake_up(&starget->sdev_wq); + if (parent) put_device(parent); module_put(mod); @@ -1535,6 +1538,14 @@ static void __scsi_remove_target(struct scsi_target *starget) goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); + + /* + * After scsi_remove_target() returns its caller can remove resources + * associated with @starget, e.g. an rport or session. Wait until all + * devices associated with @starget have been removed to prevent that + * a SCSI error handling callback function triggers a use-after-free. + */ + wait_event(starget->sdev_wq, atomic_read(&starget->sdev_count) == 0); } /** @@ -1645,6 +1656,9 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) list_add_tail(&sdev->same_target_siblings, &starget->devices); list_add_tail(&sdev->siblings, &shost->__devices); spin_unlock_irqrestore(shost->host_lock, flags); + + atomic_inc(&starget->sdev_count); + /* * device can now only be removed via __scsi_remove_device() so hold * the target. Target will be held in CREATED state until something diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 7cf5f3b7589f..190d2081f4c6 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -309,6 +309,8 @@ struct scsi_target { struct list_head devices; struct device dev; struct kref reap_ref; /* last put renders target invisible */ + atomic_t sdev_count; + wait_queue_head_t sdev_wq; unsigned int channel; unsigned int id; /* target id ... replace * scsi_device.id eventually */ -- cgit v1.2.3 From 16728aaba62e8b3b170735fdc3d8aa972835c136 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 28 Jul 2022 15:18:49 -0700 Subject: 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 Cc: Ming Lei Cc: Mike Christie Cc: Hannes Reinecke Cc: John Garry Reviewed-by: Mike Christie Signed-off-by: Ming Lei Signed-off-by: Bart Van Assche [ bvanassche: Reworked Ming's patch and split it ] Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 8 ++++++++ drivers/scsi/scsi_scan.c | 7 +++++++ include/scsi/scsi_host.h | 3 +++ 3 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index ef6c0e37acce..8fa98c8d0ee0 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -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) { diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 4c1efd6a3b0c..ac6059702d13 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -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); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 667d889b92b5..339f975d356e 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -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 -- cgit v1.2.3