[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