diff mbox

[v1,3/4] scsi: ufs: Expose Task Management to ioctl() UAPI

Message ID 1494511309-32256-4-git-send-email-michalx.potomski@intel.com (mailing list archive)
State Rejected, archived
Headers show

Commit Message

Potomski, MichalX May 11, 2017, 2:01 p.m. UTC
From: Michał Potomski <michalx.potomski@intel.com>

Since we already have UFS ioctl() API some additional tools
like task management are "nice-to-have" for testing and management
purposes.

Using this API we can transparently from user perspective
query or abort tasks and reset Logical Units. This will help
to test all hosts and devices to check their stability, error
handling, etc. While this feature does not present great value
for standard user, it might help a lot in troubleshooting of
the device.

Signed-off-by: Michał Potomski <michalx.potomski@intel.com>
---
 Documentation/scsi/ufs.txt      | 35 +++++++++++++++++++++++++
 drivers/scsi/ufs/ufs.h          | 10 --------
 drivers/scsi/ufs/ufshcd-ioctl.c | 57 +++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/ufs/ufshcd.c       |  2 +-
 drivers/scsi/ufs/ufshcd.h       |  4 +++
 include/scsi/scsi.h             |  2 +-
 include/uapi/scsi/ufs/ioctl.h   | 18 +++++++++++++
 include/uapi/scsi/ufs/ufs.h     | 10 ++++++++
 8 files changed, 124 insertions(+), 14 deletions(-)

Comments

Christoph Hellwig May 11, 2017, 5:57 p.m. UTC | #1
On Thu, May 11, 2017 at 04:01:48PM +0200, Michal Potomski wrote:
> From: Michał Potomski <michalx.potomski@intel.com>
> 
> Since we already have UFS ioctl() API some additional tools
> like task management are "nice-to-have" for testing and management
> purposes.

No way you're going to bypass the SCSI midlayer for task management.

NAK with prejudice.
diff mbox

Patch

diff --git a/Documentation/scsi/ufs.txt b/Documentation/scsi/ufs.txt
index 6c0e995..ed5768d 100644
--- a/Documentation/scsi/ufs.txt
+++ b/Documentation/scsi/ufs.txt
@@ -18,6 +18,7 @@  Contents
 4. UFSHCD User Interface
   4.1 UFS Query IOCTL
   4.2 UFS Auto-Hibern8 IOCTL
+  4.3 UFS Task Management IOCTL
 
 
 1. Overview
@@ -220,6 +221,40 @@  host and device using IOCTL interface.
 
 		return error;
 	}
+4.3 UFS Task Management IOCTL
+
+  This interface enables user to manage tasks on the device. It's meant
+  mainly for test and troubleshooting purposes. You can use following
+  snippet to comunicate with this interface:
+
+	#include <sys/ioctl.h>
+	#include <scsi/ufs/ioctl.h>
+	#include <scsi/ufs/ufs.h>
+
+	static int handleTask(int fd, uint8_t task_id, uint8_t task_func,
+		uint8_t *response)
+	{
+		ufs_ioctl_task_mgmt_data task_data;
+		int error;
+
+		/* Task ID (normally in range 0-F) */
+		task_data.task_id = task_id;
+
+		/* Task Function (check <scsi/ufs/ufs.h>) */
+		task_data.task_func = task_func;
+
+		/* [fd] used here shall be opened UFS device */
+		error = ioctl(fd, UFS_IOCTL_TASK_MANAGEMENT, &task_data);
+
+		if (!error)
+			/* This will return task specific response */
+			*response = task_data.response;
+
+		return error;
+	}
+
+  This one should be used with extreme care as it's more of a testing/vaidation
+  interface.
 
 
 UFS Specifications can be found at,
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 06da302..8bfa5f3 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -88,16 +88,6 @@  static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
  * UFS Protocol Information Unit related definitions
  */
 
-/* Task management functions */
-enum {
-	UFS_ABORT_TASK		= 0x01,
-	UFS_ABORT_TASK_SET	= 0x02,
-	UFS_CLEAR_TASK_SET	= 0x04,
-	UFS_LOGICAL_RESET	= 0x08,
-	UFS_QUERY_TASK		= 0x80,
-	UFS_QUERY_TASK_SET	= 0x81,
-};
-
 /* UTP UPIU Transaction Codes Initiator to Target */
 enum {
 	UPIU_TRANSACTION_NOP_OUT	= 0x00,
diff --git a/drivers/scsi/ufs/ufshcd-ioctl.c b/drivers/scsi/ufs/ufshcd-ioctl.c
index 71f4026..afcb099 100644
--- a/drivers/scsi/ufs/ufshcd-ioctl.c
+++ b/drivers/scsi/ufs/ufshcd-ioctl.c
@@ -180,6 +180,7 @@  static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
 		goto out_release_mem;
 	}
 
+	/* Prepare to handle query */
 	switch (ioctl_data->opcode) {
 	case UPIU_QUERY_OPCODE_WRITE_DESC:
 		write = true;
@@ -329,6 +330,51 @@  static int ufshcd_auto_hibern8_ioctl(struct ufs_hba *hba, void __user *buffer)
 	return err;
 }
 
+static int ufshcd_task_mgmt_ioctl(struct ufs_hba *hba, u8 lun,
+	void __user *buffer)
+{
+	struct ufs_ioctl_task_mgmt_data *ioctl_data;
+	int err = 0;
+
+	if (!buffer)
+		return -EINVAL;
+
+	if (!ufs_is_valid_unit_desc_lun(lun))
+		return -EINVAL;
+
+	ioctl_data = kzalloc(sizeof(struct ufs_ioctl_task_mgmt_data),
+				GFP_KERNEL);
+	if (!ioctl_data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Extract params from user buffer */
+	if (copy_from_user(ioctl_data, buffer, sizeof(*ioctl_data))) {
+		err = -EFAULT;
+		goto out_release_mem;
+	}
+
+	err = ufshcd_issue_tm_cmd(hba, lun, ioctl_data->task_id,
+		ioctl_data->task_func, &ioctl_data->response);
+
+	if (err)
+		goto out_release_mem;
+
+	/* Copy response to user */
+	if (copy_to_user(buffer, ioctl_data, sizeof(*ioctl_data)))
+		err = -EFAULT;
+
+out_release_mem:
+	kfree(ioctl_data);
+out:
+	if (err)
+		dev_err(hba->dev, "User Task Management failed (error: %d)",
+			err);
+
+	return err;
+}
+
 /**
  * ufshcd_ioctl - ufs ioctl callback registered in scsi_host
  * @dev: scsi device required for per LUN queries
@@ -338,6 +384,7 @@  static int ufshcd_auto_hibern8_ioctl(struct ufs_hba *hba, void __user *buffer)
  * Supported commands:
  * UFS_IOCTL_QUERY
  * UFS_IOCTL_AUTO_HIBERN8
+ * UFS_IOCTL_TASK_MANAGEMENT
  */
 int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
 {
@@ -359,6 +405,12 @@  int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
 		err = ufshcd_auto_hibern8_ioctl(hba, buffer);
 		pm_runtime_put_sync(hba->dev);
 		break;
+	case UFS_IOCTL_TASK_MANAGEMENT:
+		pm_runtime_get_sync(hba->dev);
+		err = ufshcd_task_mgmt_ioctl(hba,
+			ufshcd_scsi_to_upiu_lun(dev->lun), buffer);
+		pm_runtime_put_sync(hba->dev);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6fcbeb5..6c52102 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5437,7 +5437,7 @@  static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
  *
  * Returns non-zero value on error, zero on success.
  */
-static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
+int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
 		u8 tm_function, u8 *tm_response)
 {
 	struct utp_task_req_desc *task_req_descp;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 392f702..ef1607f 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -864,6 +864,10 @@  int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
 void ufshcd_setup_auto_hibern8(struct ufs_hba *hba, u8 scale, u16 timer_val);
 u32 ufshcd_read_auto_hibern8_state(struct ufs_hba *hba);
 
+/* Expose Task Management API */
+int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
+		u8 tm_function, u8 *tm_response);
+
 /* Wrapper functions for safely calling variant operations */
 static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
 {
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 43f87ad..b8e1006 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -255,7 +255,7 @@  static inline int scsi_is_wlun(u64 lun)
  * Here are some scsi specific ioctl commands which are sometimes useful.
  *
  * Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
- * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 - 0x53A1
+ * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 - 0x53A2
  */
 
 /* Used to obtain PUN and LUN info.  Conflicts with CDROMAUDIOBUFSIZ */
diff --git a/include/uapi/scsi/ufs/ioctl.h b/include/uapi/scsi/ufs/ioctl.h
index f3583de..6b7e876 100644
--- a/include/uapi/scsi/ufs/ioctl.h
+++ b/include/uapi/scsi/ufs/ioctl.h
@@ -9,6 +9,7 @@ 
  */
 #define UFS_IOCTL_QUERY			0x53A0
 #define UFS_IOCTL_AUTO_HIBERN8		0x53A1
+#define UFS_IOCTL_TASK_MANAGEMENT	0x53A2
 
 /**
  * struct ufs_ioctl_query_data - used to transfer data to and from user via
@@ -89,4 +90,21 @@  struct ufs_ioctl_auto_hibern8_data {
 	__u16 timer_val;
 };
 
+/**
+ * struct ufs_ioctl_task_mgmt_data - used to perform Task Management specific
+ * functions
+ *
+ * @task_id: ID of a task to be managed
+ * @task_func: function to perform on managed task
+ * @response: Task Management response
+ *
+ * Submitted: task_id, task_func
+ * Received: response
+ */
+struct ufs_ioctl_task_mgmt_data {
+	__u8 task_id;
+	__u8 task_func;
+	__u8 response;
+};
+
 #endif /* UAPI_UFS_IOCTL_H_ */
diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h
index 096ebea..9f93c9a 100644
--- a/include/uapi/scsi/ufs/ufs.h
+++ b/include/uapi/scsi/ufs/ufs.h
@@ -66,4 +66,14 @@  enum query_opcode {
 	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
 };
 
+/* Task management functions */
+enum {
+	UFS_ABORT_TASK		= 0x01,
+	UFS_ABORT_TASK_SET	= 0x02,
+	UFS_CLEAR_TASK_SET	= 0x04,
+	UFS_LOGICAL_RESET	= 0x08,
+	UFS_QUERY_TASK		= 0x80,
+	UFS_QUERY_TASK_SET	= 0x81,
+};
+
 #endif /* UAPI_UFS_H_ */