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 }
1890