Message ID | a1c9bac8-b560-b662-f0aa-58c7e000cbbd@intel.com |
---|---|
State | New |
Headers | show |
Series | [V5] scsi: ufshcd: Fix device links when BOOT WLUN fails to probe | expand |
Adrian, > Managed device links are deleted by device_del(). However it is > possible to add a device link to a consumer before device_add(), and > then discovering an error prevents the device from being used. In that > case normally references to the device would be dropped and the device > would be deleted. However the device link holds a reference to the > device, so the device link and device remain indefinitely (unless the > supplier is deleted). Applied to 5.15/scsi-staging, thanks!
On Fri, 6 Aug 2021 16:04:41 +0300, Adrian Hunter wrote: > Managed device links are deleted by device_del(). However it is possible to > add a device link to a consumer before device_add(), and then discovering > an error prevents the device from being used. In that case normally > references to the device would be dropped and the device would be deleted. > However the device link holds a reference to the device, so the device link > and device remain indefinitely (unless the supplier is deleted). > > [...] Applied to 5.15/scsi-queue, thanks! [1/1] scsi: ufshcd: Fix device links when BOOT WLUN fails to probe https://git.kernel.org/mkp/scsi/c/bf25967ac541
diff --git a/drivers/base/core.c b/drivers/base/core.c index cadcade65825..9badd7f7fe62 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -884,6 +884,8 @@ static void device_link_put_kref(struct device_link *link) { if (link->flags & DL_FLAG_STATELESS) kref_put(&link->kref, __device_link_del); + else if (!device_is_registered(link->consumer)) + __device_link_del(&link->kref); else WARN(1, "Unable to drop a managed device link reference\n"); } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6c263e94144b..9f72698ff597 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5028,6 +5028,7 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) static void ufshcd_slave_destroy(struct scsi_device *sdev) { struct ufs_hba *hba; + unsigned long flags; hba = shost_priv(sdev->host); @@ -5035,11 +5036,29 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) /* Drop the reference as it won't be needed anymore */ if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) { - unsigned long flags; - spin_lock_irqsave(hba->host->host_lock, flags); hba->sdev_ufs_device = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); + } else if (hba->sdev_ufs_device) { + struct device *supplier = NULL; + + /* Ensure UFS Device WLUN exists and does not disappear */ + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->sdev_ufs_device) { + supplier = &hba->sdev_ufs_device->sdev_gendev; + get_device(supplier); + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + + if (supplier) { + /* + * If a LUN fails to probe (e.g. absent BOOT WLUN), the + * device will not have been registered but can still + * have a device link holding a reference to the device. + */ + device_link_remove(&sdev->sdev_gendev, supplier); + put_device(supplier); + } } }