[stgt] [PATCH] add persistent reservation support

Mark Harvey markh794 at gmail.com
Tue Nov 17 00:30:31 CET 2009


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 at lab.ntt.co.jp> 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 at lab.ntt.co.jp>
> 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 at lab.ntt.co.jp>
> ---
>  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(&reg->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(&reg->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 at vger.kernel.org
> 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 at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the stgt mailing list