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.patch> |