Hello Tomo, I would like to see this patch merged. As you say, there are no ill effects on existing features. I've only managed to test basic PERSISTENT REGISTRATION/RESERVATION features using 'sg_persist' and works as expected. Cheers Mark On Sun, Nov 15, 2009 at 8:17 PM, FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> wrote: > I think that the persistent reservation support patch is ready for > merging. > > The patch should not affect the existing features (that is, it doesn't > hurt you who are not interested in persistent reservation support). > > = > From: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> > Subject: [PATCH] add persistent reservation support > > This supports: > > - PR_IN_READ_KEYS > - PR_IN_READ_RESERVATION > - PR_IN_REPORT_CAPABILITIES > > - PR_OUT_REGISTER > - PR_OUT_RESERVE > - PR_OUT_RELEASE > - PR_OUT_CLEAR > - PR_OUT_PREEMPT > - PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY > - PR_OUT_REGISTER_AND_MOVE > > Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> > --- > usr/driver.h | 2 + > usr/iscsi/iscsid.c | 39 +++ > usr/iscsi/iscsid.h | 1 + > usr/iscsi/session.c | 2 +- > usr/sbc.c | 38 ++-- > usr/scc.c | 2 +- > usr/scsi.c | 3 + > usr/scsi.h | 32 +++ > usr/smc.c | 2 +- > usr/spc.c | 687 ++++++++++++++++++++++++++++++++++++++++++++++++++- > usr/spc.h | 7 +- > usr/ssc.c | 2 +- > usr/target.c | 16 ++ > usr/target.h | 1 + > usr/tgtd.h | 22 ++ > 15 files changed, 824 insertions(+), 32 deletions(-) > > diff --git a/usr/driver.h b/usr/driver.h > index f9cb386..f53e012 100644 > --- a/usr/driver.h > +++ b/usr/driver.h > @@ -21,6 +21,8 @@ struct tgt_driver { > int (*cmd_end_notify)(uint64_t nid, int result, struct scsi_cmd *); > int (*mgmt_end_notify)(struct mgmt_req *); > > + int (*transportid)(int, uint64_t, char *, int); > + > const char *default_bst; > }; > > diff --git a/usr/iscsi/iscsid.c b/usr/iscsi/iscsid.c > index 0199978..5515fb9 100644 > --- a/usr/iscsi/iscsid.c > +++ b/usr/iscsi/iscsid.c > @@ -2241,6 +2241,44 @@ out: > return ret; > } > > +static int iscsi_transportid(int tid, uint64_t itn_id, char *buf, int size) > +{ > + struct iscsi_session *session; > + char *p; > + uint16_t len; > + > + session = session_lookup_by_tsih(itn_id); > + if (!session) > + return 0; > + > + len = 4; > + len += strlen(session->initiator) + 1; > + len += 5; /* separator */ > + len += 7; /* isid + '\0' */ > + > + len = ALIGN(len, 4); > + > + if (len > size) > + return len; > + > + memset(buf, 0, size); > + > + buf[0] = 0x05; > + buf[0] |= 0x40; > + > + put_unaligned_be16(len - 4, buf + 2); > + > + sprintf(buf + 4, session->initiator); > + > + p = buf + (4 + strlen(session->initiator) + 1); > + > + p += sprintf(p, ",i,0x"); > + > + memcpy(p, session->isid, sizeof(session->isid)); > + > + return len; > +} > + > static struct tgt_driver iscsi = { > .name = "iscsi", > .use_kernel = 0, > @@ -2253,6 +2291,7 @@ static struct tgt_driver iscsi = { > .show = iscsi_target_show, > .cmd_end_notify = iscsi_scsi_cmd_done, > .mgmt_end_notify = iscsi_tm_done, > + .transportid = iscsi_transportid, > .default_bst = "rdwr", > }; > > diff --git a/usr/iscsi/iscsid.h b/usr/iscsi/iscsid.h > index 9eecfa2..6b982cb 100644 > --- a/usr/iscsi/iscsid.h > +++ b/usr/iscsi/iscsid.h > @@ -293,6 +293,7 @@ extern void iscsi_free_cmd_task(struct iscsi_task *task); > > /* session.c */ > extern struct iscsi_session *session_find_name(int tid, const char *iname, uint8_t *isid); > +extern struct iscsi_session *session_lookup_by_tsih(uint16_t tsih); > extern int session_create(struct iscsi_connection *conn); > extern void session_get(struct iscsi_session *session); > extern void session_put(struct iscsi_session *session); > diff --git a/usr/iscsi/session.c b/usr/iscsi/session.c > index 028d538..46864c7 100644 > --- a/usr/iscsi/session.c > +++ b/usr/iscsi/session.c > @@ -52,7 +52,7 @@ struct iscsi_session *session_find_name(int tid, const char *iname, uint8_t *isi > return NULL; > } > > -static struct iscsi_session *session_lookup_by_tsih(uint16_t tsih) > +struct iscsi_session *session_lookup_by_tsih(uint16_t tsih) > { > struct iscsi_session *session; > list_for_each_entry(session, &sessions_list, hlist) { > diff --git a/usr/sbc.c b/usr/sbc.c > index 710165d..f443508 100644 > --- a/usr/sbc.c > +++ b/usr/sbc.c > @@ -284,9 +284,9 @@ static struct device_type_template sbc_template = { > {spc_illegal_op,}, > {spc_illegal_op,}, > > - {sbc_rw,}, > + {sbc_rw, NULL, PR_EA_FA|PR_EA_FN}, > {spc_illegal_op,}, > - {sbc_rw,}, > + {sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > @@ -299,14 +299,14 @@ static struct device_type_template sbc_template = { > {spc_inquiry,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {sbc_mode_select}, > + {sbc_mode_select, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {sbc_reserve,}, > {sbc_release,}, > > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_mode_sense,}, > - {spc_start_stop,}, > + {spc_mode_sense, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > + {spc_start_stop, NULL, PR_SPECIAL}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > @@ -322,14 +322,14 @@ static struct device_type_template sbc_template = { > {spc_illegal_op,}, > {spc_illegal_op,}, > > - {sbc_rw}, > + {sbc_rw, NULL, PR_EA_FA|PR_EA_FN}, > {spc_illegal_op,}, > - {sbc_rw}, > + {sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {sbc_verify,}, > + {sbc_verify, NULL, PR_EA_FA|PR_EA_FN}, > > /* 0x30 */ > {spc_illegal_op,}, > @@ -337,7 +337,7 @@ static struct device_type_template sbc_template = { > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {sbc_sync_cache,}, > + {sbc_sync_cache, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > > @@ -358,18 +358,18 @@ static struct device_type_template sbc_template = { > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {sbc_mode_select,}, > + {sbc_mode_select, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_mode_sense,}, > - {spc_illegal_op,}, > - {spc_illegal_op,}, > + {spc_mode_sense, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > + {spc_service_action, persistent_reserve_in_actions,}, > + {spc_service_action, persistent_reserve_out_actions,}, > > [0x60 ... 0x7f] = {spc_illegal_op,}, > > @@ -383,9 +383,9 @@ static struct device_type_template sbc_template = { > {spc_illegal_op,}, > {spc_illegal_op,}, > > - {sbc_rw,}, > + {sbc_rw, NULL, PR_EA_FA|PR_EA_FN}, > {spc_illegal_op,}, > - {sbc_rw,}, > + {sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > @@ -394,7 +394,7 @@ static struct device_type_template sbc_template = { > > /* 0x90 */ > {spc_illegal_op,}, > - {sbc_sync_cache,}, > + {sbc_sync_cache, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > @@ -415,15 +415,15 @@ static struct device_type_template sbc_template = { > {spc_report_luns,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_maint_in, maint_in_service_actions,}, > + {spc_service_action, maint_in_service_actions,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > > - {sbc_rw,}, > + {sbc_rw, NULL, PR_EA_FA|PR_EA_FN}, > {spc_illegal_op,}, > - {sbc_rw,}, > + {sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > diff --git a/usr/scc.c b/usr/scc.c > index eed48a0..27bc07b 100644 > --- a/usr/scc.c > +++ b/usr/scc.c > @@ -143,7 +143,7 @@ static struct device_type_template scc_template = { > {spc_report_luns,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_maint_in, maint_in_service_actions,}, > + {spc_service_action, maint_in_service_actions,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > diff --git a/usr/scsi.c b/usr/scsi.c > index daf4aef..bf39328 100644 > --- a/usr/scsi.c > +++ b/usr/scsi.c > @@ -227,6 +227,9 @@ int scsi_cmd_perform(int host_no, struct scsi_cmd *cmd) > return SAM_STAT_CHECK_CONDITION; > } > > + if (spc_access_check(cmd)) > + return SAM_STAT_RESERVATION_CONFLICT; > + > return cmd->dev->dev_type_template.ops[op].cmd_perform(host_no, cmd); > } > > diff --git a/usr/scsi.h b/usr/scsi.h > index 84fadff..80a0431 100644 > --- a/usr/scsi.h > +++ b/usr/scsi.h > @@ -201,12 +201,14 @@ > #define ASC_INVALID_FIELD_IN_CDB 0x2400 > #define ASC_LUN_NOT_SUPPORTED 0x2500 > #define ASC_INVALID_FIELD_IN_PARMS 0x2600 > +#define ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION 0x2604 > #define ASC_INCOMPATIBLE_FORMAT 0x3005 > #define ASC_SAVING_PARMS_UNSUP 0x3900 > #define ASC_MEDIUM_DEST_FULL 0x3b0d > #define ASC_MEDIUM_SRC_EMPTY 0x3b0e > #define ASC_POSITION_PAST_BOM 0x3b0c > #define ASC_MEDIUM_REMOVAL_PREVENTED 0x5302 > +#define ASC_INSUFFICENT_REGISTRATION_RESOURCES 0x5504 > #define ASC_BAD_MICROCODE_DETECTED 0x8283 > > /* Key 6: Unit Attention */ > @@ -214,6 +216,8 @@ > #define ASC_POWERON_RESET 0x2900 > #define ASC_I_T_NEXUS_LOSS_OCCURRED 0x2907 > #define ASC_MODE_PARAMETERS_CHANGED 0x2a01 > +#define ASC_RESERVATIONS_PREEMPTED 0x2a03 > +#define ASC_RESERVATIONS_RELEASED 0x2a04 > #define ASC_INSUFFICIENT_TIME_FOR_OPERATION 0x2e00 > #define ASC_COMMANDS_CLEARED_BY_ANOTHOR_INI 0x2f00 > #define ASC_MICROCODE_DOWNLOADED 0x3f01 > @@ -225,4 +229,32 @@ > #define ASC_WRITE_PROTECT 0x2700 > #define ASC_MEDIUM_OVERWRITE_ATTEMPTED 0x300c > > + > +/* PERSISTENT_RESERVE_IN service action codes */ > +#define PR_IN_READ_KEYS 0x00 > +#define PR_IN_READ_RESERVATION 0x01 > +#define PR_IN_REPORT_CAPABILITIES 0x02 > +#define PR_IN_READ_FULL_STATUS 0x03 > + > +/* PERSISTENT_RESERVE_OUT service action codes */ > +#define PR_OUT_REGISTER 0x00 > +#define PR_OUT_RESERVE 0x01 > +#define PR_OUT_RELEASE 0x02 > +#define PR_OUT_CLEAR 0x03 > +#define PR_OUT_PREEMPT 0x04 > +#define PR_OUT_PREEMPT_AND_ABORT 0x05 > +#define PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY 0x06 > +#define PR_OUT_REGISTER_AND_MOVE 0x07 > + > +/* Persistent Reservation scope */ > +#define PR_LU_SCOPE 0x00 > + > +/* Persistent Reservation Type Mask format */ > +#define PR_TYPE_WRITE_EXCLUSIVE 0x01 > +#define PR_TYPE_EXCLUSIVE_ACCESS 0x03 > +#define PR_TYPE_WRITE_EXCLUSIVE_REGONLY 0x05 > +#define PR_TYPE_EXCLUSIVE_ACCESS_REGONLY 0x06 > +#define PR_TYPE_WRITE_EXCLUSIVE_ALLREG 0x07 > +#define PR_TYPE_EXCLUSIVE_ACCESS_ALLREG 0x08 > + > #endif > diff --git a/usr/smc.c b/usr/smc.c > index ab36e9c..6430882 100644 > --- a/usr/smc.c > +++ b/usr/smc.c > @@ -861,7 +861,7 @@ struct device_type_template smc_template = { > {spc_report_luns,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_maint_in, maint_in_service_actions,}, > + {spc_service_action, maint_in_service_actions,}, > {spc_illegal_op,}, > {smc_move_medium,}, > {spc_illegal_op,}, > diff --git a/usr/spc.c b/usr/spc.c > index 1a75766..4d57091 100644 > --- a/usr/spc.c > +++ b/usr/spc.c > @@ -720,7 +720,7 @@ struct service_action maint_in_service_actions[] = { > {0, NULL} > }; > > -struct service_action * > +static struct service_action * > find_service_action(struct service_action *service_action, uint32_t action) > { > while (service_action->cmd_perform) { > @@ -731,16 +731,20 @@ find_service_action(struct service_action *service_action, uint32_t action) > return NULL; > } > > -/** > - * This functions emulates the various commands using the 0xa3 cdb opcode > +/* > + * This is useful for the various commands using the SERVICE ACTION > + * format. > */ > -int spc_maint_in(int host_no, struct scsi_cmd *cmd) > +int spc_service_action(int host_no, struct scsi_cmd *cmd) > { > uint8_t action; > - struct service_action *service_action; > + unsigned char op = cmd->scb[0]; > + struct service_action *service_action, *actions; > > action = cmd->scb[1] & 0x1f; > - service_action = find_service_action(maint_in_service_actions, action); > + actions = cmd->dev->dev_type_template.ops[op].service_actions; > + > + service_action = find_service_action(actions, action); > > if (!service_action) { > scsi_set_in_resid_by_actual(cmd, 0); > @@ -752,6 +756,676 @@ int spc_maint_in(int host_no, struct scsi_cmd *cmd) > return service_action->cmd_perform(host_no, cmd); > } > > +static int is_pr_holder(struct scsi_lu *lu, struct registration *reg) > +{ > + if (lu->pr_holder->pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || > + lu->pr_holder->pr_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) > + return 1; > + > + if (lu->pr_holder == reg) > + return 1; > + > + return 0; > +} > + > +static int spc_pr_read_keys(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + struct registration *reg; > + uint16_t len; > + uint32_t keys; > + uint8_t *buf; > + int off; > + > + len = get_unaligned_be16(cmd->scb + 7); > + if (len < 8) > + goto sense; > + > + if (scsi_get_in_length(cmd) < len) > + goto sense; > + > + buf = scsi_get_in_buffer(cmd); > + memset(buf, 0, len); > + > + len &= ~(8 - 1); > + keys = 0; > + off = 8; > + > + put_unaligned_be32(cmd->dev->prgeneration, &buf[0]); > + > + list_for_each_entry(reg, &cmd->dev->registration_list, > + registration_siblings) { > + > + if (!len) > + continue; > + put_unaligned_be64(reg->key, &buf[off]); > + > + len -= 8; > + off += 8; > + keys++; > + } > + > + put_unaligned_be32(keys * 8, &buf[4]); > + > + scsi_set_in_resid_by_actual(cmd, keys * 8 + 8); > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_read_reservation(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + struct registration *reg; > + uint16_t len; > + uint8_t *buf; > + uint64_t res_key; > + > + reg = cmd->dev->pr_holder; > + > + if (reg) > + len = 24; > + else > + len = 8; > + > + if (get_unaligned_be16(cmd->scb + 7) < len) > + goto sense; > + > + if (scsi_get_in_length(cmd) < len) > + goto sense; > + > + buf = scsi_get_in_buffer(cmd); > + memset(buf, 0, len); > + > + put_unaligned_be32(cmd->dev->prgeneration, &buf[0]); > + > + if (reg) { > + put_unaligned_be32(16, &buf[4]); > + > + if (reg->pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || > + reg->pr_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) > + res_key = 0; > + else > + res_key = reg->key; > + > + put_unaligned_be32(res_key, &buf[8]); > + buf[21] = ((reg->pr_scope << 4) & 0xf0) | (reg->pr_type & 0x0f); > + } else > + put_unaligned_be32(0, &buf[4]); > + > + scsi_set_in_resid_by_actual(cmd, len); > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_report_capabilities(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + uint8_t *buf; > + uint16_t len; > + > + len = get_unaligned_be16(cmd->scb + 7); > + if (len < 8) > + goto sense; > + > + if (scsi_get_in_length(cmd) < len) > + goto sense; > + > + buf = scsi_get_in_buffer(cmd); > + > + len = 8; > + > + memset(buf, 0, len); > + > + put_unaligned_be16(len, &buf[0]); > + > + /* we don't set any capability for now */ > + > + /* Persistent Reservation Type Mask format */ > + buf[4] |= 0x80; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */ > + buf[4] |= 0x40; /* PR_TYPE_EXCLUSIVE_ACCESS_REGONLY */ > + buf[4] |= 0x20; /* PR_TYPE_WRITE_EXCLUSIVE_REGONLY */ > + buf[4] |= 0x08; /* PR_TYPE_EXCLUSIVE_ACCESS */ > + buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */ > + buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */ > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +struct service_action persistent_reserve_in_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}, > + {0, NULL}, > +}; > + > +static struct registration *lookup_registration_by_nexus(struct scsi_lu *lu, > + struct it_nexus *itn) > +{ > + struct registration *reg; > + > + list_for_each_entry(reg, &lu->registration_list, registration_siblings) { > + if (reg->nexus_id == itn->itn_id && > + reg->ctime == itn->ctime) > + return reg; > + } > + > + return NULL; > +} > + > +static void __unregister(struct scsi_lu *lu, struct registration *reg) > +{ > + list_del(®->registration_siblings); > + free(reg); > +} > + > +static int check_pr_out_basic_parameter(struct scsi_cmd *cmd) > +{ > + uint8_t spec_i_pt, all_tg_pt, aptpl; > + uint8_t *buf; > + uint16_t len = 24; > + > + if (get_unaligned_be16(cmd->scb + 7) < len) > + return 1; > + > + if (scsi_get_out_length(cmd) < len) > + return 1; > + > + buf = scsi_get_out_buffer(cmd); > + > + spec_i_pt = buf[20] & (1U << 3); > + all_tg_pt = buf[20] & (1U << 2); > + aptpl = buf[20] & (1U << 0); > + > + if (spec_i_pt | all_tg_pt | aptpl) { > + /* > + * for now, we say that we don't support these bits > + * via REPORT CAPABILITIES. > + */ > + return 1; > + } > + > + return 0; > +} > + > +static int spc_pr_register(int host_no, struct scsi_cmd *cmd) > +{ > + uint8_t force, key = ILLEGAL_REQUEST; > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint64_t res_key, sa_res_key; > + int ret; > + uint8_t *buf; > + struct registration *reg; > + > + force = ((cmd->scb[1] & 0x1f) == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY); > + > + ret = check_pr_out_basic_parameter(cmd); > + if (ret) > + goto sense; > + > + buf = scsi_get_out_buffer(cmd); > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (reg) { > + if (force || reg->key == res_key) { > + if (sa_res_key) > + reg->key = sa_res_key; > + else > + __unregister(cmd->dev, reg); > + } else > + return SAM_STAT_RESERVATION_CONFLICT; > + } else { > + if (force || !res_key) { > + if (sa_res_key) { > + reg = zalloc(sizeof(*reg)); > + if (!reg) { > + key = ILLEGAL_REQUEST; > + asc = ASC_INSUFFICENT_REGISTRATION_RESOURCES; > + goto sense; > + } > + > + reg->key = sa_res_key; > + reg->nexus_id = cmd->cmd_itn_id; > + reg->ctime = cmd->it_nexus->ctime; > + list_add_tail(®->registration_siblings, > + &cmd->dev->registration_list); > + } else > + ; /* do nothing */ > + } else > + return SAM_STAT_RESERVATION_CONFLICT; > + } > + > + cmd->dev->prgeneration++; > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_reserve(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + uint8_t pr_scope, pr_type; > + uint8_t *buf; > + uint64_t res_key, sa_res_key; > + int ret; > + struct registration *reg, *holder; > + > + ret = check_pr_out_basic_parameter(cmd); > + if (ret) > + goto sense; > + > + pr_scope = (cmd->scb[2] & 0xf0) >> 4; > + pr_type = cmd->scb[2] & 0x0f; > + > + buf = scsi_get_out_buffer(cmd); > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + switch (pr_type) { > + case PR_TYPE_WRITE_EXCLUSIVE: > + case PR_TYPE_EXCLUSIVE_ACCESS: > + case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: > + case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: > + case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: > + case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: > + break; > + default: > + goto sense; > + } > + > + if (pr_scope != PR_LU_SCOPE) > + goto sense; > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + holder = cmd->dev->pr_holder; > + if (holder) { > + if (!is_pr_holder(cmd->dev, reg)) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + if (holder->pr_type != pr_type || > + holder->pr_scope != pr_scope) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + return SAM_STAT_GOOD; > + } > + > + reg->pr_scope = pr_scope; > + reg->pr_type = pr_type; > + cmd->dev->pr_holder = reg; > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_release(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + uint8_t pr_scope, pr_type; > + uint8_t *buf; > + uint64_t res_key, sa_res_key; > + int ret; > + struct registration *reg, *holder, *sibling; > + > + ret = check_pr_out_basic_parameter(cmd); > + if (ret) > + goto sense; > + > + pr_scope = (cmd->scb[2] & 0xf0) >> 4; > + pr_type = cmd->scb[2] & 0x0f; > + > + buf = scsi_get_out_buffer(cmd); > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + holder = cmd->dev->pr_holder; > + if (!holder) > + return SAM_STAT_GOOD; > + > + if (!is_pr_holder(cmd->dev, reg)) > + return SAM_STAT_GOOD; > + > + if (res_key != reg->key) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + if (reg->pr_scope != pr_scope || reg->pr_type != pr_type) { > + asc = ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION; > + goto sense; > + } > + > + cmd->dev->pr_holder = NULL; > + reg->pr_scope = 0; > + reg->pr_type = 0; > + > + switch (pr_type) { > + case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: > + case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: > + case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: > + case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: > + return SAM_STAT_GOOD; > + default: > + ; > + } > + > + list_for_each_entry(sibling, &cmd->dev->registration_list, > + registration_siblings) { > + /* we don't send myself */ > + if (sibling == reg) > + continue; > + > + ua_sense_add_other_it_nexus(sibling->nexus_id, > + cmd->dev, ASC_RESERVATIONS_RELEASED); > + } > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_clear(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + uint8_t *buf; > + uint64_t res_key, sa_res_key; > + int ret; > + struct registration *reg, *holder, *sibling, *n; > + > + ret = check_pr_out_basic_parameter(cmd); > + if (ret) > + goto sense; > + > + buf = scsi_get_out_buffer(cmd); > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + if (reg->key != res_key) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + holder = cmd->dev->pr_holder; > + if (holder) { > + holder->pr_scope = 0; > + holder->pr_type = 0; > + cmd->dev->pr_holder = NULL; > + } > + > + list_for_each_entry_safe(sibling, n, &cmd->dev->registration_list, > + registration_siblings) { > + /* we don't send myself */ > + if (sibling != reg) > + ua_sense_add_other_it_nexus(sibling->nexus_id, > + cmd->dev, > + ASC_RESERVATIONS_PREEMPTED); > + list_del(&sibling->registration_siblings); > + free(sibling); > + } > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_preempt(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + int ret, abort; > + uint64_t res_key, sa_res_key; > + uint8_t pr_scope, pr_type; > + uint8_t *buf; > + struct registration *reg, *sibling, *n; > + > + ret = check_pr_out_basic_parameter(cmd); > + if (ret) > + goto sense; > + > + abort = ((cmd->scb[1] & 0x1f) == PR_OUT_PREEMPT_AND_ABORT); > + > + pr_scope = (cmd->scb[2] & 0xf0) >> 4; > + pr_type = cmd->scb[2] & 0x0f; > + > + buf = scsi_get_out_buffer(cmd); > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + if (reg->key != res_key) > + return SAM_STAT_RESERVATION_CONFLICT; > + > +remove_registration: > + if (!cmd->dev->pr_holder) { > + list_for_each_entry_safe(sibling, n, &cmd->dev->registration_list, > + registration_siblings) { > + > + if (sibling->key == sa_res_key) { > + __unregister(cmd->dev, sibling); > + break; > + } > + } > + > + return SAM_STAT_GOOD; > + } > + > + if (cmd->dev->pr_holder->pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || > + cmd->dev->pr_holder->pr_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) { > + > + if (sa_res_key) > + goto remove_registration; > + > + list_for_each_entry_safe(sibling, n, &cmd->dev->registration_list, > + registration_siblings) { > + > + if (sibling == reg) > + continue; > + > + __unregister(cmd->dev, sibling); > + } > + > + cmd->dev->pr_holder = reg; > + reg->pr_type = pr_type; > + reg->pr_scope = pr_scope; > + > + return SAM_STAT_GOOD; > + } > + > + if (cmd->dev->pr_holder->key == sa_res_key) { > + cmd->dev->pr_holder = reg; > + reg->pr_type = pr_type; > + reg->pr_scope = pr_scope; > + > + goto remove_registration; > + } > + > + if (sa_res_key) > + goto remove_registration; > + > + else > + goto sense; > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +static int spc_pr_register_and_move(int host_no, struct scsi_cmd *cmd) > +{ > + uint16_t asc = ASC_INVALID_FIELD_IN_CDB; > + uint8_t key = ILLEGAL_REQUEST; > + char *buf; > + uint8_t pr_scope, pr_type; > + uint8_t unreg; > + uint64_t res_key, sa_res_key; > + uint32_t addlen, idlen; > + struct registration *reg, *dst; > + uint16_t len = 24; > + int (*id)(int, uint64_t, char *, int); > + char tpid[300]; /* large enough? */ > + > + pr_scope = (cmd->scb[2] & 0xf0) >> 4; > + pr_type = cmd->scb[2] & 0x0f; > + > + if (get_unaligned_be16(cmd->scb + 7) < len) > + goto sense; > + > + if (scsi_get_out_length(cmd) < len) > + goto sense; > + > + buf = scsi_get_out_buffer(cmd); > + > + if (buf[17] & 0x01) > + goto sense; > + > + unreg = buf[17] & 0x02; > + > + res_key = get_unaligned_be64(buf); > + sa_res_key = get_unaligned_be64(buf + 8); > + > + addlen = get_unaligned_be32(buf + 24); > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + if (!reg) { > + if (cmd->dev->pr_holder) > + return SAM_STAT_RESERVATION_CONFLICT; > + else > + goto sense; > + } > + > + if (!is_pr_holder(cmd->dev, reg)) > + return SAM_STAT_GOOD; > + > + if (reg->key != res_key) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + if (!sa_res_key) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + list_for_each_entry(dst, &cmd->dev->registration_list, > + registration_siblings) > + if (dst->key == sa_res_key) > + goto found; > + > + /* we can't find the destination */ > + goto sense; > +found: > + id = tgt_drivers[cmd->c_target->lid]->transportid; > + if (id) { > + memset(tpid, 0, sizeof(tpid)); > + idlen = id(cmd->dev->tgt->tid, dst->nexus_id, tpid, sizeof(tpid)); > + if (addlen) { > + if (strncmp(tpid, buf + 28, strlen(tpid))) > + goto sense; > + } > + } > + > + cmd->dev->pr_holder = dst; > + > + if (unreg) > + __unregister(cmd->dev, reg); > + > + return SAM_STAT_GOOD; > +sense: > + scsi_set_in_resid_by_actual(cmd, 0); > + sense_data_build(cmd, key, asc); > + return SAM_STAT_CHECK_CONDITION; > +} > + > +struct service_action persistent_reserve_out_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_AND_ABORT, spc_pr_preempt}, */ > + {PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, spc_pr_register}, > + {PR_OUT_REGISTER_AND_MOVE, spc_pr_register_and_move}, > + {0, NULL}, > +}; > + > +int spc_access_check(struct scsi_cmd *cmd) > +{ > + unsigned char op = cmd->scb[0]; > + uint8_t bits; > + uint8_t pr_type; > + struct registration *reg; > + int conflict = 0; > + > + if (!cmd->dev->pr_holder) > + return 0; > + > + reg = lookup_registration_by_nexus(cmd->dev, cmd->it_nexus); > + > + if (reg && is_pr_holder(cmd->dev, reg)) > + return 0; > + > + pr_type = cmd->dev->pr_holder->pr_type; > + bits = cmd->dev->dev_type_template.ops[op].pr_conflict_bits; > + > + if (pr_type == PR_TYPE_WRITE_EXCLUSIVE || > + pr_type == PR_TYPE_EXCLUSIVE_ACCESS) > + conflict = bits & (PR_WE_FA|PR_EA_FA); > + else { > + if (reg) > + conflict = bits & PR_RR_FR; > + else { > + if (pr_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY || > + pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) > + conflict = bits & PR_WE_FN; > + else > + conflict = bits & PR_EA_FN; > + } > + } > + > + return conflict; > +} > + > int spc_request_sense(int host_no, struct scsi_cmd *cmd) > { > uint8_t *data; > @@ -1065,4 +1739,3 @@ void spc_lu_exit(struct scsi_lu *lu) > if (lu->mode_pgs[i]) > free(lu->mode_pgs[i]); > } > - > diff --git a/usr/spc.h b/usr/spc.h > index cfc9cf3..430a882 100644 > --- a/usr/spc.h > +++ b/usr/spc.h > @@ -1,8 +1,10 @@ > #ifndef __SPC_H > #define __SPC_H > > -extern struct service_action maint_in_service_actions[]; > -extern int spc_maint_in(int host_no, struct scsi_cmd *cmd); > +extern struct service_action maint_in_service_actions[], > + persistent_reserve_in_actions[], persistent_reserve_out_actions[]; > + > +extern int spc_service_action(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); > extern int spc_start_stop(int host_no, struct scsi_cmd *cmd); > @@ -27,4 +29,5 @@ extern struct vpd *alloc_vpd(uint16_t size); > extern int spc_lu_online(struct scsi_lu *lu); > extern int spc_lu_offline(struct scsi_lu *lu); > > +extern int spc_access_check(struct scsi_cmd *cmd); > #endif > diff --git a/usr/ssc.c b/usr/ssc.c > index 834ec16..84fa317 100644 > --- a/usr/ssc.c > +++ b/usr/ssc.c > @@ -310,7 +310,7 @@ static struct device_type_template ssc_template = { > {spc_report_luns,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > - {spc_maint_in, maint_in_service_actions,}, > + {spc_service_action, maint_in_service_actions,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > {spc_illegal_op,}, > diff --git a/usr/target.c b/usr/target.c > index 14e3632..a69fe0b 100644 > --- a/usr/target.c > +++ b/usr/target.c > @@ -25,8 +25,10 @@ > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > +#include <time.h> > #include <unistd.h> > #include <sys/socket.h> > +#include <sys/time.h> > > #include "list.h" > #include "util.h" > @@ -36,6 +38,7 @@ > #include "scsi.h" > #include "tgtadm.h" > #include "parser.h" > +#include "spc.h" > > static LIST_HEAD(device_type_list); > > @@ -236,6 +239,7 @@ int it_nexus_create(int tid, uint64_t itn_id, int host_no, char *info) > struct it_nexus *itn; > struct scsi_lu *lu; > struct it_nexus_lu_info *itn_lu; > + struct timeval tv; > > dprintf("%d %" PRIu64 " %d\n", tid, itn_id, host_no); > /* for reserve/release code */ > @@ -257,6 +261,8 @@ int it_nexus_create(int tid, uint64_t itn_id, int host_no, char *info) > itn->nexus_target = target; > itn->info = info; > INIT_LIST_HEAD(&itn->it_nexus_lu_info_list); > + gettimeofday(&tv, NULL); > + itn->ctime = tv.tv_sec; > > list_for_each_entry(lu, &target->device_list, device_siblings) { > itn_lu = zalloc(sizeof(*itn_lu)); > @@ -490,6 +496,9 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params, > lu->tgt = target; > lu->lun = lun; > tgt_cmd_queue_init(&lu->cmd_queue); > + INIT_LIST_HEAD(&lu->registration_list); > + lu->prgeneration = 0; > + lu->pr_holder = NULL; > > if (lu->dev_type_template.lu_init) { > ret = lu->dev_type_template.lu_init(lu); > @@ -567,6 +576,7 @@ int tgt_device_destroy(int tid, uint64_t lun, int force) > struct scsi_lu *lu; > struct it_nexus *itn; > struct it_nexus_lu_info *itn_lu, *next; > + struct registration *reg, *reg_next; > int ret; > > dprintf("%u %" PRIu64 "\n", tid, lun); > @@ -606,6 +616,12 @@ int tgt_device_destroy(int tid, uint64_t lun, int force) > } > > list_del(&lu->device_siblings); > + > + list_for_each_entry_safe(reg, reg_next, &lu->registration_list, > + registration_siblings) { > + free(reg); > + } > + > free(lu); > > list_for_each_entry(itn, &target->it_nexus_list, nexus_siblings) { > diff --git a/usr/target.h b/usr/target.h > index 8fe30aa..9283431 100644 > --- a/usr/target.h > +++ b/usr/target.h > @@ -43,6 +43,7 @@ struct target { > > struct it_nexus { > uint64_t itn_id; > + long ctime; > > struct list_head cmd_hash_list[1 << HASH_ORDER]; > > diff --git a/usr/tgtd.h b/usr/tgtd.h > index febaed7..3323a9b 100644 > --- a/usr/tgtd.h > +++ b/usr/tgtd.h > @@ -90,8 +90,16 @@ struct service_action { > struct device_type_operations { > int (*cmd_perform)(int host_no, struct scsi_cmd *cmd); > struct service_action *service_actions; > + uint8_t pr_conflict_bits; > }; > > +#define PR_SPECIAL (1U << 5) > +#define PR_WE_FA (1U << 4) > +#define PR_EA_FA (1U << 3) > +#define PR_RR_FR (1U << 2) > +#define PR_WE_FN (1U << 1) > +#define PR_EA_FN (1U << 0) > + > struct device_type_template { > unsigned char type; > > @@ -126,6 +134,16 @@ struct mode_pg { > uint8_t mode_data[0]; /* Rest of mode page info */ > }; > > +struct registration { > + uint64_t key; > + uint64_t nexus_id; > + long ctime; > + struct list_head registration_siblings; > + > + uint8_t pr_scope; > + uint8_t pr_type; > +}; > + > struct scsi_lu { > int fd; > uint64_t addr; /* persistent mapped address */ > @@ -152,6 +170,10 @@ struct scsi_lu { > > struct lu_phy_attr attrs; > > + struct list_head registration_list; > + uint32_t prgeneration; > + struct registration *pr_holder; > + > /* A pointer for each modules private use. > * Currently used by ssc, smc and mmc modules. > */ > -- > 1.5.6.5 > > -- > To unsubscribe from this list: send the line "unsubscribe stgt" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html