UFS driver fixes for v3.18
cover UFS driver fixes for v3.18
This patch series includes two fixes for UFS driver.
The first patch already got reviewed and it was a part of Dolev's various fixes for UFS-PM series (http://marc.info/?l=linux-scsi&m=141405993104015),
but they are not merged scsi-queue yet. So this series also includes it.
The second one is a fix which I have already reported and Dolev agreed with that in http://marc.info/?l=linux-scsi&m=141164131404767
Akinobu Mita (2):
scsi: ufs: fix reference counting of W-LUs
scsi: ufs: fix NULL dereference when no regulators are defined
drivers/scsi/ufs/ufshcd.c | 80 +++++++++++++++++++++++++---------------------- drivers/scsi/ufs/ufshcd.h | 2 -- 2 files changed, 42 insertions(+), 40 deletions(-)
Signature · 1 line
1.9.1
PATCH 1/2 scsi: ufs: fix reference counting of W-LUs
UFS driver adds three well known LUs in the initialization, but those reference counts are not decremented, so it makes ufshcd module impossible to unload.
This fixes it by putting scsi_device_put() in the initalization, and in order to protect concurrent access to hba->sdev_ufs_device (UFS Device W-LU) from manual delete, increment the reference count while requesting device power mode setting.
The rest of W-LUs (hba->sdev_boot and hba->sdev_rpmb) are not directly used from driver, so these references in struct ufs_hba are removed.
--- drivers/scsi/ufs/ufshcd.c | 74 +++++++++++++++++++++++------------------------ drivers/scsi/ufs/ufshcd.h | 2 -- 2 files changed, 36 insertions(+), 40 deletions(-)
drivers/scsi/ufs/ufshcd.c +36 -38
@@ -2844,8 +2844,13 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) hba =shost_priv (sdev->host);scsi_deactivate_tcq (sdev, hba->nutrs);/* Drop the reference as it won't be needed anymore */ -if (ufshcd_scsi_to_upiu_lun (sdev->lun) ==UFS_UPIU_UFS_DEVICE_WLUN ) +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); + } }/**
@@ -4062,6 +4067,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)static int ufshcd_scsi_add_wlus (struct ufs_hba *hba) {int ret =0 ; +struct scsi_device *sdev_rpmb; +struct scsi_device *sdev_boot; hba->sdev_ufs_device =__scsi_add_device (hba->host,0 ,0 ,ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN ),NULL );
@@ -4070,26 +4077,27 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device =NULL ; goto out; } +scsi_device_put (hba->sdev_ufs_device); - hba->sdev_boot =__scsi_add_device (hba->host,0 ,0 , + sdev_boot =__scsi_add_device (hba->host,0 ,0 ,ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_BOOT_WLUN ),NULL ); -if (IS_ERR (hba->sdev_boot)) { - ret =PTR_ERR (hba->sdev_boot); - hba->sdev_boot =NULL ; +if (IS_ERR (sdev_boot)) { + ret =PTR_ERR (sdev_boot); goto remove_sdev_ufs_device; } +scsi_device_put (sdev_boot); - hba->sdev_rpmb =__scsi_add_device (hba->host,0 ,0 , + sdev_rpmb =__scsi_add_device (hba->host,0 ,0 ,ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN ),NULL ); -if (IS_ERR (hba->sdev_rpmb)) { - ret =PTR_ERR (hba->sdev_rpmb); - hba->sdev_rpmb =NULL ; +if (IS_ERR (sdev_rpmb)) { + ret =PTR_ERR (sdev_rpmb); goto remove_sdev_boot; } +scsi_device_put (sdev_rpmb); goto out; remove_sdev_boot: -scsi_remove_device (hba->sdev_boot); +scsi_remove_device (sdev_boot); remove_sdev_ufs_device:scsi_remove_device (hba->sdev_ufs_device); out:
@@ -4097,30 +4105,6 @@ out: }/** -* ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by -* ufshcd_scsi_add_wlus() -* @hba: per-adapter instance -* -*/ -static void ufshcd_scsi_remove_wlus (struct ufs_hba *hba) -{ -if (hba->sdev_ufs_device) { -scsi_remove_device (hba->sdev_ufs_device); - hba->sdev_ufs_device =NULL ; - } - -if (hba->sdev_boot) { -scsi_remove_device (hba->sdev_boot); - hba->sdev_boot =NULL ; - } - -if (hba->sdev_rpmb) { -scsi_remove_device (hba->sdev_rpmb); - hba->sdev_rpmb =NULL ; - } -} - -/** * ufshcd_probe_hba - probe hba to detect device and initialize * @hba: per-adapter instance *
@@ -4675,11 +4659,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, {unsigned char cmd[6 ] = {START_STOP };struct scsi_sense_hdr sshdr; -struct scsi_device *sdp = hba->sdev_ufs_device; +struct scsi_device *sdp; +unsigned long flags;int ret; -if (!sdp || !scsi_device_online (sdp)) -return -ENODEV ; +spin_lock_irqsave (hba->host->host_lock, flags); + sdp = hba->sdev_ufs_device; +if (sdp) { + ret =scsi_device_get (sdp); +if (!ret && !scsi_device_online (sdp)) { + ret = -ENODEV ; +scsi_device_put (sdp); + } + }else { + ret = -ENODEV ; + } +spin_unlock_irqrestore (hba->host->host_lock, flags); + +if (ret) +return ret;/* * If scsi commands fail, the scsi mid-layer schedules scsi error-
@@ -4718,6 +4716,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,if (!ret) hba->curr_dev_pwr_mode = pwr_mode; out: +scsi_device_put (sdp); hba->host->eh_noresume =0 ;return ret; }
@@ -5231,7 +5230,6 @@ EXPORT_SYMBOL(ufshcd_shutdown);void ufshcd_remove (struct ufs_hba *hba) {scsi_remove_host (hba->host); -ufshcd_scsi_remove_wlus (hba);/* disable interrupts */ ufshcd_disable_intr (hba, hba->intr_mask);ufshcd_hba_stop (hba);
drivers/scsi/ufs/ufshcd.h +0 -2
@@ -392,8 +392,6 @@ struct ufs_hba {* "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; -struct scsi_device *sdev_rpmb; -struct scsi_device *sdev_boot;enum ufs_dev_pwr_mode curr_dev_pwr_mode;enum uic_link_state uic_link_state;
Signature · 1 line
1.9.1
RE: [PATCH 1/2] scsi: ufs: fix reference counting of W-LUs
Looks good to me,
-----Original Message-----
From: Akinobu Mita [mailto:[email protected]]
Sent: Tuesday, November 18, 2014 6:03 AM
To: [email protected]
Cc: Akinobu Mita; Akinobu Mita; Vinayak Holikatti; Dolev Raviv; Subhash Jadavani; Yaniv Gardi; Sujit Reddy Thumma; Christoph Hellwig; James E.J. Bottomley
Subject: [PATCH 1/2] scsi: ufs: fix reference counting of W-LUs
UFS driver adds three well known LUs in the initialization, but those reference counts are not decremented, so it makes ufshcd module impossible to unload.
This fixes it by putting scsi_device_put() in the initalization, and in order to protect concurrent access to hba->sdev_ufs_device (UFS Device W-LU) from manual delete, increment the reference count while requesting device power mode setting.
The rest of W-LUs (hba->sdev_boot and hba->sdev_rpmb) are not directly used from driver, so these references in struct ufs_hba are removed.
--- drivers/scsi/ufs/ufshcd.c | 74 +++++++++++++++++++++++------------------------ drivers/scsi/ufs/ufshcd.h | 2 -- 2 files changed, 36 insertions(+), 40 deletions(-)
drivers/scsi/ufs/ufshcd.c index renamed from drivers/scsi/ufs/ufshcd.c +0 -0
497c38a..59b6544 100644
drivers/scsi/ufs/ufshcd.c +0 -0
@@ -2844,8 +2844,13 @@ static void ufshcd_slave_destroy(struct scsi_device
*sdev)
hba = shost_priv(sdev->host); scsi_deactivate_tcq(sdev, hba->nutrs);
/* Drop the reference as it won't be needed anymore */
- if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN)
+ 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);
+ }
}
/**
@@ -4062,6 +4067,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) {
int ret = 0;
+ struct scsi_device *sdev_rpmb;
+ struct scsi_device *sdev_boot;
hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN),
NULL); @@ -4070,26 +4077,27 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
hba->sdev_ufs_device = NULL;
goto out;
}
+ scsi_device_put(hba->sdev_ufs_device);
- hba->sdev_boot = __scsi_add_device(hba->host, 0, 0,
+ sdev_boot = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
- if (IS_ERR(hba->sdev_boot)) {
- ret = PTR_ERR(hba->sdev_boot);
- hba->sdev_boot = NULL;
+ if (IS_ERR(sdev_boot)) {
+ ret = PTR_ERR(sdev_boot);
goto remove_sdev_ufs_device;
}
+ scsi_device_put(sdev_boot);
- hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
+ sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
- if (IS_ERR(hba->sdev_rpmb)) {
- ret = PTR_ERR(hba->sdev_rpmb);
- hba->sdev_rpmb = NULL;
+ if (IS_ERR(sdev_rpmb)) {
+ ret = PTR_ERR(sdev_rpmb);
goto remove_sdev_boot;
}
+ scsi_device_put(sdev_rpmb);
goto out;
remove_sdev_boot:
- scsi_remove_device(hba->sdev_boot);
+ scsi_remove_device(sdev_boot);
remove_sdev_ufs_device:
scsi_remove_device(hba->sdev_ufs_device);
out:
@@ -4097,30 +4105,6 @@ out:
}
/**
- * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by
- * ufshcd_scsi_add_wlus()
- * @hba: per-adapter instance
- *
- */
-static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba) -{
- if (hba->sdev_ufs_device) {
- scsi_remove_device(hba->sdev_ufs_device);
- hba->sdev_ufs_device = NULL;
- }
-
- if (hba->sdev_boot) {
- scsi_remove_device(hba->sdev_boot);
- hba->sdev_boot = NULL;
- }
-
- if (hba->sdev_rpmb) {
- scsi_remove_device(hba->sdev_rpmb);
- hba->sdev_rpmb = NULL;
- }
-}
-
-/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
*
@@ -4675,11 +4659,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, {
unsigned char cmd[6] = { START_STOP };
struct scsi_sense_hdr sshdr;
- struct scsi_device *sdp = hba->sdev_ufs_device;
+ struct scsi_device *sdp;
+ unsigned long flags;
int ret;
- if (!sdp || !scsi_device_online(sdp))
- return -ENODEV;
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ sdp = hba->sdev_ufs_device;
+ if (sdp) {
+ ret = scsi_device_get(sdp);
+ if (!ret && !scsi_device_online(sdp)) {
+ ret = -ENODEV;
+ scsi_device_put(sdp);
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ if (ret)
+ return ret;
/*
* If scsi commands fail, the scsi mid-layer schedules scsi error-
@@ -4718,6 +4716,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
if (!ret)
hba->curr_dev_pwr_mode = pwr_mode;
out:
+ scsi_device_put(sdp);
hba->host->eh_noresume = 0;
return ret;
}
@@ -5231,7 +5230,6 @@ EXPORT_SYMBOL(ufshcd_shutdown); void ufshcd_remove(struct ufs_hba *hba) {
scsi_remove_host(hba->host);
- ufshcd_scsi_remove_wlus(hba);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask); ufshcd_hba_stop(hba);
drivers/scsi/ufs/ufshcd.h index renamed from drivers/scsi/ufs/ufshcd.h +0 -0
58ecdff..4a574aa 100644
drivers/scsi/ufs/ufshcd.h +0 -2
@@ -392,8 +392,6 @@ struct ufs_hba {* "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; -struct scsi_device *sdev_rpmb; -struct scsi_device *sdev_boot;enum ufs_dev_pwr_mode curr_dev_pwr_mode;enum uic_link_state uic_link_state;
--
1.9.1
PATCH 2/2 scsi: ufs: fix NULL dereference when no regulators are defined
In reply to Akinobu MitaIf no voltage supply regulators are defined for the UFS devices (assumed they are always-on), ufshcd_config_vreg_load() can be called on suspend/resume paths with vreg == NULL as hba->vreg_info.vcc* equal to NULL, and it causes NULL pointer dereference.
This fixes it by making ufshcd_config_vreg_{h,l}pm noop when no regulators are defined.
--- drivers/scsi/ufs/ufshcd.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/scsi/ufs/ufshcd.c +6 -0
@@ -4248,12 +4248,18 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,static inline int ufshcd_config_vreg_lpm (struct ufs_hba *hba,struct ufs_vreg *vreg) { +if (!vreg) +return 0 ; +return ufshcd_config_vreg_load (hba->dev, vreg,UFS_VREG_LPM_LOAD_UA ); }static inline int ufshcd_config_vreg_hpm (struct ufs_hba *hba,struct ufs_vreg *vreg) { +if (!vreg) +return 0 ; +return ufshcd_config_vreg_load (hba->dev, vreg, vreg->max_uA); }
Signature · 1 line
1.9.1
RE: [PATCH 2/2] scsi: ufs: fix NULL dereference when no regulators are defined
-----Original Message-----
From: Akinobu Mita [mailto:[email protected]]
Sent: Tuesday, November 18, 2014 6:03 AM
To: [email protected]
Cc: Akinobu Mita; Akinobu Mita; Vinayak Holikatti; Subhash Jadavani; Dolev Raviv; Sujit Reddy Thumma; Yaniv Gardi; Christoph Hellwig; James E.J. Bottomley
Subject: [PATCH 2/2] scsi: ufs: fix NULL dereference when no regulators are defined
If no voltage supply regulators are defined for the UFS devices (assumed they are always-on), ufshcd_config_vreg_load() can be called on suspend/resume paths with vreg == NULL as hba->vreg_info.vcc* equal to NULL, and it causes NULL pointer dereference.
This fixes it by making ufshcd_config_vreg_{h,l}pm noop when no regulators are defined.
--- drivers/scsi/ufs/ufshcd.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/scsi/ufs/ufshcd.c index renamed from drivers/scsi/ufs/ufshcd.c +0 -0
59b6544..d18adc4 100644
drivers/scsi/ufs/ufshcd.c +0 -0
@@ -4248,12 +4248,18 @@ static int ufshcd_config_vreg_load(struct device
*dev, struct ufs_vreg *vreg, static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
+ if (!vreg)
+ return 0;
+
return ufshcd_config_vreg_load(hba->dev, vreg,UFS_VREG_LPM_LOAD_UA); }
static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
+ if (!vreg)
+ return 0;
+
return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); }
--
1.9.1
Quoted 5 lines · Akinobu Mita
This patch series includes two fixes for UFS driver.
The first patch already got reviewed and it was a part of Dolev's various fixes for UFS-PM series (http://marc.info/?l=linux-scsi&m=141405993104015),
but they are not merged scsi-queue yet. So this series also includes it.
Actually that series is missing reviews for this and mother other patches.
Can the UFS community please start reviewing each others patches? Thanks!
Hi Chris and Mita,
We will review and test the below patches early next week.
Thanks,
Maya
Quoted 21 lines · Akinobu Mita
Quoted 8 lines
This patch series includes two fixes for UFS driver.
The first patch already got reviewed and it was a part of Dolev's various
fixes for UFS-PM series
(http://marc.info/?l=linux-scsi&m=141405993104015),
but they are not merged scsi-queue yet. So this series also includes it.Actually that series is missing reviews for this and mother other patches.
Can the UFS community please start reviewing each others patches? Thanks!
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 5 lines
Maya Erez Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project Collaborative Project