aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkinobu Mita <akinobu.mita@gmail.com>2014-10-23 06:25:12 -0400
committerChristoph Hellwig <hch@lst.de>2014-11-20 11:01:31 -0500
commit7c48bfd038e570cd12b33d6ab13316f3a0b0d1a7 (patch)
tree13ceab3cb6c1d3c607168d82ce5c5b235c5d8b7e
parent1899045510ff109980d9cc34e330fd8ca3631871 (diff)
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. Signed-off-by: Akinobu Mita <mita@fixstars.com> Reviewed-by: Maya Erez <merez@codeaurora.org> Reviewed-by: Dolev Raviv <draviv@codeaurora.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/scsi/ufs/ufshcd.c74
-rw-r--r--drivers/scsi/ufs/ufshcd.h2
2 files changed, 36 insertions, 40 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 497c38a4a866..59b654467d48 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2844,8 +2844,13 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
2844 hba = shost_priv(sdev->host); 2844 hba = shost_priv(sdev->host);
2845 scsi_deactivate_tcq(sdev, hba->nutrs); 2845 scsi_deactivate_tcq(sdev, hba->nutrs);
2846 /* Drop the reference as it won't be needed anymore */ 2846 /* Drop the reference as it won't be needed anymore */
2847 if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) 2847 if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) {
2848 unsigned long flags;
2849
2850 spin_lock_irqsave(hba->host->host_lock, flags);
2848 hba->sdev_ufs_device = NULL; 2851 hba->sdev_ufs_device = NULL;
2852 spin_unlock_irqrestore(hba->host->host_lock, flags);
2853 }
2849} 2854}
2850 2855
2851/** 2856/**
@@ -4062,6 +4067,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
4062static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) 4067static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
4063{ 4068{
4064 int ret = 0; 4069 int ret = 0;
4070 struct scsi_device *sdev_rpmb;
4071 struct scsi_device *sdev_boot;
4065 4072
4066 hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0, 4073 hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
4067 ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL); 4074 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)
4070 hba->sdev_ufs_device = NULL; 4077 hba->sdev_ufs_device = NULL;
4071 goto out; 4078 goto out;
4072 } 4079 }
4080 scsi_device_put(hba->sdev_ufs_device);
4073 4081
4074 hba->sdev_boot = __scsi_add_device(hba->host, 0, 0, 4082 sdev_boot = __scsi_add_device(hba->host, 0, 0,
4075 ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL); 4083 ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
4076 if (IS_ERR(hba->sdev_boot)) { 4084 if (IS_ERR(sdev_boot)) {
4077 ret = PTR_ERR(hba->sdev_boot); 4085 ret = PTR_ERR(sdev_boot);
4078 hba->sdev_boot = NULL;
4079 goto remove_sdev_ufs_device; 4086 goto remove_sdev_ufs_device;
4080 } 4087 }
4088 scsi_device_put(sdev_boot);
4081 4089
4082 hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0, 4090 sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
4083 ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL); 4091 ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
4084 if (IS_ERR(hba->sdev_rpmb)) { 4092 if (IS_ERR(sdev_rpmb)) {
4085 ret = PTR_ERR(hba->sdev_rpmb); 4093 ret = PTR_ERR(sdev_rpmb);
4086 hba->sdev_rpmb = NULL;
4087 goto remove_sdev_boot; 4094 goto remove_sdev_boot;
4088 } 4095 }
4096 scsi_device_put(sdev_rpmb);
4089 goto out; 4097 goto out;
4090 4098
4091remove_sdev_boot: 4099remove_sdev_boot:
4092 scsi_remove_device(hba->sdev_boot); 4100 scsi_remove_device(sdev_boot);
4093remove_sdev_ufs_device: 4101remove_sdev_ufs_device:
4094 scsi_remove_device(hba->sdev_ufs_device); 4102 scsi_remove_device(hba->sdev_ufs_device);
4095out: 4103out:
@@ -4097,30 +4105,6 @@ out:
4097} 4105}
4098 4106
4099/** 4107/**
4100 * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by
4101 * ufshcd_scsi_add_wlus()
4102 * @hba: per-adapter instance
4103 *
4104 */
4105static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba)
4106{
4107 if (hba->sdev_ufs_device) {
4108 scsi_remove_device(hba->sdev_ufs_device);
4109 hba->sdev_ufs_device = NULL;
4110 }
4111
4112 if (hba->sdev_boot) {
4113 scsi_remove_device(hba->sdev_boot);
4114 hba->sdev_boot = NULL;
4115 }
4116
4117 if (hba->sdev_rpmb) {
4118 scsi_remove_device(hba->sdev_rpmb);
4119 hba->sdev_rpmb = NULL;
4120 }
4121}
4122
4123/**
4124 * ufshcd_probe_hba - probe hba to detect device and initialize 4108 * ufshcd_probe_hba - probe hba to detect device and initialize
4125 * @hba: per-adapter instance 4109 * @hba: per-adapter instance
4126 * 4110 *
@@ -4675,11 +4659,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
4675{ 4659{
4676 unsigned char cmd[6] = { START_STOP }; 4660 unsigned char cmd[6] = { START_STOP };
4677 struct scsi_sense_hdr sshdr; 4661 struct scsi_sense_hdr sshdr;
4678 struct scsi_device *sdp = hba->sdev_ufs_device; 4662 struct scsi_device *sdp;
4663 unsigned long flags;
4679 int ret; 4664 int ret;
4680 4665
4681 if (!sdp || !scsi_device_online(sdp)) 4666 spin_lock_irqsave(hba->host->host_lock, flags);
4682 return -ENODEV; 4667 sdp = hba->sdev_ufs_device;
4668 if (sdp) {
4669 ret = scsi_device_get(sdp);
4670 if (!ret && !scsi_device_online(sdp)) {
4671 ret = -ENODEV;
4672 scsi_device_put(sdp);
4673 }
4674 } else {
4675 ret = -ENODEV;
4676 }
4677 spin_unlock_irqrestore(hba->host->host_lock, flags);
4678
4679 if (ret)
4680 return ret;
4683 4681
4684 /* 4682 /*
4685 * If scsi commands fail, the scsi mid-layer schedules scsi error- 4683 * 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,
4718 if (!ret) 4716 if (!ret)
4719 hba->curr_dev_pwr_mode = pwr_mode; 4717 hba->curr_dev_pwr_mode = pwr_mode;
4720out: 4718out:
4719 scsi_device_put(sdp);
4721 hba->host->eh_noresume = 0; 4720 hba->host->eh_noresume = 0;
4722 return ret; 4721 return ret;
4723} 4722}
@@ -5231,7 +5230,6 @@ EXPORT_SYMBOL(ufshcd_shutdown);
5231void ufshcd_remove(struct ufs_hba *hba) 5230void ufshcd_remove(struct ufs_hba *hba)
5232{ 5231{
5233 scsi_remove_host(hba->host); 5232 scsi_remove_host(hba->host);
5234 ufshcd_scsi_remove_wlus(hba);
5235 /* disable interrupts */ 5233 /* disable interrupts */
5236 ufshcd_disable_intr(hba, hba->intr_mask); 5234 ufshcd_disable_intr(hba, hba->intr_mask);
5237 ufshcd_hba_stop(hba); 5235 ufshcd_hba_stop(hba);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 58ecdff5065c..4a574aa45855 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -392,8 +392,6 @@ struct ufs_hba {
392 * "UFS device" W-LU. 392 * "UFS device" W-LU.
393 */ 393 */
394 struct scsi_device *sdev_ufs_device; 394 struct scsi_device *sdev_ufs_device;
395 struct scsi_device *sdev_rpmb;
396 struct scsi_device *sdev_boot;
397 395
398 enum ufs_dev_pwr_mode curr_dev_pwr_mode; 396 enum ufs_dev_pwr_mode curr_dev_pwr_mode;
399 enum uic_link_state uic_link_state; 397 enum uic_link_state uic_link_state;