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