1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/atomic.h>
  26 #include <sys/conf.h>
  27 #include <sys/byteorder.h>
  28 #include <sys/scsi/scsi_types.h>
  29 #include <sys/scsi/generic/persist.h>
  30 
  31 #include <sys/lpif.h>
  32 #include <sys/stmf.h>
  33 #include <sys/stmf_ioctl.h>
  34 #include <sys/portif.h>
  35 #include <sys/stmf_sbd_ioctl.h>
  36 
  37 #include "stmf_sbd.h"
  38 #include "sbd_impl.h"
  39 
  40 #define MAX_PGR_PARAM_LIST_LENGTH       (256 * 1024)
  41 
  42 int  sbd_pgr_reservation_conflict(scsi_task_t *);
  43 void sbd_pgr_reset(sbd_lu_t *);
  44 void sbd_pgr_initialize_it(scsi_task_t *, sbd_it_data_t *);
  45 void sbd_handle_pgr_in_cmd(scsi_task_t *, stmf_data_buf_t *);
  46 void sbd_handle_pgr_out_cmd(scsi_task_t *, stmf_data_buf_t *);
  47 void sbd_handle_pgr_out_data(scsi_task_t *, stmf_data_buf_t *);
  48 void sbd_pgr_keylist_dealloc(sbd_lu_t *);
  49 char *sbd_get_devid_string(sbd_lu_t *);
  50 
  51 sbd_status_t sbd_pgr_meta_init(sbd_lu_t *);
  52 sbd_status_t sbd_pgr_meta_load(sbd_lu_t *);
  53 sbd_status_t sbd_pgr_meta_write(sbd_lu_t *);
  54 static void sbd_swap_pgr_info(sbd_pgr_info_t *);
  55 static void sbd_swap_pgrkey_info(sbd_pgr_key_info_t *);
  56 static void sbd_pgr_key_free(sbd_pgr_key_t *);
  57 static void sbd_pgr_remove_key(sbd_lu_t *, sbd_pgr_key_t *);
  58 static uint32_t sbd_pgr_remove_keys(sbd_lu_t *, sbd_it_data_t *,
  59         sbd_pgr_key_t *, uint64_t, boolean_t);
  60 static boolean_t sbd_pgr_key_compare(sbd_pgr_key_t *, scsi_devid_desc_t *,
  61         stmf_remote_port_t *);
  62 static sbd_pgr_key_t *sbd_pgr_key_alloc(scsi_devid_desc_t *,
  63         scsi_transport_id_t *, int16_t, int16_t);
  64 
  65 static void sbd_pgr_set_pgr_check_flag(sbd_lu_t *, boolean_t);
  66 static void sbd_pgr_set_ua_conditions(sbd_lu_t *, sbd_it_data_t *, uint8_t);
  67 static void sbd_pgr_in_read_keys(scsi_task_t *, stmf_data_buf_t *);
  68 static void sbd_pgr_in_report_capabilities(scsi_task_t *, stmf_data_buf_t *);
  69 static void sbd_pgr_in_read_reservation(scsi_task_t *, stmf_data_buf_t *);
  70 static void sbd_pgr_in_read_full_status(scsi_task_t *, stmf_data_buf_t *);
  71 static void sbd_pgr_out_register(scsi_task_t *, stmf_data_buf_t *);
  72 static void sbd_pgr_out_reserve(scsi_task_t *);
  73 static void sbd_pgr_out_release(scsi_task_t *);
  74 static void sbd_pgr_out_clear(scsi_task_t *);
  75 static void sbd_pgr_out_preempt(scsi_task_t *, stmf_data_buf_t *);
  76 static void sbd_pgr_out_register_and_move(scsi_task_t *, stmf_data_buf_t *);
  77 
  78 static sbd_pgr_key_t *sbd_pgr_do_register(sbd_lu_t *, sbd_it_data_t *,
  79         scsi_devid_desc_t *, stmf_remote_port_t *, uint8_t, uint64_t);
  80 static void sbd_pgr_do_unregister(sbd_lu_t *, sbd_it_data_t *, sbd_pgr_key_t *);
  81 static void sbd_pgr_do_release(sbd_lu_t *, sbd_it_data_t *, uint8_t);
  82 static void sbd_pgr_do_reserve(sbd_pgr_t *, sbd_pgr_key_t *, sbd_it_data_t *it,
  83         stmf_scsi_session_t *, scsi_cdb_prout_t *);
  84 
  85 extern sbd_status_t sbd_write_meta_section(sbd_lu_t *, sm_section_hdr_t *);
  86 extern sbd_status_t sbd_read_meta_section(sbd_lu_t *, sm_section_hdr_t **,
  87         uint16_t);
  88 extern void sbd_swap_section_hdr(sm_section_hdr_t *);
  89 extern void sbd_handle_short_write_transfers(scsi_task_t *task,
  90         stmf_data_buf_t *dbuf, uint32_t cdb_xfer_size);
  91 extern void sbd_handle_short_read_transfers(scsi_task_t *task,
  92         stmf_data_buf_t *dbuf, uint8_t *p, uint32_t cdb_xfer_size,
  93         uint32_t cmd_xfer_size);
  94 extern uint16_t stmf_scsilib_get_lport_rtid(scsi_devid_desc_t *devid);
  95 extern scsi_devid_desc_t *stmf_scsilib_get_devid_desc(uint16_t rtpid);
  96 extern char sbd_ctoi(char c);
  97 
  98 /*
  99  *
 100  *
 101  *   +-----------+
 102  *   |           |sl_it_list
 103  *   |           |---------------------------------------+
 104  *   |           |                                       |
 105  *   |  sbd_lu_t |                                       |
 106  *   |           |                                       |
 107  *   |           |                                       |
 108  *   |           |                                       |
 109  *   +-----+-----+                                       V
 110  *         |                                          +-------+
 111  *         V                                          |       |
 112  *   +-----------+ pgr_key_list               +------>|       |
 113  *   |           |------------+  +-->(NULL)   | +- ---|sbd_it |
 114  *   |           |            |  |            | |     | _data |
 115  *   | sbd_pgr_t |            V  |            | |     |       |
 116  *   |           |          +-------+         | |     +-------+
 117  *   |           |---+      |       |         | |         |
 118  *   |           |   |      |sbd_pgr|---------+ |         v
 119  *   +-----------+   |      | _key_t|<----------+     +-------+
 120  *                   |      |       |                 |       |
 121  *                   |      |       |                 |       |
 122  *                   |      +-------+        +--------|       |
 123  *                   |         |^            |        |       |
 124  *                   |         ||            |        |       |
 125  *                   |         v|            |        +-------+
 126  *                   |      +-------+        |            |
 127  *                   |      |       |        |            v
 128  *                   |      |ALL_TG_|<-------+        +-------+
 129  *                   |      |PT = 1 |<---------+      |       |
 130  *                   |      |       |---+      |      |       |
 131  *                   |      |       |   |      +------|       |
 132  *          (pgr_rsvholder  +-------+   V             |       |
 133  *             pgr_flags&      |^     (NUll)          |       |
 134  *              RSVD_ONE)      ||                     +-------+
 135  *                   |         v|                         |
 136  *                   |      +-------+                     v
 137  *                   |      |       |                 +-------+
 138  *                   |      |  not  |                 |       |
 139  *                   |      |claimed|---+             |       |
 140  *                   |      |       |   |        +----| unreg |
 141  *                   |      |       |   V        |    |       |
 142  *                   |      +-------+ (NUll)     V    |       |
 143  *                   |         |^              (NUll) +-------+
 144  *                   |         ||                         |
 145  *                   |         v|                         v
 146  *                   |      +-------+                 +-------+
 147  *                   |      |       |                 |       |
 148  *                   |      |reserv-|<----------------|       |
 149  *                   +----->|  ation|---------------->|       |
 150  *                          |holder |                 |       |
 151  *                          |key    |                 |       |
 152  *                          +-------+                 +-------+
 153  *                              |^                        |
 154  *                              ||                        v
 155  *                              v|                    +-------+
 156  *                           +-------+                |       |
 157  *                           |       |                |       |
 158  *                           |  not  |---+       +----| unreg |
 159  *                           |claimed|   |       |    |       |
 160  *                           |       |   V       V    |       |
 161  *                           |       | (NUll)  (NUll) +-------+
 162  *                           +-------+                    |
 163  *                              |                         v
 164  *                              v                      (NULL)
 165  *                           (NULL)
 166  *
 167  *
 168  */
 169 
 170 #define PGR_CONFLICT_FREE_CMDS(cdb)     ( \
 171         /* ----------------------- */                                      \
 172         /* SPC-3 (rev 23) Table 31 */                                      \
 173         /* ----------------------- */                                      \
 174         ((cdb[0]) == SCMD_INQUIRY)                                      || \
 175         ((cdb[0]) == SCMD_LOG_SENSE_G1)                                 || \
 176         ((cdb[0]) == SCMD_PERSISTENT_RESERVE_IN)                        || \
 177         ((cdb[0]) == SCMD_REPORT_LUNS)                                  || \
 178         ((cdb[0]) == SCMD_REQUEST_SENSE)                                || \
 179         ((cdb[0]) == SCMD_TEST_UNIT_READY)                              || \
 180         /* PREVENT ALLOW MEDIUM REMOVAL with prevent == 0 */               \
 181         ((((cdb[0]) == SCMD_DOORLOCK) && (((cdb[4]) & 0x3) == 0)))  || \
 182         /* SERVICE ACTION IN with READ MEDIA SERIAL NUMBER (0x01) */       \
 183         (((cdb[0]) == SCMD_SVC_ACTION_IN_G5) && (                          \
 184             ((cdb[1]) & 0x1F) == 0x01))                                     || \
 185         /* MAINTENANCE IN with service actions REPORT ALIASES (0x0Bh) */   \
 186         /* REPORT DEVICE IDENTIFIER (0x05)  REPORT PRIORITY (0x0Eh) */     \
 187         /* REPORT TARGET PORT GROUPS (0x0A) REPORT TIMESTAMP (0x0F) */     \
 188         (((cdb[0]) == SCMD_MAINTENANCE_IN) && (                            \
 189             (((cdb[1]) & 0x1F) == 0x0B) ||                                 \
 190             (((cdb[1]) & 0x1F) == 0x05) ||                                 \
 191             (((cdb[1]) & 0x1F) == 0x0E) ||                                 \
 192             (((cdb[1]) & 0x1F) == 0x0A) ||                                 \
 193             (((cdb[1]) & 0x1F) == 0x0F)))                           || \
 194         /* REGISTER and REGISTER_AND_IGNORE_EXISTING_KEY */                \
 195         /* actions for PERSISTENT RESERVE OUT command */                   \
 196         (((cdb[0]) == SCMD_PERSISTENT_RESERVE_OUT) && (                    \
 197             (((cdb[1]) & 0x1F) == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY) || \
 198             (((cdb[1]) & 0x1F) == PR_OUT_REGISTER)))                        || \
 199         /* ----------------------- */                                      \
 200         /* SBC-3 (rev 17) Table 3  */                                      \
 201         /* ----------------------- */                                      \
 202         /* READ CAPACITY(10) */                                            \
 203         ((cdb[0]) == SCMD_READ_CAPACITY)                                || \
 204         /* READ CAPACITY(16) */                                            \
 205         (((cdb[0]) == SCMD_SVC_ACTION_IN_G4) && (                          \
 206             ((cdb[1]) & 0x1F) == 0x10))                                     || \
 207         /* START STOP UNIT with START bit 0 and POWER CONDITION 0  */      \
 208         (((cdb[0]) == SCMD_START_STOP) && (                                \
 209             (((cdb[4]) & 0xF0) == 0) && (((cdb[4]) & 0x01) == 0))))
 210 /* End of PGR_CONFLICT_FREE_CMDS */
 211 
 212 /* Commands allowed for registered IT nexues but not reservation holder */
 213 #define PGR_REGISTERED_POSSIBLE_CMDS(cdb)       ( \
 214         (((cdb[0]) == SCMD_PERSISTENT_RESERVE_OUT) && (                \
 215             (((cdb[1]) & 0x1F) == PR_OUT_RELEASE)           ||     \
 216             (((cdb[1]) & 0x1F) == PR_OUT_CLEAR)                     ||     \
 217             (((cdb[1]) & 0x1F) == PR_OUT_PREEMPT)           ||     \
 218             (((cdb[1]) & 0x1F) == PR_OUT_PREEMPT_ABORT))))
 219 
 220 /* List of commands allowed when WR_EX type reservation held */
 221 #define PGR_READ_POSSIBLE_CMDS(c)       (  \
 222         ((c) == SCMD_READ)              || \
 223         ((c) == SCMD_READ_G1)           || \
 224         ((c) == SCMD_READ_G4)           || \
 225         ((c) == SCMD_READ_G5)           || \
 226         /* READ FETCH (10) (16) */         \
 227         ((c) == SCMD_READ_POSITION)     || \
 228         ((c) == 0x90)                   || \
 229         /* READ DEFECT DATA */             \
 230         ((c) == SCMD_READ_DEFECT_LIST)  || \
 231         ((c) == 0xB7)                   || \
 232         /* VERIFY (10) (16) (12) */        \
 233         ((c) == SCMD_VERIFY)            || \
 234         ((c) == SCMD_VERIFY_G4)         || \
 235         ((c) == SCMD_VERIFY_G5)         || \
 236         /* XDREAD (10) */                  \
 237         ((c) == 0x52))
 238 
 239 #define PGR_RESERVATION_HOLDER(pgr, key, it)    ( \
 240         ((pgr)->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS) || ( \
 241             ((pgr)->pgr_rsvholder) && ((pgr)->pgr_rsvholder == (key)) && \
 242             ((key)->pgr_key_it) && ((key)->pgr_key_it == (it))))
 243 
 244 #define PGR_SET_FLAG(flg, val)          (atomic_or_8(&(flg), (val)))
 245 #define PGR_CLEAR_FLAG(flg, val)        (atomic_and_8(&(flg), ~(val)))
 246 #define PGR_CLEAR_RSV_FLAG(flg)         (atomic_and_8(&(flg), \
 247         (~(SBD_PGR_RSVD_ALL_REGISTRANTS | SBD_PGR_RSVD_ONE))))
 248 
 249 #define PGR_VALID_SCOPE(scope)  ((scope) == PR_LU_SCOPE)
 250 #define PGR_VALID_TYPE(type)    ( \
 251                                 ((type) == PGR_TYPE_WR_EX)      || \
 252                                 ((type) == PGR_TYPE_EX_AC)      || \
 253                                 ((type) == PGR_TYPE_WR_EX_RO)   || \
 254                                 ((type) == PGR_TYPE_EX_AC_RO)   || \
 255                                 ((type) == PGR_TYPE_WR_EX_AR)   || \
 256                                 ((type) == PGR_TYPE_EX_AC_AR))
 257 
 258 #define ALIGNED_TO_8BYTE_BOUNDARY(i)    (((i) + 7) & ~7)
 259 
 260 static void
 261 sbd_swap_pgr_info(sbd_pgr_info_t *spi)
 262 {
 263         sbd_swap_section_hdr(&spi->pgr_sms_header);
 264         if (spi->pgr_data_order == SMS_DATA_ORDER)
 265                 return;
 266         spi->pgr_sms_header.sms_chksum += SMS_DATA_ORDER - spi->pgr_data_order;
 267         spi->pgr_rsvholder_indx              = BSWAP_32(spi->pgr_rsvholder_indx);
 268         spi->pgr_numkeys             = BSWAP_32(spi->pgr_numkeys);
 269 }
 270 
 271 static void
 272 sbd_swap_pgrkey_info(sbd_pgr_key_info_t *key)
 273 {
 274         key->pgr_key                 = BSWAP_64(key->pgr_key);
 275         key->pgr_key_lpt_len         = BSWAP_16(key->pgr_key_lpt_len);
 276         key->pgr_key_rpt_len         = BSWAP_16(key->pgr_key_rpt_len);
 277 }
 278 
 279 sbd_status_t
 280 sbd_pgr_meta_init(sbd_lu_t *slu)
 281 {
 282         sbd_pgr_info_t  *spi = NULL;
 283         uint32_t        sz;
 284         sbd_status_t    ret;
 285 
 286         sz = sizeof (sbd_pgr_info_t);
 287         spi = (sbd_pgr_info_t *)kmem_zalloc(sz, KM_SLEEP);
 288         spi->pgr_data_order = SMS_DATA_ORDER;
 289         spi->pgr_sms_header.sms_size = sz;
 290         spi->pgr_sms_header.sms_id = SMS_ID_PGR_INFO;
 291         spi->pgr_sms_header.sms_data_order = SMS_DATA_ORDER;
 292 
 293         ret = sbd_write_meta_section(slu, (sm_section_hdr_t *)spi);
 294         kmem_free(spi, sz);
 295         return (ret);
 296 }
 297 
 298 sbd_status_t
 299 sbd_pgr_meta_load(sbd_lu_t *slu)
 300 {
 301         sbd_pgr_t               *pgr = slu->sl_pgr;
 302         sbd_pgr_info_t          *spi = NULL;
 303         sbd_pgr_key_t           *key, *last_key = NULL;
 304         sbd_pgr_key_info_t      *spi_key;
 305         sbd_status_t            ret = SBD_SUCCESS;
 306         scsi_devid_desc_t       *lpt;
 307         uint8_t                 *ptr, *keyoffset,  *endoffset;
 308         uint32_t                i, sz;
 309 
 310         ret = sbd_read_meta_section(slu, (sm_section_hdr_t **)&spi,
 311             SMS_ID_PGR_INFO);
 312         if (ret != SBD_SUCCESS) {
 313                 /* No PGR section found, means volume made before PGR support */
 314                 if (ret == SBD_NOT_FOUND) {
 315                         /* So just create a default PGR section */
 316                         ret = sbd_pgr_meta_init(slu);
 317                 }
 318                 return (ret);
 319         }
 320 
 321         if (spi->pgr_data_order != SMS_DATA_ORDER) {
 322                 sbd_swap_pgr_info(spi);
 323         }
 324 
 325         pgr->pgr_flags = spi->pgr_flags;
 326         if (pgr->pgr_flags & SBD_PGR_APTPL) {
 327                 pgr->pgr_rsv_type = spi->pgr_rsv_type;
 328                 pgr->pgr_rsv_scope = spi->pgr_rsv_scope;
 329         } else {
 330                 PGR_CLEAR_RSV_FLAG(pgr->pgr_flags);
 331         }
 332 
 333         PGR_CLEAR_FLAG(slu->sl_pgr->pgr_flags, SBD_PGR_ALL_KEYS_HAS_IT);
 334 
 335         endoffset       = (uint8_t *)spi;
 336         endoffset       += spi->pgr_sms_header.sms_size;
 337         keyoffset       = (uint8_t *)(spi + 1);
 338         for (i = 1; i <= spi->pgr_numkeys; i++) {
 339 
 340                 spi_key = (sbd_pgr_key_info_t *)keyoffset;
 341                 if (spi->pgr_data_order != SMS_DATA_ORDER) {
 342                         sbd_swap_pgrkey_info(spi_key);
 343                 }
 344 
 345                 /* Calculate the size and next offset */
 346                 sz = ALIGNED_TO_8BYTE_BOUNDARY(sizeof (sbd_pgr_key_info_t) - 1 +
 347                     spi_key->pgr_key_lpt_len + spi_key->pgr_key_rpt_len);
 348                 keyoffset += sz;
 349 
 350                 /* Validate the key fields */
 351                 if (spi_key->pgr_key_rpt_len == 0 || endoffset < keyoffset ||
 352                     (spi_key->pgr_key_lpt_len == 0 &&
 353                     !(spi_key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT))) {
 354                         ret = SBD_META_CORRUPTED;
 355                         goto sbd_pgr_meta_load_failed;
 356                 }
 357 
 358                 lpt = (scsi_devid_desc_t *)spi_key->pgr_key_it;
 359                 ptr = (uint8_t *)spi_key->pgr_key_it + spi_key->pgr_key_lpt_len;
 360 
 361                 if (spi_key->pgr_key_flags & SBD_PGR_KEY_TPT_ID_FLAG) {
 362                         uint16_t tpd_len = 0;
 363 
 364                         if (!stmf_scsilib_tptid_validate(
 365                             (scsi_transport_id_t *)ptr,
 366                             spi_key->pgr_key_rpt_len, &tpd_len)) {
 367                                 ret = SBD_META_CORRUPTED;
 368                                 goto sbd_pgr_meta_load_failed;
 369                         }
 370                         if (tpd_len != spi_key->pgr_key_rpt_len) {
 371                                 ret = SBD_META_CORRUPTED;
 372                                 goto sbd_pgr_meta_load_failed;
 373                         }
 374                         key = sbd_pgr_key_alloc(lpt, (scsi_transport_id_t *)ptr,
 375                             spi_key->pgr_key_lpt_len, spi_key->pgr_key_rpt_len);
 376                 } else {
 377                         stmf_remote_port_t      *rpt = NULL;
 378 
 379                         /*
 380                          * This block is executed only if the metadata was
 381                          * stored before the implementation of Transport ID
 382                          * support.
 383                          */
 384                         rpt = stmf_scsilib_devid_to_remote_port(
 385                             (scsi_devid_desc_t *)ptr);
 386                         if (rpt == NULL) {
 387                                 ret = SBD_META_CORRUPTED;
 388                                 goto sbd_pgr_meta_load_failed;
 389                         }
 390                         key = sbd_pgr_key_alloc(lpt, rpt->rport_tptid,
 391                             spi_key->pgr_key_lpt_len, rpt->rport_tptid_sz);
 392                         stmf_remote_port_free(rpt);
 393                 }
 394 
 395                 key->pgr_key         = spi_key->pgr_key;
 396                 key->pgr_key_flags   = spi_key->pgr_key_flags;
 397                 key->pgr_key_prev    = last_key;
 398 
 399                 if (last_key) {
 400                         last_key->pgr_key_next = key;
 401                 } else {
 402                         pgr->pgr_keylist = key;
 403                 }
 404                 last_key = key;
 405 
 406                 if ((pgr->pgr_flags & SBD_PGR_RSVD_ONE) &&
 407                     (i == spi->pgr_rsvholder_indx)) {
 408                         pgr->pgr_rsvholder = key;
 409                 }
 410         }
 411 
 412         kmem_free(spi, spi->pgr_sms_header.sms_size);
 413         return (ret);
 414 
 415 sbd_pgr_meta_load_failed:
 416         {
 417         char *lun_name = sbd_get_devid_string(slu);
 418         sbd_pgr_keylist_dealloc(slu);
 419         kmem_free(spi, spi->pgr_sms_header.sms_size);
 420         cmn_err(CE_WARN, "sbd_pgr_meta_load: Failed to load PGR meta data "
 421             "for lun %s.", lun_name);
 422         kmem_free(lun_name, strlen(lun_name) + 1);
 423         return (ret);
 424         }
 425 }
 426 
 427 sbd_status_t
 428 sbd_pgr_meta_write(sbd_lu_t *slu)
 429 {
 430         sbd_pgr_key_t           *key;
 431         sbd_pgr_info_t          *spi;
 432         sbd_pgr_key_info_t      *spi_key;
 433         sbd_pgr_t               *pgr = slu->sl_pgr;
 434         sbd_status_t            ret = SBD_SUCCESS;
 435         uint32_t                sz, totalsz;
 436 
 437         /* Calculate total pgr meta section size needed */
 438         sz = sizeof (sbd_pgr_info_t);
 439         if (pgr->pgr_flags & SBD_PGR_APTPL) {
 440                 key = pgr->pgr_keylist;
 441                 while (key != NULL) {
 442                         sz = ALIGNED_TO_8BYTE_BOUNDARY(sz +
 443                             sizeof (sbd_pgr_key_info_t) - 1 +
 444                             key->pgr_key_lpt_len + key->pgr_key_rpt_len);
 445                         key = key->pgr_key_next;
 446                 }
 447         }
 448         totalsz = sz;
 449 
 450         spi = (sbd_pgr_info_t *)kmem_zalloc(totalsz, KM_SLEEP);
 451         spi->pgr_flags               = pgr->pgr_flags;
 452         spi->pgr_rsv_type    = pgr->pgr_rsv_type;
 453         spi->pgr_rsv_scope   = pgr->pgr_rsv_scope;
 454         spi->pgr_data_order  = SMS_DATA_ORDER;
 455         spi->pgr_numkeys     = 0;
 456 
 457         spi->pgr_sms_header.sms_size = totalsz;
 458         spi->pgr_sms_header.sms_id = SMS_ID_PGR_INFO;
 459         spi->pgr_sms_header.sms_data_order = SMS_DATA_ORDER;
 460 
 461         if (pgr->pgr_flags & SBD_PGR_APTPL) {
 462                 uint8_t *ptr;
 463                 key = pgr->pgr_keylist;
 464                 sz = sizeof (sbd_pgr_info_t);
 465                 while (key != NULL) {
 466                         spi_key = (sbd_pgr_key_info_t *)((uint8_t *)spi + sz);
 467                         spi_key->pgr_key = key->pgr_key;
 468                         spi_key->pgr_key_flags = key->pgr_key_flags;
 469                         spi_key->pgr_key_lpt_len = key->pgr_key_lpt_len;
 470                         spi_key->pgr_key_rpt_len = key->pgr_key_rpt_len;
 471                         ptr = spi_key->pgr_key_it;
 472                         bcopy(key->pgr_key_lpt_id, ptr, key->pgr_key_lpt_len);
 473                         ptr += key->pgr_key_lpt_len;
 474                         bcopy(key->pgr_key_rpt_id, ptr, key->pgr_key_rpt_len);
 475 
 476                         spi->pgr_numkeys++;
 477                         if (key == pgr->pgr_rsvholder) {
 478                                 spi->pgr_rsvholder_indx = spi->pgr_numkeys;
 479                         }
 480 
 481                         sz = ALIGNED_TO_8BYTE_BOUNDARY(sz +
 482                             sizeof (sbd_pgr_key_info_t) - 1 +
 483                             key->pgr_key_lpt_len + key->pgr_key_rpt_len);
 484                         key = key->pgr_key_next;
 485                 }
 486         }
 487 
 488         ret = sbd_write_meta_section(slu, (sm_section_hdr_t *)spi);
 489         kmem_free(spi, totalsz);
 490         if (ret != SBD_SUCCESS) {
 491                 sbd_pgr_key_t   *tmp_list;
 492                 tmp_list = pgr->pgr_keylist;
 493                 pgr->pgr_keylist = NULL;
 494                 if (sbd_pgr_meta_load(slu) != SBD_SUCCESS) {
 495                         char *lun_name = sbd_get_devid_string(slu);
 496                         cmn_err(CE_WARN, "sbd_pgr_meta_write: Failed to revert "
 497                             "back to existing PGR state after meta write "
 498                             "failure, may cause PGR inconsistancy for lun %s.",
 499                             lun_name);
 500                         kmem_free(lun_name, strlen(lun_name) + 1);
 501                         pgr->pgr_keylist = tmp_list;
 502                 } else {
 503                         key = pgr->pgr_keylist;
 504                         pgr->pgr_keylist = tmp_list;
 505                         sbd_pgr_set_pgr_check_flag(slu, B_TRUE);
 506                         sbd_pgr_keylist_dealloc(slu);
 507                         pgr->pgr_keylist = key;
 508                 }
 509 
 510         }
 511         return (ret);
 512 }
 513 
 514 static sbd_pgr_key_t *
 515 sbd_pgr_key_alloc(scsi_devid_desc_t *lptid, scsi_transport_id_t *rptid,
 516                                         int16_t lpt_len, int16_t rpt_len)
 517 {
 518         sbd_pgr_key_t *key;
 519 
 520         key = (sbd_pgr_key_t *)kmem_zalloc(sizeof (sbd_pgr_key_t), KM_SLEEP);
 521 
 522         if (lptid && lpt_len >= sizeof (scsi_devid_desc_t)) {
 523                 key->pgr_key_lpt_len = lpt_len;
 524                 key->pgr_key_lpt_id  = (scsi_devid_desc_t *)kmem_zalloc(
 525                     lpt_len, KM_SLEEP);
 526                 bcopy(lptid, key->pgr_key_lpt_id, lpt_len);
 527         }
 528 
 529         if (rptid && rpt_len >= sizeof (scsi_transport_id_t)) {
 530                 key->pgr_key_flags |= SBD_PGR_KEY_TPT_ID_FLAG;
 531                 key->pgr_key_rpt_len = rpt_len;
 532                 key->pgr_key_rpt_id  = (scsi_transport_id_t *)kmem_zalloc(
 533                     rpt_len, KM_SLEEP);
 534                 bcopy(rptid, key->pgr_key_rpt_id, rpt_len);
 535         }
 536 
 537         return (key);
 538 }
 539 
 540 static void
 541 sbd_pgr_key_free(sbd_pgr_key_t *key)
 542 {
 543         if (key->pgr_key_lpt_id) {
 544                 kmem_free(key->pgr_key_lpt_id, key->pgr_key_lpt_len);
 545         }
 546         if (key->pgr_key_rpt_id) {
 547                 kmem_free(key->pgr_key_rpt_id, key->pgr_key_rpt_len);
 548         }
 549         kmem_free(key, sizeof (sbd_pgr_key_t));
 550 }
 551 
 552 void
 553 sbd_pgr_keylist_dealloc(sbd_lu_t *slu)
 554 {
 555         sbd_pgr_t       *pgr  = slu->sl_pgr;
 556         sbd_it_data_t   *it;
 557         sbd_pgr_key_t   *key;
 558 
 559         mutex_enter(&slu->sl_lock);
 560         for (it = slu->sl_it_list; it != NULL; it = it->sbd_it_next) {
 561                 it->pgr_key_ptr = NULL;
 562         }
 563         mutex_exit(&slu->sl_lock);
 564 
 565         while (pgr->pgr_keylist != NULL) {
 566                 key = pgr->pgr_keylist;
 567                 pgr->pgr_keylist = key->pgr_key_next;
 568                 sbd_pgr_key_free(key);
 569         }
 570 }
 571 
 572 /*
 573  * Reset and clear the keys, Can be used in the case of Lun Reset
 574  */
 575 void
 576 sbd_pgr_reset(sbd_lu_t *slu)
 577 {
 578         sbd_pgr_t       *pgr  = slu->sl_pgr;
 579 
 580         rw_enter(&pgr->pgr_lock, RW_WRITER);
 581         if (!(pgr->pgr_flags & SBD_PGR_APTPL)) {
 582                 sbd_pgr_keylist_dealloc(slu);
 583                 pgr->pgr_PRgeneration        = 0;
 584                 pgr->pgr_rsvholder   = NULL;
 585                 pgr->pgr_rsv_type    = 0;
 586                 pgr->pgr_flags               = 0;
 587         }
 588         rw_exit(&pgr->pgr_lock);
 589 }
 590 
 591 static void
 592 sbd_pgr_remove_key(sbd_lu_t *slu, sbd_pgr_key_t *key)
 593 {
 594         sbd_pgr_t *pgr  = slu->sl_pgr;
 595         sbd_it_data_t *it;
 596 
 597         ASSERT(key);
 598 
 599         mutex_enter(&slu->sl_lock);
 600         if (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
 601                 for (it = slu->sl_it_list; it != NULL; it = it->sbd_it_next) {
 602                         if (it->pgr_key_ptr == key)
 603                                 it->pgr_key_ptr = NULL;
 604                 }
 605         } else {
 606                 if (key->pgr_key_it) {
 607                         key->pgr_key_it->pgr_key_ptr = NULL;
 608                 }
 609         }
 610         mutex_exit(&slu->sl_lock);
 611 
 612         if (key->pgr_key_next) {
 613                 key->pgr_key_next->pgr_key_prev = key->pgr_key_prev;
 614         }
 615         if (key->pgr_key_prev) {
 616                 key->pgr_key_prev->pgr_key_next = key->pgr_key_next;
 617         } else {
 618                 pgr->pgr_keylist =  key->pgr_key_next;
 619         }
 620 
 621         sbd_pgr_key_free(key);
 622 }
 623 
 624 /*
 625  * Remove keys depends on boolean variable "match"
 626  * match = B_TRUE  ==>       Remove all keys which matches the given svc_key,
 627  *                      except for IT equal to given "my_it".
 628  * match = B_FALSE ==>       Remove all keys which does not matches the svc_key,
 629  *                      except for IT equal to given "my_it"
 630  */
 631 static uint32_t
 632 sbd_pgr_remove_keys(sbd_lu_t *slu, sbd_it_data_t *my_it, sbd_pgr_key_t *my_key,
 633                                 uint64_t svc_key, boolean_t match)
 634 {
 635         sbd_pgr_t       *pgr  = slu->sl_pgr;
 636         sbd_it_data_t   *it;
 637         sbd_pgr_key_t   *nextkey, *key = pgr->pgr_keylist;
 638         uint32_t        count = 0;
 639 
 640         while (key) {
 641 
 642                 nextkey = key->pgr_key_next;
 643                 if (match == B_TRUE && key->pgr_key == svc_key ||
 644                     match == B_FALSE && key->pgr_key != svc_key) {
 645                         /*
 646                          * If the key is registered by current IT keep it,
 647                          * but just remove pgr pointers from other ITs
 648                          */
 649                         if (key == my_key) {
 650                                 mutex_enter(&slu->sl_lock);
 651                                 for (it = slu->sl_it_list; it != NULL;
 652                                     it = it->sbd_it_next) {
 653                                         if (it->pgr_key_ptr == key &&
 654                                             it != my_it)
 655                                                 it->pgr_key_ptr = NULL;
 656                                 }
 657                                 mutex_exit(&slu->sl_lock);
 658                         } else {
 659                                 sbd_pgr_remove_key(slu, key);
 660                         }
 661                         count++;
 662                 }
 663                 key = nextkey;
 664         }
 665         return (count);
 666 }
 667 
 668 static void
 669 sbd_pgr_set_ua_conditions(sbd_lu_t *slu, sbd_it_data_t *my_it, uint8_t ua)
 670 {
 671         sbd_it_data_t *it;
 672 
 673         mutex_enter(&slu->sl_lock);
 674         for (it = slu->sl_it_list; it != NULL; it = it->sbd_it_next) {
 675                 if (it == my_it)
 676                         continue;
 677                 it->sbd_it_ua_conditions |= ua;
 678         }
 679         mutex_exit(&slu->sl_lock);
 680 }
 681 
 682 /*
 683  * Set the SBD_IT_PGR_CHECK_FLAG  depends on variable "registered". See Below.
 684  *
 685  *   If
 686  *     registered is B_TRUE  => Set PGR_CHECK_FLAG on all registered IT nexus
 687  *     registered is B_FALSE => Set PGR_CHECK_FLAG on all unregistered IT nexus
 688  */
 689 static void
 690 sbd_pgr_set_pgr_check_flag(sbd_lu_t *slu, boolean_t registered)
 691 {
 692         sbd_it_data_t *it;
 693 
 694         PGR_CLEAR_FLAG(slu->sl_pgr->pgr_flags, SBD_PGR_ALL_KEYS_HAS_IT);
 695         mutex_enter(&slu->sl_lock);
 696         for (it = slu->sl_it_list; it != NULL; it = it->sbd_it_next) {
 697                 if (it->pgr_key_ptr) {
 698                         if (registered == B_TRUE)  {
 699                                 it->sbd_it_flags |=  SBD_IT_PGR_CHECK_FLAG;
 700                         }
 701                 } else {
 702                         if (registered == B_FALSE)
 703                                 it->sbd_it_flags |=  SBD_IT_PGR_CHECK_FLAG;
 704                 }
 705         }
 706         mutex_exit(&slu->sl_lock);
 707 }
 708 
 709 static boolean_t
 710 sbd_pgr_key_compare(sbd_pgr_key_t *key, scsi_devid_desc_t *lpt,
 711                                         stmf_remote_port_t *rpt)
 712 {
 713         scsi_devid_desc_t *id;
 714 
 715         if (!stmf_scsilib_tptid_compare(rpt->rport_tptid, key->pgr_key_rpt_id))
 716                         return (B_FALSE);
 717 
 718         /*
 719          * You can skip target port name comparison if ALL_TG_PT flag
 720          * is set for this key;
 721          */
 722         if (!(key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) && lpt) {
 723                 id = key->pgr_key_lpt_id;
 724                 if ((lpt->ident_length != id->ident_length) ||
 725                     (memcmp(id->ident, lpt->ident, id->ident_length) != 0)) {
 726                         return (B_FALSE);
 727                 }
 728         }
 729         return (B_TRUE);
 730 }
 731 
 732 
 733 sbd_pgr_key_t *
 734 sbd_pgr_key_registered(sbd_pgr_t *pgr, scsi_devid_desc_t *lpt,
 735                                         stmf_remote_port_t *rpt)
 736 {
 737         sbd_pgr_key_t *key;
 738 
 739         for (key = pgr->pgr_keylist; key != NULL; key = key->pgr_key_next) {
 740                 if (sbd_pgr_key_compare(key, lpt, rpt) == B_TRUE) {
 741                         return (key);
 742                 }
 743         }
 744         return (NULL);
 745 }
 746 
 747 void
 748 sbd_pgr_initialize_it(scsi_task_t *task, sbd_it_data_t *it)
 749 {
 750         sbd_lu_t *slu = (sbd_lu_t *)task->task_lu->lu_provider_private;
 751         stmf_scsi_session_t     *ses = task->task_session;
 752         sbd_pgr_t               *pgr = slu->sl_pgr;
 753         sbd_pgr_key_t           *key;
 754         scsi_devid_desc_t       *lpt, *id;
 755         stmf_remote_port_t      *rpt;
 756 
 757         if (pgr->pgr_flags & SBD_PGR_ALL_KEYS_HAS_IT)
 758                 return;
 759 
 760         rpt = ses->ss_rport;
 761         lpt = ses->ss_lport->lport_id;
 762 
 763         rw_enter(&pgr->pgr_lock, RW_WRITER);
 764         PGR_SET_FLAG(pgr->pgr_flags,  SBD_PGR_ALL_KEYS_HAS_IT);
 765         for (key = pgr->pgr_keylist; key != NULL; key = key->pgr_key_next) {
 766 
 767                 if ((!(key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT)) &&
 768                     key->pgr_key_it != NULL)
 769                         continue;
 770                 /*
 771                  * SBD_PGR_ALL_KEYS_HAS_IT is set only if no single key
 772                  * in the list has SBD_PGR_KEY_ALL_TG_PT flag set and
 773                  * pgr_key_it all keys points to some IT
 774                  */
 775                 PGR_CLEAR_FLAG(pgr->pgr_flags, SBD_PGR_ALL_KEYS_HAS_IT);
 776 
 777                 /* Check if key matches with given lpt rpt combination */
 778                 if (sbd_pgr_key_compare(key, lpt, rpt) == B_FALSE)
 779                         continue;
 780 
 781                 /* IT nexus devid information matches with this key */
 782                 if (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
 783                         /*
 784                          * If ALL_TG_PT is set, pgr_key_it will point to NULL,
 785                          * unless pgr->pgr_rsvholder pointing to this key.
 786                          * In that case, pgr_key_it should point to the IT
 787                          * which initiated that reservation.
 788                          */
 789                         if (pgr->pgr_rsvholder == key) {
 790                                 id = key->pgr_key_lpt_id;
 791                                 if (lpt->ident_length == id->ident_length) {
 792                                         if (memcmp(id->ident, lpt->ident,
 793                                             id->ident_length) == 0)
 794                                                 key->pgr_key_it = it;
 795                                 }
 796                         }
 797 
 798                 } else {
 799                         key->pgr_key_it = it;
 800                 }
 801 
 802                 mutex_enter(&slu->sl_lock);
 803                 it->pgr_key_ptr = key;
 804                 mutex_exit(&slu->sl_lock);
 805                 rw_exit(&pgr->pgr_lock);
 806                 return;
 807         }
 808         rw_exit(&pgr->pgr_lock);
 809 }
 810 
 811 /*
 812  * Check for any PGR Reservation conflict. return 0 if access allowed
 813  */
 814 int
 815 sbd_pgr_reservation_conflict(scsi_task_t *task)
 816 {
 817         sbd_lu_t        *slu = (sbd_lu_t *)task->task_lu->lu_provider_private;
 818         sbd_pgr_t       *pgr = slu->sl_pgr;
 819         sbd_it_data_t   *it  = (sbd_it_data_t *)task->task_lu_itl_handle;
 820 
 821         /* If Registered */
 822         if (pgr->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS && it->pgr_key_ptr)
 823                         return (0);
 824 
 825         /* If you are registered */
 826         if (pgr->pgr_flags & SBD_PGR_RSVD_ONE) {
 827                 rw_enter(&pgr->pgr_lock, RW_READER);
 828 
 829                 /*
 830                  * Note: it->pgr_key_ptr is protected by sl_lock. Also,
 831                  *    it is expected to change its value only with pgr_lock
 832                  *    held. Hence we are safe to read its value without
 833                  *    grabbing sl_lock. But make sure that the value used is
 834                  *    not from registers by using "volatile" keyword.
 835                  *    Since this funtion is in performance path, we may want
 836                  *    to avoid grabbing sl_lock.
 837                  */
 838                 if ((volatile sbd_pgr_key_t *)it->pgr_key_ptr) {
 839                         /* If you are the reservation holder */
 840                         if (pgr->pgr_rsvholder == it->pgr_key_ptr &&
 841                             it->pgr_key_ptr->pgr_key_it == it) {
 842                                 rw_exit(&pgr->pgr_lock);
 843                                 return (0);
 844                         }
 845 
 846                         /* If reserve type is not EX_AC */
 847                         if (pgr->pgr_rsv_type != PGR_TYPE_EX_AC) {
 848                                 /* If reserve type is WR_EX allow read */
 849                                 if (pgr->pgr_rsv_type == PGR_TYPE_WR_EX) {
 850                                         if (PGR_READ_POSSIBLE_CMDS(
 851                                             task->task_cdb[0])) {
 852                                                 rw_exit(&pgr->pgr_lock);
 853                                                 return (0);
 854                                         }
 855                                 /* For all other reserve types allow access */
 856                                 } else {
 857                                         rw_exit(&pgr->pgr_lock);
 858                                         return (0);
 859                                 }
 860                         }
 861 
 862                         /* If registered, allow these commands */
 863                         if (PGR_REGISTERED_POSSIBLE_CMDS(task->task_cdb)) {
 864                                 rw_exit(&pgr->pgr_lock);
 865                                 return (0);
 866                         }
 867                 }
 868                 rw_exit(&pgr->pgr_lock);
 869         }
 870 
 871         /* For any case, allow these commands */
 872         if (PGR_CONFLICT_FREE_CMDS(task->task_cdb)) {
 873                 return (0);
 874         }
 875 
 876         /* Give read access if reservation type WR_EX for registrants */
 877         if (pgr->pgr_rsv_type == PGR_TYPE_WR_EX_RO ||
 878             pgr->pgr_rsv_type == PGR_TYPE_WR_EX_AR) {
 879                 if (PGR_READ_POSSIBLE_CMDS(task->task_cdb[0]))
 880                         return (0);
 881         }
 882 
 883         /* If  you reached here, No access for you */
 884         return (1);
 885 }
 886 
 887 void
 888 sbd_handle_pgr_in_cmd(scsi_task_t *task, stmf_data_buf_t *initial_dbuf)
 889 {
 890 
 891         sbd_lu_t        *slu = (sbd_lu_t *)task->task_lu->lu_provider_private;
 892         sbd_pgr_t       *pgr = slu->sl_pgr;
 893         scsi_cdb_prin_t *pr_in;
 894 
 895         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_IN);
 896 
 897         pr_in = (scsi_cdb_prin_t *)task->task_cdb;
 898 
 899         rw_enter(&pgr->pgr_lock, RW_READER);
 900         switch (pr_in->action) {
 901         case PR_IN_READ_KEYS:
 902                 sbd_pgr_in_read_keys(task, initial_dbuf);
 903                 break;
 904         case PR_IN_READ_RESERVATION:
 905                 sbd_pgr_in_read_reservation(task, initial_dbuf);
 906                 break;
 907         case PR_IN_REPORT_CAPABILITIES:
 908                 sbd_pgr_in_report_capabilities(task, initial_dbuf);
 909                 break;
 910         case PR_IN_READ_FULL_STATUS:
 911                 sbd_pgr_in_read_full_status(task, initial_dbuf);
 912                 break;
 913         default :
 914                 stmf_scsilib_send_status(task, STATUS_CHECK,
 915                     STMF_SAA_INVALID_FIELD_IN_CDB);
 916                 break;
 917         }
 918         rw_exit(&pgr->pgr_lock);
 919 }
 920 
 921 void
 922 sbd_handle_pgr_out_cmd(scsi_task_t *task, stmf_data_buf_t *initial_dbuf)
 923 {
 924 
 925         scsi_cdb_prout_t *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
 926         uint32_t param_len;
 927 
 928         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_OUT);
 929 
 930         switch (pr_out->action) {
 931                 case PR_OUT_REGISTER:
 932                 case PR_OUT_RESERVE:
 933                 case PR_OUT_RELEASE:
 934                 case PR_OUT_CLEAR:
 935                 case PR_OUT_PREEMPT:
 936                 case PR_OUT_PREEMPT_ABORT:
 937                 case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY:
 938                 case PR_OUT_REGISTER_MOVE:
 939                         param_len = READ_SCSI32(pr_out->param_len, uint32_t);
 940                         if (param_len < MAX_PGR_PARAM_LIST_LENGTH &&
 941                             param_len > 0) {
 942                                 sbd_handle_short_write_transfers(task,
 943                                     initial_dbuf, param_len);
 944                         } else {
 945                                 stmf_scsilib_send_status(task, STATUS_CHECK,
 946                                     STMF_SAA_PARAM_LIST_LENGTH_ERROR);
 947                         }
 948                         break;
 949                 default :
 950                         stmf_scsilib_send_status(task, STATUS_CHECK,
 951                             STMF_SAA_INVALID_FIELD_IN_CDB);
 952                         break;
 953         }
 954 }
 955 
 956 void
 957 sbd_handle_pgr_out_data(scsi_task_t *task, stmf_data_buf_t *dbuf)
 958 {
 959         sbd_lu_t        *slu = (sbd_lu_t *)task->task_lu->lu_provider_private;
 960         scsi_cdb_prout_t        *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
 961         sbd_it_data_t           *it     = task->task_lu_itl_handle;
 962         sbd_pgr_t               *pgr    = slu->sl_pgr;
 963         sbd_pgr_key_t           *key;
 964         scsi_prout_plist_t      *plist;
 965         uint64_t                rsv_key;
 966         uint32_t                buflen;
 967         uint8_t                 *buf;
 968 
 969         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_OUT);
 970 
 971         if (dbuf == NULL || dbuf->db_data_size < 24) {
 972                 stmf_scsilib_send_status(task, STATUS_CHECK,
 973                     STMF_SAA_PARAM_LIST_LENGTH_ERROR);
 974                 return;
 975         }
 976 
 977         buf = dbuf->db_sglist[0].seg_addr;
 978         buflen = dbuf->db_data_size;
 979         plist = (scsi_prout_plist_t *)buf;
 980 
 981         /* SPC3 - 6.12.1 */
 982         if (pr_out->action != PR_OUT_REGISTER_MOVE && buflen != 24) {
 983                 if ((pr_out->action !=
 984                     PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY &&
 985                     pr_out->action != PR_OUT_REGISTER) ||
 986                     plist->spec_i_pt == 0) {
 987                         stmf_scsilib_send_status(task, STATUS_CHECK,
 988                             STMF_SAA_PARAM_LIST_LENGTH_ERROR);
 989                         return;
 990                 }
 991         }
 992 
 993         /*
 994          * Common Reservation Conflict Checks
 995          *
 996          * It is okey to handle REGISTER_MOVE with same plist here,
 997          * because we are only accessing reservation key feild.
 998          */
 999         rw_enter(&pgr->pgr_lock, RW_WRITER);
1000 
1001         /*
1002          * Currently it is not mandatory to have volatile keyword here,
1003          * because, it->pgr_key_ptr is not accessed yet. But still
1004          * keeping it to safe gaurd against any possible future changes.
1005          */
1006         key = (sbd_pgr_key_t *)((volatile sbd_pgr_key_t *)it->pgr_key_ptr);
1007         if (pr_out->action != PR_OUT_REGISTER &&
1008             pr_out->action != PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY) {
1009                 /* if IT is not yet registered send conflict status */
1010                 if (key == NULL) {
1011                         if (pr_out->action == PR_OUT_REGISTER_MOVE &&
1012                             SBD_PGR_RSVD_NONE(pgr)) {
1013                                 stmf_scsilib_send_status(task, STATUS_CHECK,
1014                                     STMF_SAA_INVALID_FIELD_IN_CDB);
1015 
1016                         } else {
1017                                 stmf_scsilib_send_status(task,
1018                                     STATUS_RESERVATION_CONFLICT, 0);
1019                         }
1020                         rw_exit(&pgr->pgr_lock);
1021                         return;
1022                 }
1023 
1024                 /* Given reservation key should matches with registered key */
1025                 rsv_key = READ_SCSI64(plist->reservation_key, uint64_t);
1026                 if (key->pgr_key != rsv_key) {
1027                         stmf_scsilib_send_status(task,
1028                             STATUS_RESERVATION_CONFLICT, 0);
1029                         rw_exit(&pgr->pgr_lock);
1030                         return;
1031                 }
1032         }
1033 
1034         switch (pr_out->action) {
1035         case PR_OUT_REGISTER:
1036         case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY:
1037                 sbd_pgr_out_register(task, dbuf);
1038                 break;
1039         case PR_OUT_REGISTER_MOVE:
1040                 sbd_pgr_out_register_and_move(task, dbuf);
1041                 break;
1042         case PR_OUT_RESERVE:
1043                 sbd_pgr_out_reserve(task);
1044                 break;
1045         case PR_OUT_RELEASE:
1046                 sbd_pgr_out_release(task);
1047                 break;
1048         case PR_OUT_CLEAR:
1049                 sbd_pgr_out_clear(task);
1050                 break;
1051         case PR_OUT_PREEMPT:
1052         case PR_OUT_PREEMPT_ABORT:
1053                 sbd_pgr_out_preempt(task, dbuf);
1054                 break;
1055         default :
1056                 stmf_scsilib_send_status(task, STATUS_CHECK,
1057                     STMF_SAA_INVALID_FIELD_IN_CDB);
1058                 break;
1059         }
1060         rw_exit(&pgr->pgr_lock);
1061 }
1062 
1063 static void
1064 sbd_pgr_in_read_keys(scsi_task_t *task, stmf_data_buf_t *initial_dbuf)
1065 {
1066         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1067         sbd_pgr_t       *pgr   =  slu->sl_pgr;
1068         sbd_pgr_key_t   *key;
1069         scsi_prin_readrsrv_t *buf;
1070         uint32_t buf_size, cdb_len, numkeys = 0;
1071         uint64_t *reg_key;
1072 
1073         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_IN);
1074 
1075         cdb_len = READ_SCSI16(&task->task_cdb[7], uint16_t);
1076         for (key = pgr->pgr_keylist; key != NULL; key = key->pgr_key_next)
1077                 ++numkeys;
1078         buf_size = 8 + numkeys * 8; /* minimum 8 bytes */
1079         buf = kmem_zalloc(buf_size, KM_SLEEP);
1080         SCSI_WRITE32(buf->PRgeneration, pgr->pgr_PRgeneration);
1081         SCSI_WRITE32(buf->add_len, numkeys * 8);
1082 
1083         reg_key = (uint64_t *)&buf->key_list;
1084         for (key = pgr->pgr_keylist; key != NULL; key = key->pgr_key_next) {
1085                 SCSI_WRITE64(reg_key, key->pgr_key);
1086                 reg_key++;
1087         }
1088         sbd_handle_short_read_transfers(task, initial_dbuf, (uint8_t *)buf,
1089             cdb_len, buf_size);
1090         kmem_free(buf, buf_size);
1091 }
1092 
1093 static void
1094 sbd_pgr_in_read_reservation(scsi_task_t *task, stmf_data_buf_t *initial_dbuf)
1095 {
1096         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1097         sbd_pgr_t       *pgr   =  slu->sl_pgr;
1098         scsi_prin_readrsrv_t *buf;
1099         uint32_t cdb_len, buf_len, buf_size = 24;
1100 
1101         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_IN);
1102 
1103         cdb_len = READ_SCSI16(&task->task_cdb[7], uint16_t);
1104         buf = kmem_zalloc(buf_size, KM_SLEEP); /* fixed size cdb, 24 bytes */
1105         SCSI_WRITE32(buf->PRgeneration, pgr->pgr_PRgeneration);
1106 
1107         if (SBD_PGR_RSVD_NONE(pgr)) {
1108                 SCSI_WRITE32(buf->add_len, 0);
1109                 buf_len = 8;
1110         } else {
1111                 if (pgr->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS) {
1112                         SCSI_WRITE64(
1113                             buf->key_list.res_key_list[0].reservation_key, 0);
1114                 } else {
1115                         SCSI_WRITE64(
1116                             buf->key_list.res_key_list[0].reservation_key,
1117                             pgr->pgr_rsvholder->pgr_key);
1118                 }
1119                 buf->key_list.res_key_list[0].type = pgr->pgr_rsv_type;
1120                 buf->key_list.res_key_list[0].scope = pgr->pgr_rsv_scope;
1121                 SCSI_WRITE32(buf->add_len, 16);
1122                 buf_len = 24;
1123         }
1124 
1125         sbd_handle_short_read_transfers(task, initial_dbuf, (uint8_t *)buf,
1126             cdb_len, buf_len);
1127         kmem_free(buf, buf_size);
1128 }
1129 
1130 static void
1131 sbd_pgr_in_report_capabilities(scsi_task_t *task,
1132                                 stmf_data_buf_t *initial_dbuf)
1133 {
1134         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1135         sbd_pgr_t       *pgr   =  slu->sl_pgr;
1136         scsi_prin_rpt_cap_t buf;
1137         uint32_t cdb_len;
1138 
1139         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_IN);
1140         ASSERT(pgr != NULL);
1141 
1142         bzero(&buf, sizeof (buf));
1143         buf.ptpl_c              = 1;   /* Persist Through Power Loss C */
1144         buf.atp_c               = 1;   /* All Target Ports Capable */
1145         buf.sip_c               = 1;   /* Specify Initiator Ports Capable */
1146         buf.crh                 = 0;   /* Supports Reserve/Release exception */
1147         buf.tmv                 = 1;   /* Type Mask Valid */
1148         buf.pr_type.wr_ex       = 1;   /* Write Exclusve */
1149         buf.pr_type.ex_ac       = 1;   /* Exclusive Access */
1150         buf.pr_type.wr_ex_ro    = 1;   /* Write Exclusive Registrants Only */
1151         buf.pr_type.ex_ac_ro    = 1;   /* Exclusive Access Registrants Only */
1152         buf.pr_type.wr_ex_ar    = 1;   /* Write Exclusive All Registrants */
1153         buf.pr_type.ex_ac_ar    = 1;   /* Exclusive Access All Registrants */
1154 
1155         /* Persist Though Power Loss Active */
1156         buf.ptpl_a = pgr->pgr_flags & SBD_PGR_APTPL;
1157         SCSI_WRITE16(&buf.length, 8);
1158         cdb_len = READ_SCSI16(&task->task_cdb[7], uint16_t);
1159         sbd_handle_short_read_transfers(task, initial_dbuf, (uint8_t *)&buf,
1160             cdb_len, 8);
1161 }
1162 
1163 /* Minimum required size, SPC3 rev23 Table 110 */
1164 #define PGR_IN_READ_FULL_STATUS_MINBUFSZ        8
1165 /* Full Satus Descriptor Fromat size, SPC3 rev23 Table 111 */
1166 #define PGR_IN_READ_FULL_STATUS_DESCFMTSZ       24
1167 
1168 static void
1169 sbd_pgr_in_read_full_status(scsi_task_t *task,
1170                                 stmf_data_buf_t *initial_dbuf)
1171 {
1172         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1173         sbd_pgr_t       *pgr   = slu->sl_pgr;
1174         sbd_pgr_key_t   *key;
1175         scsi_prin_status_t      *sts;
1176         scsi_prin_full_status_t *buf;
1177         uint32_t                i, buf_size, cdb_len;
1178         uint8_t                 *offset;
1179 
1180         ASSERT(task->task_cdb[0] == SCMD_PERSISTENT_RESERVE_IN);
1181         ASSERT(pgr != NULL);
1182 
1183         /* 4 byte allocation length for CDB, SPC3 rev23, Table 101 */
1184         cdb_len = READ_SCSI16(&task->task_cdb[7], uint16_t);
1185 
1186         /* PRgeneration and additional length fields */
1187         buf_size = PGR_IN_READ_FULL_STATUS_MINBUFSZ;
1188         for (key = pgr->pgr_keylist; key != NULL; key = key->pgr_key_next) {
1189                 buf_size  = buf_size + PGR_IN_READ_FULL_STATUS_DESCFMTSZ +
1190                     key->pgr_key_rpt_len;
1191         }
1192 
1193         buf = kmem_zalloc(buf_size, KM_SLEEP);
1194         SCSI_WRITE32(buf->PRgeneration, pgr->pgr_PRgeneration);
1195         SCSI_WRITE32(buf->add_len, buf_size - PGR_IN_READ_FULL_STATUS_MINBUFSZ);
1196 
1197         offset  = (uint8_t *)&buf->full_desc[0];
1198         key     = pgr->pgr_keylist;
1199         i       = 0;
1200         while (key) {
1201                 sts = (scsi_prin_status_t *)offset;
1202                 SCSI_WRITE64(sts->reservation_key, key->pgr_key);
1203                 if ((pgr->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS) ||
1204                     (pgr->pgr_rsvholder && pgr->pgr_rsvholder == key)) {
1205                                 sts->r_holder        = 1;
1206                                 sts->type    = pgr->pgr_rsv_type;
1207                                 sts->scope   = pgr->pgr_rsv_scope;
1208                 }
1209 
1210                 if (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
1211                         sts->all_tg_pt = 1;
1212                 } else {
1213                         SCSI_WRITE16(sts->rel_tgt_port_id,
1214                             stmf_scsilib_get_lport_rtid(key->pgr_key_lpt_id));
1215                 }
1216                 SCSI_WRITE32(sts->add_len, key->pgr_key_rpt_len);
1217                 offset += PGR_IN_READ_FULL_STATUS_DESCFMTSZ;
1218                 (void) memcpy(offset, key->pgr_key_rpt_id,
1219                     key->pgr_key_rpt_len);
1220                 offset += key->pgr_key_rpt_len;
1221                 key = key->pgr_key_next;
1222                 ++i;
1223         }
1224         ASSERT(offset <= (uint8_t *)buf + buf_size);
1225 
1226         sbd_handle_short_read_transfers(task, initial_dbuf, (uint8_t *)buf,
1227             cdb_len, buf_size);
1228         kmem_free(buf, buf_size);
1229 }
1230 
1231 static void
1232 sbd_pgr_out_register(scsi_task_t *task, stmf_data_buf_t *dbuf)
1233 {
1234         sbd_lu_t        *slu = (sbd_lu_t *)task->task_lu->lu_provider_private;
1235         sbd_pgr_t               *pgr    = slu->sl_pgr;
1236         stmf_scsi_session_t     *ses    = task->task_session;
1237         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1238         sbd_pgr_key_t           *key    = it->pgr_key_ptr;
1239         scsi_cdb_prout_t        *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
1240         scsi_devid_desc_t       *lpt    = ses->ss_lport->lport_id;
1241         scsi_prout_plist_t      *plist;
1242         stmf_remote_port_t      rport;
1243         uint8_t                 *buf, keyflag;
1244         uint32_t                buflen;
1245         uint64_t                rsv_key, svc_key;
1246 
1247         buf = dbuf->db_sglist[0].seg_addr;
1248         plist = (scsi_prout_plist_t *)buf;
1249         buflen = dbuf->db_data_size;
1250         rsv_key = READ_SCSI64(plist->reservation_key, uint64_t);
1251         svc_key = READ_SCSI64(plist->service_key, uint64_t);
1252 
1253         /* Handling already registered IT session */
1254         if (key) {
1255 
1256                 if (pr_out->action == PR_OUT_REGISTER &&
1257                     key->pgr_key != rsv_key) {
1258                         stmf_scsilib_send_status(task,
1259                             STATUS_RESERVATION_CONFLICT, 0);
1260                         return;
1261                 }
1262                 if (plist->spec_i_pt) {
1263                         stmf_scsilib_send_status(task, STATUS_CHECK,
1264                             STMF_SAA_INVALID_FIELD_IN_CDB);
1265                         return;
1266                 }
1267 
1268                 if (plist->all_tg_pt !=
1269                     (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT)) {
1270                         stmf_scsilib_send_status(task, STATUS_CHECK,
1271                             STMF_SAA_INVALID_FIELD_IN_CDB);
1272                         return;
1273                 }
1274 
1275                 if (svc_key == 0) {
1276                         sbd_pgr_do_unregister(slu, it, key);
1277                 } else {
1278                         key->pgr_key = svc_key;
1279                 }
1280 
1281                 goto sbd_pgr_reg_done;
1282         }
1283 
1284         /* Handling unregistered IT session */
1285         if (pr_out->action == PR_OUT_REGISTER && rsv_key != 0) {
1286                 stmf_scsilib_send_status(task, STATUS_RESERVATION_CONFLICT, 0);
1287                 return;
1288         }
1289 
1290         if (svc_key == 0) {
1291                 /* Do we need to consider aptpl here? I don't think so */
1292                 pgr->pgr_PRgeneration++;
1293                 stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1294                 return;
1295         }
1296 
1297         keyflag = SBD_PGR_KEY_TPT_ID_FLAG;
1298         if (plist->all_tg_pt) {
1299                 keyflag |= SBD_PGR_KEY_ALL_TG_PT;
1300                 lpt = NULL;
1301         }
1302 
1303         if (plist->spec_i_pt) {
1304                 uint32_t max_tpdnum, tpdnum, i, adn_len = 0;
1305                 uint16_t tpd_sz = 0;
1306                 uint8_t *adn_dat;
1307                 scsi_transport_id_t *tpd;
1308                 stmf_remote_port_t *rpt_ary;
1309 
1310                 if (pr_out->action == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY) {
1311                         stmf_scsilib_send_status(task, STATUS_CHECK,
1312                             STMF_SAA_INVALID_FIELD_IN_CDB);
1313                         return;
1314                 }
1315 
1316                 /* Length validation SPC3 rev23 Section 6.12.3 and Table 115 */
1317                 if (buflen >= sizeof (scsi_prout_plist_t) - 1 +
1318                     sizeof (uint32_t))
1319                         adn_len = READ_SCSI32(plist->apd, uint32_t);
1320                 /* SPC3 rev23, adn_len should be multiple of 4 */
1321                 if (adn_len % 4 != 0 ||
1322                     adn_len < sizeof (scsi_transport_id_t) +
1323                     sizeof (uint32_t) ||
1324                     buflen < sizeof (scsi_prout_plist_t) - 1 + adn_len) {
1325                         stmf_scsilib_send_status(task, STATUS_CHECK,
1326                             STMF_SAA_PARAM_LIST_LENGTH_ERROR);
1327                         return;
1328                 }
1329 
1330                 tpdnum = 0;
1331                 adn_dat = plist->apd + sizeof (uint32_t);
1332                 max_tpdnum = adn_len / sizeof (scsi_transport_id_t);
1333                 rpt_ary = (stmf_remote_port_t *)kmem_zalloc(
1334                     sizeof (stmf_remote_port_t) * max_tpdnum, KM_SLEEP);
1335 
1336                 /* Check the validity of given TransportIDs */
1337                 while (adn_len != 0) {
1338                         if (!stmf_scsilib_tptid_validate(
1339                             (scsi_transport_id_t *)adn_dat, adn_len, &tpd_sz))
1340                                 break;
1341                         /* SPC3 rev23, tpd_sz should be multiple of 4 */
1342                         if (tpd_sz == 0 || tpd_sz % 4 != 0)
1343                                 break;
1344                         tpd = (scsi_transport_id_t *)adn_dat;
1345 
1346                         /* make sure that there is no duplicates */
1347                         for (i = 0; i < tpdnum; i++) {
1348                                 if (stmf_scsilib_tptid_compare(
1349                                     rpt_ary[i].rport_tptid, tpd))
1350                                         break;
1351                         }
1352                         if (i < tpdnum)
1353                                 break;
1354 
1355                         rpt_ary[tpdnum].rport_tptid = tpd;
1356                         rpt_ary[tpdnum].rport_tptid_sz = tpd_sz;
1357 
1358                         /* Check if the given IT nexus is already registered */
1359                         if (sbd_pgr_key_registered(pgr, lpt, &rpt_ary[tpdnum]))
1360                                 break;
1361 
1362                         adn_len -= tpd_sz;
1363                         adn_dat += tpd_sz;
1364                         tpdnum++;
1365                 }
1366 
1367                 if (adn_len != 0) {
1368                         kmem_free(rpt_ary,
1369                             sizeof (stmf_remote_port_t) * max_tpdnum);
1370                         stmf_scsilib_send_status(task, STATUS_CHECK,
1371                             STMF_SAA_INVALID_FIELD_IN_CDB);
1372                         return;
1373                 }
1374 
1375                 for (i = 0; i < tpdnum; i++) {
1376                         (void) sbd_pgr_do_register(slu, NULL, lpt, &rpt_ary[i],
1377                             keyflag, svc_key);
1378                 }
1379                 kmem_free(rpt_ary, sizeof (stmf_remote_port_t) * max_tpdnum);
1380         }
1381 
1382         rport.rport_tptid = ses->ss_rport->rport_tptid;
1383         rport.rport_tptid_sz = ses->ss_rport->rport_tptid_sz;
1384 
1385         (void) sbd_pgr_do_register(slu, it, lpt, &rport, keyflag, svc_key);
1386 
1387 sbd_pgr_reg_done:
1388 
1389         if (pgr->pgr_flags & SBD_PGR_APTPL || plist->aptpl) {
1390                 if (plist->aptpl)
1391                         PGR_SET_FLAG(pgr->pgr_flags, SBD_PGR_APTPL);
1392                 else
1393                         PGR_CLEAR_FLAG(pgr->pgr_flags, SBD_PGR_APTPL);
1394 
1395                 if (sbd_pgr_meta_write(slu) != SBD_SUCCESS) {
1396                         stmf_scsilib_send_status(task, STATUS_CHECK,
1397                             STMF_SAA_INSUFFICIENT_REG_RESOURCES);
1398                         return;
1399                 }
1400         }
1401 
1402         pgr->pgr_PRgeneration++;
1403         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1404 }
1405 
1406 static sbd_pgr_key_t *
1407 sbd_pgr_do_register(sbd_lu_t *slu, sbd_it_data_t *it, scsi_devid_desc_t *lpt,
1408                 stmf_remote_port_t *rpt, uint8_t keyflag, uint64_t svc_key)
1409 {
1410         sbd_pgr_t               *pgr = slu->sl_pgr;
1411         sbd_pgr_key_t           *key;
1412         uint16_t                lpt_len = 0;
1413 
1414         if (lpt)
1415                 lpt_len = sizeof (scsi_devid_desc_t) + lpt->ident_length;
1416 
1417         key = sbd_pgr_key_alloc(lpt, rpt->rport_tptid,
1418             lpt_len, rpt->rport_tptid_sz);
1419         key->pgr_key = svc_key;
1420         key->pgr_key_flags |= keyflag;
1421 
1422         if (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
1423                 /* set PGR_CHECK flag for all unregistered IT nexus */
1424                 sbd_pgr_set_pgr_check_flag(slu, B_FALSE);
1425         } else {
1426                 key->pgr_key_it = it;
1427         }
1428 
1429         if (it) {
1430                 mutex_enter(&slu->sl_lock);
1431                 it->pgr_key_ptr = key;
1432                 mutex_exit(&slu->sl_lock);
1433         } else {
1434                 sbd_pgr_set_pgr_check_flag(slu, B_FALSE);
1435         }
1436 
1437         key->pgr_key_next = pgr->pgr_keylist;
1438         if (pgr->pgr_keylist) {
1439                 pgr->pgr_keylist->pgr_key_prev = key;
1440         }
1441         pgr->pgr_keylist = key;
1442 
1443         return (key);
1444 }
1445 
1446 static void
1447 sbd_pgr_do_unregister(sbd_lu_t *slu, sbd_it_data_t *it, sbd_pgr_key_t *key)
1448 {
1449         if (slu->sl_pgr->pgr_rsvholder == key) {
1450                 sbd_pgr_do_release(slu, it, SBD_UA_RESERVATIONS_RELEASED);
1451         }
1452 
1453         sbd_pgr_remove_key(slu, key);
1454         if (slu->sl_pgr->pgr_keylist == NULL) {
1455                 PGR_CLEAR_RSV_FLAG(slu->sl_pgr->pgr_flags);
1456         }
1457 }
1458 
1459 static void
1460 sbd_pgr_out_reserve(scsi_task_t *task)
1461 {
1462         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1463         stmf_scsi_session_t     *ses    = task->task_session;
1464         scsi_cdb_prout_t        *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
1465         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1466         sbd_pgr_t               *pgr    = slu->sl_pgr;
1467         sbd_pgr_key_t           *key    = it->pgr_key_ptr;
1468 
1469         ASSERT(key);
1470 
1471         if (!(PGR_VALID_SCOPE(pr_out->scope) && PGR_VALID_TYPE(pr_out->type))) {
1472                 stmf_scsilib_send_status(task, STATUS_CHECK,
1473                     STMF_SAA_INVALID_FIELD_IN_CDB);
1474                 return;
1475         }
1476 
1477         if (SBD_PGR_RSVD(pgr)) {
1478                 if (PGR_RESERVATION_HOLDER(pgr, key, it)) {
1479                         if (pgr->pgr_rsv_type != pr_out->type ||
1480                             pgr->pgr_rsv_scope != pr_out->scope) {
1481                                 stmf_scsilib_send_status(task,
1482                                     STATUS_RESERVATION_CONFLICT, 0);
1483                                 return;
1484                         }
1485                 } else {
1486                         stmf_scsilib_send_status(task,
1487                             STATUS_RESERVATION_CONFLICT, 0);
1488                         return;
1489 
1490                 }
1491         /* In case there is no reservation exist */
1492         } else {
1493                 sbd_pgr_do_reserve(pgr, key, it, ses, pr_out);
1494                 if (pgr->pgr_flags & SBD_PGR_APTPL) {
1495                         if (sbd_pgr_meta_write(slu) != SBD_SUCCESS) {
1496                                 stmf_scsilib_send_status(task, STATUS_CHECK,
1497                                     STMF_SAA_INSUFFICIENT_REG_RESOURCES);
1498                                 return;
1499                         }
1500                 }
1501         }
1502 
1503         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1504 }
1505 
1506 static void
1507 sbd_pgr_do_reserve(sbd_pgr_t *pgr, sbd_pgr_key_t *key, sbd_it_data_t *it,
1508                         stmf_scsi_session_t *ses, scsi_cdb_prout_t *pr_out)
1509 {
1510         scsi_devid_desc_t       *lpt;
1511         uint16_t                lpt_len;
1512 
1513         pgr->pgr_rsv_type = pr_out->type;
1514         pgr->pgr_rsv_scope = pr_out->scope;
1515         if (pr_out->type == PGR_TYPE_WR_EX_AR ||
1516             pr_out->type == PGR_TYPE_EX_AC_AR) {
1517                 PGR_SET_FLAG(pgr->pgr_flags, SBD_PGR_RSVD_ALL_REGISTRANTS);
1518         } else {
1519                 if (key->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
1520                         lpt = key->pgr_key_lpt_id;
1521                         lpt_len = key->pgr_key_lpt_len;
1522                         if (lpt_len > 0 && lpt != NULL) {
1523                                 kmem_free(lpt, lpt_len);
1524                         }
1525                         lpt = ses->ss_lport->lport_id;
1526                         lpt_len = sizeof (scsi_devid_desc_t) +
1527                             lpt->ident_length;
1528                         key->pgr_key_lpt_len = lpt_len;
1529                         key->pgr_key_lpt_id = (scsi_devid_desc_t *)
1530                             kmem_zalloc(lpt_len, KM_SLEEP);
1531                         bcopy(lpt, key->pgr_key_lpt_id, lpt_len);
1532                         key->pgr_key_it = it;
1533                 }
1534 
1535                 PGR_SET_FLAG(pgr->pgr_flags, SBD_PGR_RSVD_ONE);
1536                 pgr->pgr_rsvholder = key;
1537         }
1538 }
1539 
1540 static void
1541 sbd_pgr_out_release(scsi_task_t *task)
1542 {
1543         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1544         scsi_cdb_prout_t        *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
1545         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1546         sbd_pgr_t               *pgr    = slu->sl_pgr;
1547         sbd_pgr_key_t           *key    = it->pgr_key_ptr;
1548 
1549         ASSERT(key);
1550 
1551         if (SBD_PGR_RSVD(pgr)) {
1552                 if (pgr->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS ||
1553                     pgr->pgr_rsvholder == key) {
1554                         if (pgr->pgr_rsv_type != pr_out->type ||
1555                             pgr->pgr_rsv_scope != pr_out->scope) {
1556                                 stmf_scsilib_send_status(task, STATUS_CHECK,
1557                                     STMF_SAA_INVALID_RELEASE_OF_PR);
1558                                 return;
1559                         }
1560                         sbd_pgr_do_release(slu, it,
1561                             SBD_UA_RESERVATIONS_RELEASED);
1562                 }
1563         }
1564         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1565 }
1566 
1567 static void
1568 sbd_pgr_do_release(sbd_lu_t *slu, sbd_it_data_t *it, uint8_t ua_condition)
1569 {
1570 
1571         sbd_pgr_t *pgr    =  slu->sl_pgr;
1572 
1573         /* Reset pgr_flags */
1574         PGR_CLEAR_RSV_FLAG(pgr->pgr_flags);
1575         pgr->pgr_rsvholder = NULL;
1576 
1577         /* set unit attention condition if necessary */
1578         if (pgr->pgr_rsv_type != PGR_TYPE_WR_EX &&
1579             pgr->pgr_rsv_type != PGR_TYPE_EX_AC) {
1580                 sbd_pgr_set_ua_conditions(slu, it, ua_condition);
1581         }
1582         pgr->pgr_rsv_type = 0;
1583 }
1584 
1585 static void
1586 sbd_pgr_out_clear(scsi_task_t *task)
1587 {
1588         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1589         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1590         sbd_pgr_t               *pgr    = slu->sl_pgr;
1591 
1592         ASSERT(it->pgr_key_ptr);
1593 
1594         PGR_CLEAR_RSV_FLAG(pgr->pgr_flags);
1595         pgr->pgr_rsvholder = NULL;
1596         pgr->pgr_rsv_type = 0;
1597         mutex_enter(&slu->sl_lock);
1598         /* Remove all pointers from IT to pgr keys */
1599         for (it = slu->sl_it_list; it != NULL; it = it->sbd_it_next) {
1600                 it->pgr_key_ptr = NULL;
1601         }
1602         mutex_exit(&slu->sl_lock);
1603         sbd_pgr_keylist_dealloc(slu);
1604         sbd_pgr_set_ua_conditions(slu, it, SBD_UA_RESERVATIONS_PREEMPTED);
1605         if (pgr->pgr_flags & SBD_PGR_APTPL) {
1606                 if (sbd_pgr_meta_write(slu) != SBD_SUCCESS) {
1607                         stmf_scsilib_send_status(task, STATUS_CHECK,
1608                             STMF_SAA_INSUFFICIENT_REG_RESOURCES);
1609                         return;
1610                 }
1611         }
1612         pgr->pgr_PRgeneration++;
1613         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1614 }
1615 
1616 static void
1617 sbd_pgr_out_preempt(scsi_task_t *task, stmf_data_buf_t *dbuf)
1618 {
1619         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1620         stmf_scsi_session_t     *ses    = task->task_session;
1621         scsi_cdb_prout_t        *pr_out = (scsi_cdb_prout_t *)task->task_cdb;
1622         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1623         sbd_pgr_t               *pgr    = slu->sl_pgr;
1624         sbd_pgr_key_t           *key    = it->pgr_key_ptr;
1625         scsi_prout_plist_t      *plist;
1626         uint8_t                 *buf, change_rsv = 0;
1627         uint64_t                svc_key;
1628 
1629         ASSERT(key);
1630 
1631         buf = dbuf->db_sglist[0].seg_addr;
1632         plist = (scsi_prout_plist_t *)buf;
1633         svc_key = READ_SCSI64(plist->service_key, uint64_t);
1634 
1635         if (SBD_PGR_RSVD_NONE(pgr)) {
1636                 if (svc_key == 0 ||
1637                     sbd_pgr_remove_keys(slu, it, key, svc_key, B_TRUE) == 0) {
1638                         stmf_scsilib_send_status(task,
1639                             STATUS_RESERVATION_CONFLICT, 0);
1640                         return;
1641                 }
1642 
1643         } else if (pgr->pgr_flags & SBD_PGR_RSVD_ONE) {
1644                 if (svc_key == 0) {
1645                         stmf_scsilib_send_status(task, STATUS_CHECK,
1646                             STMF_SAA_INVALID_FIELD_IN_CDB);
1647                         return;
1648                 }
1649 
1650                 /* Validity check of scope and type */
1651                 if (pgr->pgr_rsvholder->pgr_key == svc_key) {
1652                         if (!(PGR_VALID_SCOPE(pr_out->scope) &&
1653                             PGR_VALID_TYPE(pr_out->type))) {
1654                                 stmf_scsilib_send_status(task, STATUS_CHECK,
1655                                     STMF_SAA_INVALID_FIELD_IN_CDB);
1656                                 return;
1657                         }
1658                 }
1659 
1660                 if (pgr->pgr_rsvholder != key &&
1661                     pgr->pgr_rsvholder->pgr_key == svc_key) {
1662                         sbd_pgr_do_release(slu, it,
1663                             SBD_UA_REGISTRATIONS_PREEMPTED);
1664                         change_rsv = 1;
1665                 }
1666 
1667                 if (pgr->pgr_rsvholder == key &&
1668                     pgr->pgr_rsvholder->pgr_key == svc_key) {
1669                         if (pr_out->scope != pgr->pgr_rsv_scope ||
1670                             pr_out->type != pgr->pgr_rsv_type) {
1671                                 sbd_pgr_do_release(slu, it,
1672                                     SBD_UA_REGISTRATIONS_PREEMPTED);
1673                                 change_rsv = 1;
1674                         }
1675                 } else {
1676                         /*
1677                          * Remove matched keys in all cases, except when the
1678                          * current IT nexus holds the reservation and the given
1679                          * svc_key matches with registered key.
1680                          * Note that, if the reservation is held by another
1681                          * IT nexus, and svc_key matches registered key for
1682                          * that IT nexus, sbd_pgr_remove_key() is not expected
1683                          * return 0. Hence, returning check condition after
1684                          * releasing the reservation does not arise.
1685                          */
1686                         if (sbd_pgr_remove_keys(slu, it, key, svc_key, B_TRUE)
1687                             == 0) {
1688                                 stmf_scsilib_send_status(task,
1689                                     STATUS_RESERVATION_CONFLICT, 0);
1690                                 return;
1691                         }
1692                 }
1693 
1694                 if (change_rsv) {
1695                         sbd_pgr_do_reserve(pgr, key, it, ses, pr_out);
1696                 }
1697 
1698         } else if (pgr->pgr_flags & SBD_PGR_RSVD_ALL_REGISTRANTS) {
1699                 if (svc_key == 0) {
1700                         if (!(PGR_VALID_SCOPE(pr_out->scope) &&
1701                             PGR_VALID_TYPE(pr_out->type))) {
1702                                 stmf_scsilib_send_status(task, STATUS_CHECK,
1703                                     STMF_SAA_INVALID_FIELD_IN_CDB);
1704                                 return;
1705                         }
1706                         sbd_pgr_do_release(slu, it,
1707                             SBD_UA_REGISTRATIONS_PREEMPTED);
1708                         (void) sbd_pgr_remove_keys(slu, it, key, 0, B_FALSE);
1709                         sbd_pgr_do_reserve(pgr, key, it, ses, pr_out);
1710                 } else {
1711                         if (sbd_pgr_remove_keys(slu, it, key, svc_key, B_TRUE)
1712                             == 0) {
1713                                 stmf_scsilib_send_status(task,
1714                                     STATUS_RESERVATION_CONFLICT, 0);
1715                                 return;
1716                         }
1717                 }
1718         }
1719 
1720         if (pgr->pgr_flags & SBD_PGR_APTPL) {
1721                 if (sbd_pgr_meta_write(slu) != SBD_SUCCESS) {
1722                         stmf_scsilib_send_status(task, STATUS_CHECK,
1723                             STMF_SAA_INSUFFICIENT_REG_RESOURCES);
1724                         return;
1725                 }
1726         }
1727 
1728         pgr->pgr_PRgeneration++;
1729 
1730         if (pr_out->action == PR_OUT_PREEMPT_ABORT) {
1731                 stmf_abort(STMF_QUEUE_ABORT_LU, task, STMF_ABORTED,
1732                     (void *)slu->sl_lu);
1733         }
1734         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1735 }
1736 
1737 static void
1738 sbd_pgr_out_register_and_move(scsi_task_t *task, stmf_data_buf_t *dbuf)
1739 {
1740         sbd_lu_t        *slu   = (sbd_lu_t *)task->task_lu->lu_provider_private;
1741         sbd_it_data_t           *it     = task->task_lu_itl_handle;
1742         sbd_pgr_t               *pgr    = slu->sl_pgr;
1743         sbd_pgr_key_t           *key    = it->pgr_key_ptr;
1744         scsi_devid_desc_t       *lpt;
1745         stmf_remote_port_t      rport;
1746         sbd_pgr_key_t           *newkey;
1747         scsi_prout_reg_move_plist_t *plist;
1748         uint8_t                 *buf, lpt_len;
1749         uint16_t                tpd_len;
1750         uint32_t                adn_len;
1751         uint64_t                svc_key;
1752 
1753         /*
1754          * Check whether the key holds the reservation or current reservation
1755          * is of type all registrants.
1756          */
1757         if (pgr->pgr_rsvholder != key) {
1758                 stmf_scsilib_send_status(task, STATUS_RESERVATION_CONFLICT, 0);
1759                 return;
1760         }
1761 
1762         buf = dbuf->db_sglist[0].seg_addr;
1763         plist = (scsi_prout_reg_move_plist_t *)buf;
1764         svc_key = READ_SCSI64(plist->service_key, uint64_t);
1765         if (svc_key == 0) {
1766                 stmf_scsilib_send_status(task, STATUS_CHECK,
1767                     STMF_SAA_INVALID_FIELD_IN_CDB);
1768                 return;
1769         }
1770 
1771         lpt = stmf_scsilib_get_devid_desc(READ_SCSI16(plist->rel_tgt_port_id,
1772             uint16_t));
1773         if (lpt == NULL) {
1774                 stmf_scsilib_send_status(task, STATUS_CHECK,
1775                     STMF_SAA_INVALID_FIELD_IN_CDB);
1776                 return;
1777         }
1778 
1779         adn_len = READ_SCSI32(plist->tptid_len, uint32_t);
1780         if (!stmf_scsilib_tptid_validate(
1781             (scsi_transport_id_t *)plist->tptid, adn_len, &tpd_len)) {
1782                 kmem_free(lpt, sizeof (scsi_devid_desc_t) + lpt->ident_length);
1783                 stmf_scsilib_send_status(task, STATUS_CHECK,
1784                     STMF_SAA_INVALID_FIELD_IN_PARAM_LIST);
1785                 return;
1786         }
1787 
1788         rport.rport_tptid = (scsi_transport_id_t *)plist->tptid;
1789         rport.rport_tptid_sz = tpd_len;
1790 
1791         if (sbd_pgr_key_compare(key, lpt, &rport)) {
1792                 kmem_free(lpt, sizeof (scsi_devid_desc_t) + lpt->ident_length);
1793                 stmf_scsilib_send_status(task, STATUS_CHECK,
1794                     STMF_SAA_INVALID_FIELD_IN_PARAM_LIST);
1795                 return;
1796         }
1797 
1798         newkey = sbd_pgr_key_registered(pgr, lpt, &rport);
1799         if (newkey) {
1800                 /* Set the pgr_key, irrespective of what it currently holds */
1801                 newkey->pgr_key = svc_key;
1802 
1803                 /* all_tg_pt is set for found key, copy lpt info to the key */
1804                 if (newkey->pgr_key_flags & SBD_PGR_KEY_ALL_TG_PT) {
1805                         if (newkey->pgr_key_lpt_id &&
1806                             newkey->pgr_key_lpt_len > 0) {
1807                                 kmem_free(newkey->pgr_key_lpt_id,
1808                                     newkey->pgr_key_lpt_len);
1809                         }
1810                         lpt_len = sizeof (scsi_devid_desc_t) +
1811                             lpt->ident_length;
1812                         newkey->pgr_key_lpt_len = lpt_len;
1813                         newkey->pgr_key_lpt_id = (scsi_devid_desc_t *)
1814                             kmem_zalloc(lpt_len, KM_SLEEP);
1815                         bcopy(lpt, newkey->pgr_key_lpt_id, lpt_len);
1816                 }
1817                 /* No IT nexus information, hence set PGR_CHEK flag */
1818                 sbd_pgr_set_pgr_check_flag(slu, B_TRUE);
1819         } else  {
1820                 newkey = sbd_pgr_do_register(slu, NULL, lpt, &rport,
1821                     SBD_PGR_KEY_TPT_ID_FLAG, svc_key);
1822         }
1823 
1824         kmem_free(lpt, sizeof (scsi_devid_desc_t) + lpt->ident_length);
1825 
1826         /* Now reserve the key corresponding to the specified IT nexus */
1827         pgr->pgr_rsvholder = newkey;
1828 
1829         if (plist->unreg) {
1830                 sbd_pgr_do_unregister(slu, it, key);
1831         }
1832 
1833 
1834         /* Write to disk if currenty aptpl is set or given task is setting it */
1835         if (pgr->pgr_flags & SBD_PGR_APTPL || plist->aptpl) {
1836                 if (plist->aptpl)
1837                         PGR_SET_FLAG(pgr->pgr_flags, SBD_PGR_APTPL);
1838                 else
1839                         PGR_CLEAR_FLAG(pgr->pgr_flags, SBD_PGR_APTPL);
1840 
1841                 if (sbd_pgr_meta_write(slu) != SBD_SUCCESS) {
1842                         stmf_scsilib_send_status(task, STATUS_CHECK,
1843                             STMF_SAA_INSUFFICIENT_REG_RESOURCES);
1844                         return;
1845                 }
1846         }
1847 
1848         pgr->pgr_PRgeneration++;
1849         stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1850 }
1851 
1852 void
1853 sbd_pgr_remove_it_handle(sbd_lu_t *sl, sbd_it_data_t *my_it) {
1854         sbd_it_data_t *it;
1855 
1856         rw_enter(&sl->sl_pgr->pgr_lock, RW_WRITER);
1857         mutex_enter(&sl->sl_lock);
1858         for (it = sl->sl_it_list; it != NULL; it = it->sbd_it_next) {
1859                 if (it == my_it) {
1860                         if (it->pgr_key_ptr) {
1861                                 sbd_pgr_key_t *key = it->pgr_key_ptr;
1862                                 if (key->pgr_key_it == it) {
1863                                         key->pgr_key_it = NULL;
1864                                         sl->sl_pgr->pgr_flags &=
1865                                             ~SBD_PGR_ALL_KEYS_HAS_IT;
1866                                 }
1867                         }
1868                         break;
1869                 }
1870         }
1871         mutex_exit(&sl->sl_lock);
1872         rw_exit(&sl->sl_pgr->pgr_lock);
1873 
1874 }
1875 
1876 char *
1877 sbd_get_devid_string(sbd_lu_t *sl)
1878 {
1879         char *str = (char *)kmem_zalloc(33, KM_SLEEP);
1880         (void) snprintf(str, 33,
1881             "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
1882             sl->sl_device_id[4], sl->sl_device_id[5], sl->sl_device_id[6],
1883             sl->sl_device_id[7], sl->sl_device_id[8], sl->sl_device_id[9],
1884             sl->sl_device_id[10], sl->sl_device_id[11], sl->sl_device_id[12],
1885             sl->sl_device_id[13], sl->sl_device_id[14], sl->sl_device_id[15],
1886             sl->sl_device_id[16], sl->sl_device_id[17], sl->sl_device_id[18],
1887             sl->sl_device_id[19]);
1888         return (str);
1889 }