[stgt] [PATCH] persistent reservation support
Mark Harvey
markh794 at gmail.com
Thu Nov 5 11:09:26 CET 2009
Well, initial tests look good.
Basic registration & reservation worked without a hitch.
A simple test shell script...
===============================
#!/bin/bash
KEY1=ABC123
KEY2=BAD123
DEV=`lsscsi -g|awk '/HD100/ {print $7}'`
# Use sg_persist to put SCSI Persistent Reservation thru its paces.
echo -e "\n>>>> Query registered keys - Should report 'no keys'"
sg_persist --no-inquiry -i --read-keys $DEV
echo -e "\n>>>> Registering key $KEY1"
sg_persist -n -o --register --param-sark $KEY1 $DEV
echo -e "\n>>>> Query registered keys - Should report 'key' $KEY1"
sg_persist -n -i --read-keys $DEV
echo -e "\n>>>> Replacing registered key $KEY1 with $KEY2"
sg_persist -n -o --register --param-sark $KEY2 --param-rk $KEY1 $DEV
echo -e "\n>>>> Query registered keys - Should report 'key' $KEY2"
sg_persist -n -i --read-keys $DEV
echo -e "\n>>>> Reserving device using key $KEY2 (Write exclusive)"
sg_persist -n -o --reserve --prout-type=1 --param-sark $KEY2 $DEV
echo -e "\n>>>> Removing registered key 'bad123' "
sg_persist -n -o --register --param-sark 0 --param-rk $KEY2 $DEV
echo -e "\n>>>> Query registered keys - Should report no registered keys"
sg_persist -n -i --read-keys $DEV
echo -e "\n>>>> Query full status - "
sg_persist -n -i --read-full-status $DEV
===============================
Hopefully, tomorrow will be to test Win2k8 cluster validation test and
see what happens :)
Cheers
Mark
FUJITA Tomonori wrote:
> On Tue, 27 Oct 2009 17:41:36 +1100
> Mark Harvey <markh794 at gmail.com> wrote:
>
>
>> I'll be testing over the next few days and will advise of any
>> success/failures.
>>
>
> Thanks a lot!
>
> I fixed some bugs. Can you try the following latest patch?
>
> =
> From: FUJITA Tomonori <fujita.tomonori at lab.ntt.co.jp>
> Subject: [PATCH] persistent reservation support
>
> 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.
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori at lab.ntt.co.jp>
> ---
> usr/sbc.c | 38 +++---
> usr/scc.c | 2 +-
> usr/scsi.c | 40 +++++
> usr/scsi.h | 31 ++++
> usr/smc.c | 2 +-
> usr/spc.c | 491 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> usr/spc.h | 8 +-
> usr/ssc.c | 2 +-
> usr/target.c | 25 +++
> usr/tgtd.h | 21 +++
> 10 files changed, 629 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..e396746 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,480 @@ 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;
> + 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;
> +}
> +
> +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 +1543,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..65d9d91 100644
> --- a/usr/target.c
> +++ b/usr/target.c
> @@ -288,6 +288,8 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
> {
> int i;
> struct it_nexus *itn;
> + struct scsi_lu *lu;
> + struct registration *reg, *n;
>
> dprintf("%d %" PRIu64 "\n", tid, itn_id);
>
> @@ -299,6 +301,19 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
> if (!list_empty(&itn->cmd_hash_list[i]))
> return -EBUSY;
>
> + list_for_each_entry(lu, &itn->nexus_target->device_list,
> + device_siblings) {
> + list_for_each_entry_safe(reg, n, &lu->registration_list,
> + registration_siblings) {
> +
> + if (reg->nexus_id == itn->itn_id) {
> + list_del(®->registration_siblings);
> + free(reg);
> + break;
> + }
> + }
> + }
> +
> it_nexus_del_lu_info(itn);
>
> list_del(&itn->nexus_siblings);
> @@ -490,6 +505,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 +585,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 +625,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.
> */
>
--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the stgt
mailing list