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