I'll be testing over the next few days and will advise of any success/failures. Many thanks Mark On Mon, Oct 26, 2009 at 12:16 AM, FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> wrote: > This is the initial try to support persistent reservation > > I finished only READ_KEYS, READ_RESERVATION, REPORT_CAPABILITIES, and > REGISTER, RESERVE, RELEASE (and they are not complete). > > So I guess that this enables you to only play sg_persist and see if > commands are rejected properly. But I'd really appreciate any input > (comments, test results, patches, etc). As I wrote before, I'd like to > add persistent reservation support and release 1.0.0 this year. > > = > From: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> > Date: Sun, 25 Oct 2009 22:03:41 +0900 > Subject: [PATCH] persistent reservation support > > Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> > --- > usr/sbc.c | 38 +++--- > usr/scc.c | 2 +- > usr/scsi.c | 40 +++++ > usr/scsi.h | 31 ++++ > usr/smc.c | 2 +- > usr/spc.c | 488 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- > usr/spc.h | 8 +- > usr/ssc.c | 2 +- > usr/target.c | 10 ++ > usr/tgtd.h | 21 +++ > 10 files changed, 611 insertions(+), 31 deletions(-) > > 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..61a2d7e 100644 > --- a/usr/scsi.c > +++ b/usr/scsi.c > @@ -183,6 +183,43 @@ uint32_t scsi_rw_count(uint8_t *scb) > return cnt; > } > > +static int scsi_pr_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->cmd_itn_id); > + > + if (reg == cmd->dev->pr_holder) > + 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 scsi_cmd_perform(int host_no, struct scsi_cmd *cmd) > { > int ret; > @@ -227,6 +264,9 @@ int scsi_cmd_perform(int host_no, struct scsi_cmd *cmd) > return SAM_STAT_CHECK_CONDITION; > } > > + if (scsi_pr_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..9fd78fd 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,7 @@ > #define ASC_POWERON_RESET 0x2900 > #define ASC_I_T_NEXUS_LOSS_OCCURRED 0x2907 > #define ASC_MODE_PARAMETERS_CHANGED 0x2a01 > +#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 +228,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..96ac8db 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,477 @@ int spc_maint_in(int host_no, struct scsi_cmd *cmd) > return service_action->cmd_perform(host_no, cmd); > } > > +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; > + 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; > +} > + > +static int spc_pr_read_full_status(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); > + > + put_unaligned_be32(cmd->dev->prgeneration, &buf[0]); > + > + len &= ~(24 - 1); > + off = 8; > + keys = 0; > + > + list_for_each_entry(reg, &cmd->dev->registration_list, > + registration_siblings) { > + > + if (!len) > + continue; > + > + put_unaligned_be64(reg->key, &buf[off + 0]); > + > + /* TODO */ > + > + put_unaligned_be32(0, &buf[off + 20]); > + > + keys++; > + len -= 24; > + off += 24; > + } > + > + put_unaligned_be32(keys * 24, &buf[4]); > + > + scsi_set_in_resid_by_actual(cmd, keys * 24 + 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; > +} > + > +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}, > + {PR_IN_READ_FULL_STATUS, spc_pr_read_full_status}, > + {0, NULL}, > +}; > + > +struct registration *lookup_registration_by_nexus(struct scsi_lu *lu, > + uint64_t nexus_id) > +{ > + struct registration *reg; > + > + list_for_each_entry(reg, &lu->registration_list, registration_siblings) { > + if (reg->nexus_id == nexus_id) > + return reg; > + } > + > + return NULL; > +} > + > +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->cmd_itn_id); > + if (reg) { > + if (force || reg->key == res_key) { > + if (sa_res_key) > + reg->key = sa_res_key; > + else { > + /* unregister */ > + list_del(®->registration_siblings); > + free(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; > + 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->cmd_itn_id); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + holder = cmd->dev->pr_holder; > + if (holder) { > + if (holder != 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->cmd_itn_id); > + if (!reg) > + return SAM_STAT_RESERVATION_CONFLICT; > + > + holder = cmd->dev->pr_holder; > + if (!holder) > + return SAM_STAT_GOOD; > + > + if (holder != 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; > +} > + > +#if 0 > +static int spc_pr_clear(int host_no, struct scsi_cmd *cmd) > +{ > + return SAM_STAT_GOOD; > +} > + > +static int spc_pr_preempt(int host_no, struct scsi_cmd *cmd) > +{ > + return SAM_STAT_GOOD; > +} > + > +static int spc_pr_preempt_and_abort(int host_no, struct scsi_cmd *cmd) > +{ > + return SAM_STAT_GOOD; > +} > + > +static int spc_pr_register_and_move(int host_no, struct scsi_cmd *cmd) > +{ > + return SAM_STAT_GOOD; > +} > +#endif > + > +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}, > +#if 0 > + {PR_OUT_CLEAR, spc_pr_clear}, > + {PR_OUT_PREEMPT, spc_pr_preempt}, > + {PR_OUT_PREEMPT_AND_ABORT, spc_pr_preempt_and_abort}, > + {PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, spc_pr_register}, > + {PR_OUT_REGISTER_AND_MOVE, spc_pr_register_and_move}, > +#endif > + {0, NULL}, > +}; > + > int spc_request_sense(int host_no, struct scsi_cmd *cmd) > { > uint8_t *data; > @@ -1065,4 +1540,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..a731b94 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,6 @@ 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); > > +struct registration *lookup_registration_by_nexus(struct scsi_lu *lu, > + uint64_t nexus_id); > #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..3f4f05c 100644 > --- a/usr/target.c > +++ b/usr/target.c > @@ -490,6 +490,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 +570,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 +610,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/tgtd.h b/usr/tgtd.h > index febaed7..96c744f 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,15 @@ struct mode_pg { > uint8_t mode_data[0]; /* Rest of mode page info */ > }; > > +struct registration { > + uint64_t key; > + uint64_t nexus_id; > + 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 +169,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