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