Message ID | 20210716114408.17320-3-adrian.hunter@intel.com |
---|---|
State | Superseded |
Headers | show |
Series | driver core: Add ability to delete device links of unregistered devices | expand |
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c > index 708b3b62fc4d..9864a8ee0263 100644 > --- a/drivers/scsi/ufs/ufshcd.c > +++ b/drivers/scsi/ufs/ufshcd.c > @@ -5020,15 +5020,34 @@ 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); > /* 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) { Was just checked in the outer clause? Thanks, Avri > + 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); > + } > } > } > > -- > 2.17.1
Martin, perhaps you could consider picking up this patch if no one objects? On 16/07/21 2:44 pm, 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). > > For UFSHCD, 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. The unwanted device link will prevent runtime > suspend indefinitely. > > Amend device link removal to accept removal of a link with an unregistered > consumer device (suggested by Rafael), and fix UFSHCD by explicitly > deleting the device link when SCSI destroys the SCSI device. > > Fixes: b294ff3e34490 ("scsi: ufs: core: Enable power management for wlun") > Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> > Reviewed-by: Rafael J. Wysocki <rafael@kernel.org> > --- > drivers/base/core.c | 2 ++ > drivers/scsi/ufs/ufshcd.c | 23 +++++++++++++++++++++-- > 2 files changed, 23 insertions(+), 2 deletions(-) > > diff --git a/drivers/base/core.c b/drivers/base/core.c > index 2de8f7d8cf54..983e895d4ced 100644 > --- a/drivers/base/core.c > +++ b/drivers/base/core.c > @@ -887,6 +887,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 708b3b62fc4d..9864a8ee0263 100644 > --- a/drivers/scsi/ufs/ufshcd.c > +++ b/drivers/scsi/ufs/ufshcd.c > @@ -5020,15 +5020,34 @@ 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); > /* 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); > + } > } > } > >
On 6/08/21 5:50 am, Martin K. Petersen wrote: > >> Since patch 1/2 went in through Greg's tree, if patch 2/2 goes >> upstream via Martin's tree, will the resulting kernel be bisectable if >> Linus pulls Martin's tree before he pulls Greg's tree? > > Also, this patch will need to be rebased on top of 5.15/scsi-staging due > to all the UFS churn in this release. > Patch "driver core: Prevent warning when removing a device link from unregistered consumer" is already in Linus' tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e64daad660a0c9ace3acdc57099fffe5ed83f977 I will rebase on 5.15/scsi-staging
diff --git a/drivers/base/core.c b/drivers/base/core.c index 2de8f7d8cf54..983e895d4ced 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -887,6 +887,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 708b3b62fc4d..9864a8ee0263 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5020,15 +5020,34 @@ 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); /* 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); + } } }