diff mbox

[V6,10/18] scsi: ufs: manually add well known logical units

Message ID 1411648356-3883-11-git-send-email-draviv@codeaurora.org (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Dolev Raviv Sept. 25, 2014, 12:32 p.m. UTC
From: Subhash Jadavani <subhashj@codeaurora.org>

UFS device specification requires the UFS devices to support 4 well known
logical units:
	"REPORT_LUNS" (address: 01h)
	"UFS Device" (address: 50h)
	"RPMB" (address: 44h)
	"BOOT" (address: 30h)

UFS device's power management needs to be controlled by "POWER CONDITION"
field of SSU (START STOP UNIT) command. But this "power condition" field
will take effect only when its sent to "UFS device" well known logical unit
hence we require the scsi_device instance to represent this logical unit in
order for the UFS host driver to send the SSU command for power management.

We also require the scsi_device instance for "RPMB" (Replay Protected
Memory Block) LU so user space process can control this LU. User space may
also want to have access to BOOT LU.

This patch adds the scsi device instances for each of all well known LUs
(except "REPORT LUNS" LU).

Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: Dolev Raviv <draviv@codeaurora.org>

Comments

Akinobu Mita Oct. 3, 2014, 4:16 p.m. UTC | #1
2014-09-25 21:32 GMT+09:00 Dolev Raviv <draviv@codeaurora.org>:

> +static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
> +{
> +       int ret = 0;
> +
> +       hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
> +               ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
> +       if (IS_ERR(hba->sdev_ufs_device)) {
> +               ret = PTR_ERR(hba->sdev_ufs_device);
> +               hba->sdev_ufs_device = NULL;
> +               goto out;
> +       }
> +
> +       hba->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;
> +               goto remove_sdev_ufs_device;
> +       }
> +
> +       hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
> +               ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);

These __scsi_add_device() calls for W-LUs increase the module reference
count of ufshcd.ko by three.  But no one calls scsi_device_put() for
these W-LUs, so it is impossible to unload ufshcd.ko.

There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(),
But these calls do nothing (please see a comment below).

I could fix this issue by putting scsi_device_put() just after each
__scsi_add_device() call.  I'll prepare a patch if I haven't missed
something.

> +       if (IS_ERR(hba->sdev_rpmb)) {
> +               ret = PTR_ERR(hba->sdev_rpmb);
> +               hba->sdev_rpmb = NULL;
> +               goto remove_sdev_boot;
> +       }
> +       goto out;
> +
> +remove_sdev_boot:
> +       scsi_remove_device(hba->sdev_boot);
> +remove_sdev_ufs_device:
> +       scsi_remove_device(hba->sdev_ufs_device);
> +out:
> +       return ret;
> +}
> +
> +/**
> + * 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;
> +       }
> +}

When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these
three W-LU devices have already been deleted by scsi_remove_host() which
is called just before ufshcd_scsi_remove_wlus().  The scsi
device state of these W-LU devices is SDEV_DEL at this time, and
scsi_remove_device() does nothing.
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
subhashj@codeaurora.org Oct. 3, 2014, 4:35 p.m. UTC | #2
> I could fix this issue by putting scsi_device_put() just after each __scsi_add_device() call.  I'll prepare a patch if I haven't missed something.

Thanks Akinobu for fixing it. We were always compiling the driver statically in kernel so didn't notice this. Yes, I would be happy to ACK your change. BTW, these patches are already merged into I could fix this issue by putting git://git.infradead.org/users/hch/scsi-queue.git -b drivers-for-3.18, so you may  to base your fix on it.

-----Original Message-----
From: Akinobu Mita [mailto:akinobu.mita@gmail.com] 
Sent: Friday, October 03, 2014 9:16 AM
To: Dolev Raviv
Cc: Jej B; Christoph Hellwig; linux-scsi@vger.kernel.org; linux-scsi-owner@vger.kernel.org; linux-arm-msm@vger.kernel.org; Santosh Y; Subhash Jadavani
Subject: Re: [PATCH V6 10/18] scsi: ufs: manually add well known logical units

2014-09-25 21:32 GMT+09:00 Dolev Raviv <draviv@codeaurora.org>:

> +static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) {
> +       int ret = 0;
> +
> +       hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
> +               ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
> +       if (IS_ERR(hba->sdev_ufs_device)) {
> +               ret = PTR_ERR(hba->sdev_ufs_device);
> +               hba->sdev_ufs_device = NULL;
> +               goto out;
> +       }
> +
> +       hba->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;
> +               goto remove_sdev_ufs_device;
> +       }
> +
> +       hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
> +               ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), 
> + NULL);

These __scsi_add_device() calls for W-LUs increase the module reference count of ufshcd.ko by three.  But no one calls scsi_device_put() for these W-LUs, so it is impossible to unload ufshcd.ko.

There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(), But these calls do nothing (please see a comment below).

I could fix this issue by putting scsi_device_put() just after each
__scsi_add_device() call.  I'll prepare a patch if I haven't missed something.

> +       if (IS_ERR(hba->sdev_rpmb)) {
> +               ret = PTR_ERR(hba->sdev_rpmb);
> +               hba->sdev_rpmb = NULL;
> +               goto remove_sdev_boot;
> +       }
> +       goto out;
> +
> +remove_sdev_boot:
> +       scsi_remove_device(hba->sdev_boot);
> +remove_sdev_ufs_device:
> +       scsi_remove_device(hba->sdev_ufs_device);
> +out:
> +       return ret;
> +}
> +
> +/**
> + * 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;
> +       }
> +}

When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these three W-LU devices have already been deleted by scsi_remove_host() which is called just before ufshcd_scsi_remove_wlus().  The scsi device state of these W-LU devices is SDEV_DEL at this time, and
scsi_remove_device() does nothing.

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig Oct. 3, 2014, 4:35 p.m. UTC | #3
On Sat, Oct 04, 2014 at 01:16:16AM +0900, Akinobu Mita wrote:
> These __scsi_add_device() calls for W-LUs increase the module reference
> count of ufshcd.ko by three.  But no one calls scsi_device_put() for
> these W-LUs, so it is impossible to unload ufshcd.ko.
> 
> There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(),
> But these calls do nothing (please see a comment below).
> 
> I could fix this issue by putting scsi_device_put() just after each
> __scsi_add_device() call.  I'll prepare a patch if I haven't missed
> something.


> When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these
> three W-LU devices have already been deleted by scsi_remove_host() which
> is called just before ufshcd_scsi_remove_wlus().  The scsi
> device state of these W-LU devices is SDEV_DEL at this time, and
> scsi_remove_device() does nothing.


No, we need to keep a reference to avoid them going away due to a manual
delete.  We need a scsi_device_put where we currently call
scsi_remove_device I guess.
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Akinobu Mita Oct. 5, 2014, 7:31 a.m. UTC | #4
2014-10-04 1:35 GMT+09:00 Christoph Hellwig <hch@infradead.org>:
> On Sat, Oct 04, 2014 at 01:16:16AM +0900, Akinobu Mita wrote:
>> These __scsi_add_device() calls for W-LUs increase the module reference
>> count of ufshcd.ko by three.  But no one calls scsi_device_put() for
>> these W-LUs, so it is impossible to unload ufshcd.ko.
>>
>> There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(),
>> But these calls do nothing (please see a comment below).
>>
>> I could fix this issue by putting scsi_device_put() just after each
>> __scsi_add_device() call.  I'll prepare a patch if I haven't missed
>> something.
>
>
>> When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these
>> three W-LU devices have already been deleted by scsi_remove_host() which
>> is called just before ufshcd_scsi_remove_wlus().  The scsi
>> device state of these W-LU devices is SDEV_DEL at this time, and
>> scsi_remove_device() does nothing.
>
>
> No, we need to keep a reference to avoid them going away due to a manual
> delete.  We need a scsi_device_put where we currently call
> scsi_remove_device I guess.

This also works for now.  But there is a concern.

In the previous email, I said that __scsi_add_device() calls for W-LUs
increase the module reference count of ufshcd.ko by three.  But the
module reference count of ufshcd-pci.ko (or ufshcd-pltfrm.ko) should be
increased instead of ufshcd.ko.  Because ufshcd.ko is core driver and
it just provides library function for the actual LLD (ufshcd-pci.ko and
ufshcd-pltfrm.ko).

Once this problem is fixed (by setting correct ufshcd_driver_template.module
or else way), it is impossible to unload ufshcd-pci.ko (or
ufshcd-pltfrm.ko).

So the fix I proposed in the previous email plus additional work to
avoid breakage with manual delete of W-LUs (specifically, correct mutal
exclusion for hba->sdev_ufs_device accesses from ufshcd_slave_destroy()
and from ufshcd_set_dev_pwr_mode()) is acceptable?
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index dc89c48..692fd7a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -902,6 +902,17 @@  static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 }
 
 /**
+ * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID
+ * @scsi_lun: UPIU W-LUN id
+ *
+ * Returns SCSI W-LUN id
+ */
+static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
+{
+	return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE;
+}
+
+/**
  * ufshcd_queuecommand - main entry point for SCSI requests
  * @cmd: command from SCSI Midlayer
  * @done: call back function
@@ -3384,6 +3395,93 @@  static void ufshcd_init_icc_levels(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_scsi_add_wlus - Adds required W-LUs
+ * @hba: per-adapter instance
+ *
+ * UFS device specification requires the UFS devices to support 4 well known
+ * logical units:
+ *	"REPORT_LUNS" (address: 01h)
+ *	"UFS Device" (address: 50h)
+ *	"RPMB" (address: 44h)
+ *	"BOOT" (address: 30h)
+ * UFS device's power management needs to be controlled by "POWER CONDITION"
+ * field of SSU (START STOP UNIT) command. But this "power condition" field
+ * will take effect only when its sent to "UFS device" well known logical unit
+ * hence we require the scsi_device instance to represent this logical unit in
+ * order for the UFS host driver to send the SSU command for power management.
+
+ * We also require the scsi_device instance for "RPMB" (Replay Protected Memory
+ * Block) LU so user space process can control this LU. User space may also
+ * want to have access to BOOT LU.
+
+ * This function adds scsi device instances for each of all well known LUs
+ * (except "REPORT LUNS" LU).
+ *
+ * Returns zero on success (all required W-LUs are added successfully),
+ * non-zero error value on failure (if failed to add any of the required W-LU).
+ */
+static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
+{
+	int ret = 0;
+
+	hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
+		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
+	if (IS_ERR(hba->sdev_ufs_device)) {
+		ret = PTR_ERR(hba->sdev_ufs_device);
+		hba->sdev_ufs_device = NULL;
+		goto out;
+	}
+
+	hba->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;
+		goto remove_sdev_ufs_device;
+	}
+
+	hba->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;
+		goto remove_sdev_boot;
+	}
+	goto out;
+
+remove_sdev_boot:
+	scsi_remove_device(hba->sdev_boot);
+remove_sdev_ufs_device:
+	scsi_remove_device(hba->sdev_ufs_device);
+out:
+	return ret;
+}
+
+/**
+ * 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
  *
@@ -3415,6 +3513,10 @@  static int ufshcd_probe_hba(struct ufs_hba *hba)
 		if (!hba->is_init_prefetch)
 			ufshcd_init_icc_levels(hba);
 
+		/* Add required well known logical units to scsi mid layer */
+		if (ufshcd_scsi_add_wlus(hba))
+			goto out;
+
 		scsi_scan_host(hba->host);
 		pm_runtime_put_sync(hba->dev);
 	}
@@ -3901,6 +4003,7 @@  EXPORT_SYMBOL(ufshcd_runtime_idle);
 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);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 8365ad4..25065ea 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -266,6 +266,13 @@  struct ufs_hba {
 
 	struct Scsi_Host *host;
 	struct device *dev;
+	/*
+	 * This field is to keep a reference to "scsi_device" corresponding to
+	 * "UFS device" W-LU.
+	 */
+	struct scsi_device *sdev_ufs_device;
+	struct scsi_device *sdev_rpmb;
+	struct scsi_device *sdev_boot;
 
 	struct ufshcd_lrb *lrb;
 	unsigned long lrb_in_use;