[stgt] [PATCH] Add PERSISTENT SCSI RESERVE IN support.

Mark Harvey markh794 at gmail.com
Thu Aug 21 09:47:50 CEST 2008


Still in 'Request for Comment' stage.

Returns hard-coded strings as keys.
Implements all but one PSR-IN service action.

Included Ronnie Sahlberg's suggestion.

Q. How often should I submit updates ? I don't want to spam the list. On the flip side, I don't want to wait until its finished (and find I have coded it in a fashion that is not usable/useful/<any number of other errors>)..

Q. (OK I havn't looked at the code yet) How can I determine the I_T nexus in a generic fashion ? Somehow I have to identify the initiator in a unique way and save any keys supplied by that initiator.

>From 5ecfb123e29ad8b00b710b8a336a5c608ad44930 Mon Sep 17 00:00:00 2001
From: Mark Harvey <markh794 at gmail.com>
Date: Thu, 21 Aug 2008 17:25:11 +1000
Subject: [PATCH 1/1] Add PERSISTENT SCSI RESERVE IN support.

Incomplete and only for testing.
- Returns a hard-coded key (string)

Add hooks to mmc, sbc, scc, smc and ssc modules.

Signed-off-by: Mark Harvey <markh794 at gmail.com>
---
 usr/mmc.c  |    4 +-
 usr/sbc.c  |    4 +-
 usr/scc.c  |   23 ++++-
 usr/scsi.h |    5 +
 usr/smc.c  |    4 +-
 usr/spc.c  |  317 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usr/spc.h  |    4 +
 usr/ssc.c  |    4 +-
 usr/tgtd.h |   27 +++++
 9 files changed, 383 insertions(+), 9 deletions(-)

diff --git a/usr/mmc.c b/usr/mmc.c
index 0e34167..a7b5933 100644
--- a/usr/mmc.c
+++ b/usr/mmc.c
@@ -2416,8 +2416,8 @@ static struct device_type_template mmc_template = {
 		{mmc_close_track,},
 		{mmc_read_buffer_capacity,},
 		{spc_illegal_op,},
-		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{persistent_reserve_in, pr_in_service_actions,},
+		{persistent_reserve_out, pr_out_service_actions,},
 
 		[0x60 ... 0x9f] = {spc_illegal_op,},
 
diff --git a/usr/sbc.c b/usr/sbc.c
index cc308d4..c8f1741 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -374,8 +374,8 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{persistent_reserve_in, pr_in_service_actions,},
+		{persistent_reserve_out, pr_out_service_actions,},
 
 		[0x60 ... 0x7f] = {spc_illegal_op,},
 
diff --git a/usr/scc.c b/usr/scc.c
index eed48a0..ebca16c 100644
--- a/usr/scc.c
+++ b/usr/scc.c
@@ -116,7 +116,28 @@ static struct device_type_template scc_template = {
 		{spc_illegal_op,},
 		{spc_test_unit},
 
-		[0x30 ... 0x7f] = {spc_illegal_op,},
+		[0x30 ... 0x4f] = {spc_illegal_op,},
+
+		/* 0x50 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{persistent_reserve_in, pr_in_service_actions,},
+		{persistent_reserve_out, pr_out_service_actions,},
+
+		[0x60 ... 0x7f] = {spc_illegal_op,},
 
 		/* 0x80 */
 		{spc_illegal_op,},
diff --git a/usr/scsi.h b/usr/scsi.h
index 84fadff..a9481a1 100644
--- a/usr/scsi.h
+++ b/usr/scsi.h
@@ -208,6 +208,11 @@
 #define ASC_POSITION_PAST_BOM			0x3b0c
 #define ASC_MEDIUM_REMOVAL_PREVENTED		0x5302
 #define ASC_BAD_MICROCODE_DETECTED		0x8283
+#define ASC_INSUFFICENT_RESERVE_RESOURCE	0x5502
+#define ASC_INSUFFICENT_RESOURCE		0x5503
+#define ASC_INSUFFICENT_REGISTRAT_RESOURCE	0x5504
+#define ASC_INSUFFICENT_AC_RESOURCE		0x5505
+#define ASC_AUX_MEMORY_OUT_OF_SPACE		0x5506
 
 /* Key 6: Unit Attention */
 #define ASC_NOT_READY_TO_TRANSITION		0x2800
diff --git a/usr/smc.c b/usr/smc.c
index 9d7f681..91f3575 100644
--- a/usr/smc.c
+++ b/usr/smc.c
@@ -796,8 +796,8 @@ struct device_type_template smc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{persistent_reserve_in, pr_in_service_actions,},
+		{persistent_reserve_out, pr_out_service_actions,},
 
 		[0x60 ... 0x9f] = {spc_illegal_op,},
 
diff --git a/usr/spc.c b/usr/spc.c
index bd2c975..c1bc708 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -110,6 +110,22 @@
 #define DESG_MD5 7
 #define DESG_SCSI 8
 
+/*
+ * Persistent Reservation type codes
+ *
+ * 0 - Obsolete
+ * 1 - WRITE Exclusive
+ * 2 - Obsolete
+ * 3 - Exclusive Access
+ * 4 - Obsolete
+ * 5 - Write Exclusive (Registraints only)
+ * 6 - Exclusive Access (Registraints only)
+ */
+#define PR_TYPE_WRITE_EXCLUSIVE 1
+#define PR_TYPE_EXCLUSIVE 3
+#define PR_TYPE_WRITE_EXCLUSIVE_REG 5
+#define PR_TYPE_EXCLUSIVE_REG 6
+
 static void update_vpd_80(struct scsi_lu *lu, void *sn)
 {
 	struct vpd *vpd_pg = lu->attrs.lu_vpd[0];
@@ -880,6 +896,307 @@ void dump_cdb(struct scsi_cmd *cmd)
 	}
 }
 
+/**
+ * SCSI Persistent Reservation
+ *
+ * Reference: spc4r16 Ch 5.7
+ *
+ * Interesting points:
+ *  - Persistent reservations are not reset by hard reset, lu reset ot I_T loss
+ *  - Optionally, may be retained when power to target is lost
+ */
+
+/**
+ * PERSISTENT RESERVE IN - 5Eh
+ * Ref: 6.13
+ *
+ */
+#define PR_IN_READ_KEYS 0
+#define PR_IN_READ_RESERVATION 1
+#define PR_IN_REPORT_CAPABILITIES 2
+#define PR_IN_READ_FULL_STATUS 3
+
+#define PR_READ_KEYS_DESC_SZ 8
+static int spc_pr_read_keys(int host_no, struct scsi_cmd *cmd)
+{
+	int len;
+	int cdb_alloc_len;
+	int i;
+	struct scsi_pr *pr;
+	struct scsi_pr_key *pr_key;
+	uint8_t buf[PR_READ_KEYS_DESC_SZ * PR_RESERVATION_SZ + 20];
+
+	cdb_alloc_len = ((cmd->scb[7] & 0xff) << 8) | (cmd->scb[8] & 0xff);
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr_key = pr->pr_key;
+
+	memset(buf, 0, sizeof(buf));
+
+	pr_key++;
+	pr_key->state = 1;
+	strcpy((char *)pr_key->data, "ABC1234");
+
+	pr_key = pr->pr_key;
+	for (i = 0, len = 0; i < PR_RESERVATION_SZ; i++) {
+		if (pr_key->state) {
+			memcpy(&buf[len + 8], pr_key->data, PR_KEY_SZ);
+			len += PR_KEY_SZ;
+		}
+		pr_key++;
+	}
+
+	buf[0] = (pr->PRgeneration >> 24) & 0xff;
+	buf[1] = (pr->PRgeneration >> 16) & 0xff;
+	buf[2] = (pr->PRgeneration >>  8) & 0xff;
+	buf[3] = pr->PRgeneration & 0xff;
+	buf[4] = (len >> 24) & 0xff;
+	buf[5] = (len >> 16) & 0xff;
+	buf[6] = (len >>  8) & 0xff;
+	buf[7] = len & 0xff;
+
+	memcpy(scsi_get_in_buffer(cmd), buf,
+	       min((int)scsi_get_in_length(cmd), len + 8));
+
+	scsi_set_in_resid_by_actual(cmd, len + 8);
+
+	return SAM_STAT_GOOD;
+}
+
+#define PR_RD_RESERVATION_DESC_SZ 16
+static int spc_pr_read_reservation(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t buf[PR_RD_RESERVATION_DESC_SZ * PR_RESERVATION_SZ + 20];
+	int len;
+	int cdb_alloc_len;
+	int i;
+	struct scsi_pr *pr;
+	struct scsi_pr_key *pr_key;
+
+	cdb_alloc_len = ((cmd->scb[7] & 0xff) << 8) | (cmd->scb[8] & 0xff);
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr_key = pr->pr_key;
+
+	memset(buf, 0, sizeof(buf));
+
+	pr_key->state = PR_TYPE_EXCLUSIVE;
+	strcpy((char *)pr_key->data, "112233");
+
+	for (i = 0, len = 8; i < PR_RESERVATION_SZ; i++) {
+		if (pr_key->state) {
+			memcpy(&buf[len], pr_key->data, PR_KEY_SZ);
+			/* [8 -> 12] = Scope-Specific Address (set to 0) */
+			buf[len + 13] = pr_key->state;
+			len += PR_RD_RESERVATION_DESC_SZ;
+		}
+		pr_key++;
+	}
+
+	buf[0] = (pr->PRgeneration >> 24) & 0xff;
+	buf[1] = (pr->PRgeneration >> 16) & 0xff;
+	buf[2] = (pr->PRgeneration >>  8) & 0xff;
+	buf[3] = pr->PRgeneration & 0xff;
+	buf[4] = (len >> 24) & 0xff;
+	buf[5] = (len >> 16) & 0xff;
+	buf[6] = (len >>  8) & 0xff;
+	buf[7] = len & 0xff;
+
+	memcpy(scsi_get_in_buffer(cmd), buf,
+			min((int)scsi_get_in_length(cmd), len + 8));
+
+	scsi_set_in_resid_by_actual(cmd, len + 8);
+
+	return SAM_STAT_GOOD;
+}
+
+static int spc_pr_report_capabilities(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t buf[8];
+	dprintf("**** Called ****\n");
+
+	/* Taken from IBM LTO Ultrium Tape Drve: SCSI Reference (Ninth ed) */
+	buf[0] = 0;
+	buf[1] = 8;
+	buf[2] = 0x1c;
+	buf[3] = 0x80;
+	buf[4] = 0x48;
+	buf[5] = 0;
+	buf[6] = 0;
+	buf[7] = 0;
+
+	memcpy(scsi_get_in_buffer(cmd), buf,
+			min((int)scsi_get_in_length(cmd), 8));
+
+	scsi_set_in_resid_by_actual(cmd, 8);
+
+	return SAM_STAT_GOOD;
+}
+
+static int spc_pr_read_full_status(int host_no, struct scsi_cmd *cmd)
+{
+	dprintf("**** Called ****\n");
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+struct service_action pr_in_service_actions[] = {
+	{PR_IN_READ_KEYS, spc_pr_read_keys},
+	{PR_IN_READ_RESERVATION, spc_pr_read_reservation},
+	{PR_IN_REPORT_CAPABILITIES, spc_pr_report_capabilities},
+	{PR_IN_READ_FULL_STATUS, spc_pr_read_full_status},
+	{0, NULL}
+};
+
+int persistent_reserve_in(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t action;
+	struct service_action *service_action;
+
+	action = cmd->scb[1] & 0x1f;
+	service_action = find_service_action(pr_in_service_actions, action);
+
+	if (!service_action) {
+		scsi_set_in_resid_by_actual(cmd, 0);
+		sense_data_build(cmd, ILLEGAL_REQUEST,
+				ASC_INVALID_FIELD_IN_CDB);
+		return SAM_STAT_CHECK_CONDITION;
+	}
+
+	return service_action->cmd_perform(host_no, cmd);
+}
+
+/**
+ * PERSISTENT RESERVE OUT - 5Fh
+ * Ref: 6.14
+ */
+#define PR_OUT_REGISTER 0
+#define PR_OUT_RESERVE 1
+#define PR_OUT_RELEASE 2
+#define PR_OUT_CLEAR 3
+#define PR_OUT_PREEMPT 4
+#define PR_OUT_PREEMPT_ABORT 5
+#define PR_OUT_REGISTER_IGNORE 6
+#define PR_OUT_REGISTER_MOVE 7
+
+static int spc_pr_register(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_reserve(int host_no, struct scsi_cmd *cmd)
+{
+	dprintf("**** Called ****\n");
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_release(int host_no, struct scsi_cmd *cmd)
+{
+	dprintf("**** Called ****\n");
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_clear(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_preempt(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_preempt_abort(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_register_ignore(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_register_move(int host_no, struct scsi_cmd *cmd)
+{
+	struct scsi_pr *pr;
+
+	dprintf("**** Called ****\n");
+	pr = &cmd->dev->pr;
+	pr->PRgeneration += 1;
+
+	sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+struct service_action pr_out_service_actions[] = {
+	{PR_OUT_REGISTER, spc_pr_register},
+	{PR_OUT_RESERVE, spc_pr_reserve},
+	{PR_OUT_RELEASE, spc_pr_release},
+	{PR_OUT_CLEAR, spc_pr_clear},
+	{PR_OUT_PREEMPT, spc_pr_preempt},
+	{PR_OUT_PREEMPT_ABORT, spc_pr_preempt_abort},
+	{PR_OUT_REGISTER_IGNORE, spc_pr_register_ignore},
+	{PR_OUT_REGISTER_MOVE, spc_pr_register_move},
+	{0, NULL}
+};
+
+int persistent_reserve_out(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t action;
+	struct service_action *service_action;
+
+	action = cmd->scb[1] & 0x1f;
+	service_action = find_service_action(pr_out_service_actions, action);
+
+	if (!service_action) {
+		scsi_set_in_resid_by_actual(cmd, 0);
+		sense_data_build(cmd, ILLEGAL_REQUEST,
+				ASC_INVALID_FIELD_IN_CDB);
+		return SAM_STAT_CHECK_CONDITION;
+	}
+
+	return service_action->cmd_perform(host_no, cmd);
+}
+
 int spc_illegal_op(int host_no, struct scsi_cmd *cmd)
 {
 	dump_cdb(cmd);
diff --git a/usr/spc.h b/usr/spc.h
index 8fe3e3c..a26141f 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -2,6 +2,8 @@
 #define __SPC_H
 
 extern struct service_action maint_in_service_actions[];
+extern struct service_action pr_in_service_actions[];
+extern struct service_action pr_out_service_actions[];
 extern int spc_maint_in(int host_no, struct scsi_cmd *cmd);
 extern int spc_inquiry(int host_no, struct scsi_cmd *cmd);
 extern int spc_report_luns(int host_no, struct scsi_cmd *cmd);
@@ -9,6 +11,8 @@ extern int spc_start_stop(int host_no, struct scsi_cmd *cmd);
 extern int spc_test_unit(int host_no, struct scsi_cmd *cmd);
 extern int spc_request_sense(int host_no, struct scsi_cmd *cmd);
 extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd);
+extern int persistent_reserve_in(int host_no, struct scsi_cmd *cmd);
+extern int persistent_reserve_out(int host_no, struct scsi_cmd *cmd);
 extern int spc_lu_init(struct scsi_lu *lu);
 
 typedef int (match_fn_t)(struct scsi_lu *lu, char *params);
diff --git a/usr/ssc.c b/usr/ssc.c
index 2630a6a..61021c3 100644
--- a/usr/ssc.c
+++ b/usr/ssc.c
@@ -251,8 +251,8 @@ static struct device_type_template ssc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{persistent_reserve_in, pr_in_service_actions,},
+		{persistent_reserve_out, pr_out_service_actions,},
 
 		[0x60 ... 0x7f] = {spc_illegal_op,},
 
diff --git a/usr/tgtd.h b/usr/tgtd.h
index 4febcd3..0f675bb 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -19,6 +19,11 @@
 
 #define VENDOR_ID	"IET"
 
+/* 8 byte reservation key size */
+#define PR_KEY_SZ 8
+/* Number of PR keys we can store at any one time */
+#define PR_RESERVATION_SZ 4
+
 #define _TAB1 "    "
 #define _TAB2 _TAB1 _TAB1
 #define _TAB3 _TAB1 _TAB1 _TAB1
@@ -66,6 +71,14 @@ struct lu_phy_attr {
 	char online;		/* Logical Unit online */
 	char sense_format;	/* Descrptor format sense data supported */
 
+/* Check pr_generation against current scsi_pr->PRgeneration to see if
+ * we need to go thru complete check. If PRgeneration <= pr_generation,
+ * then no reservation state has changed and we can just return current
+ * persistent reservation state (pr_state)
+ */
+	uint8_t pr_state;	/* Persistent Reserve state for this lu */
+	uint32_t pr_generation;	/* Persistent Reserve generation key */
+
 	/* VPD pages 0x80 -> 0xff masked with 0x80*/
 	struct vpd *lu_vpd[1 << PCODE_SHIFT];
 };
@@ -126,6 +139,18 @@ struct mode_pg {
 	uint8_t mode_data[0];	/* Rest of mode page info */
 };
 
+struct scsi_pr_key {
+	uint8_t state;
+	uint8_t data[PR_KEY_SZ];
+	int host_no;
+};
+
+struct scsi_pr {
+	/* Persistent Reservation Generation */
+	uint32_t PRgeneration;
+	struct scsi_pr_key pr_key[PR_RESERVATION_SZ];
+};
+
 struct scsi_lu {
 	int fd;
 	uint64_t addr; /* persistent mapped address */
@@ -150,6 +175,8 @@ struct scsi_lu {
 	uint8_t	mode_block_descriptor[BLOCK_DESCRIPTOR_LEN];
 	struct mode_pg *mode_pgs[0x3f];
 
+	struct scsi_pr pr;
+
 	struct lu_phy_attr attrs;
 
 	/* A pointer for each modules private use.
-- 
1.5.4.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Add-PERSISTENT-SCSI-RESERVE-IN-support.patch
Type: text/x-diff
Size: 14764 bytes
Desc: not available
URL: <http://lists.wpkg.org/pipermail/stgt/attachments/20080821/9874fd32/attachment-0001.patch>


More information about the stgt mailing list