xref: /freebsd/sbin/camcontrol/persist.c (revision e72055b7feba695a760d45f01f0f8268b1cb4a74)
1 /*-
2  * Copyright (c) 2013 Spectra Logic Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions, and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    substantially similar to the "NO WARRANTY" disclaimer below
13  *    ("Disclaimer") and any redistribution must be conditioned upon
14  *    including a substantially similar Disclaimer requirement for further
15  *    binary redistribution.
16  *
17  * NO WARRANTY
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGES.
29  *
30  * Authors: Ken Merry           (Spectra Logic Corporation)
31  */
32 /*
33  * SCSI Persistent Reservation support for camcontrol(8).
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/ioctl.h>
40 #include <sys/stdint.h>
41 #include <sys/types.h>
42 #include <sys/endian.h>
43 #include <sys/sbuf.h>
44 #include <sys/queue.h>
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <inttypes.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <strings.h>
52 #include <fcntl.h>
53 #include <ctype.h>
54 #include <limits.h>
55 #include <err.h>
56 
57 #include <cam/cam.h>
58 #include <cam/cam_debug.h>
59 #include <cam/cam_ccb.h>
60 #include <cam/scsi/scsi_all.h>
61 #include <cam/scsi/scsi_pass.h>
62 #include <cam/scsi/scsi_message.h>
63 #include <camlib.h>
64 #include "camcontrol.h"
65 
66 struct persist_transport_id {
67 	struct scsi_transportid_header *hdr;
68 	unsigned int alloc_len;
69 	STAILQ_ENTRY(persist_transport_id) links;
70 };
71 
72 /*
73  * Service Actions for PERSISTENT RESERVE IN.
74  */
75 static struct scsi_nv persist_in_actions[] = {
76 	{ "read_keys", SPRI_RK },
77 	{ "read_reservation", SPRI_RR },
78 	{ "report_capabilities", SPRI_RC },
79 	{ "read_full_status", SPRI_RS }
80 };
81 
82 /*
83  * Service Actions for PERSISTENT RESERVE OUT.
84  */
85 static struct scsi_nv persist_out_actions[] = {
86 	{ "register", SPRO_REGISTER },
87 	{ "reserve", SPRO_RESERVE },
88 	{ "release" , SPRO_RELEASE },
89 	{ "clear", SPRO_CLEAR },
90 	{ "preempt", SPRO_PREEMPT },
91 	{ "preempt_abort", SPRO_PRE_ABO },
92 	{ "register_ignore", SPRO_REG_IGNO },
93 	{ "register_move", SPRO_REG_MOVE },
94 	{ "replace_lost", SPRO_REPL_LOST_RES }
95 };
96 
97 /*
98  * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
99  * spec.  The others are obsolete.
100  */
101 static struct scsi_nv persist_scope_table[] = {
102 	{ "lun", SPR_LU_SCOPE },
103 	{ "extent", SPR_EXTENT_SCOPE },
104 	{ "element", SPR_ELEMENT_SCOPE }
105 };
106 
107 /*
108  * Reservation types.  The longer name for a given reservation type is
109  * listed first, so that it makes more sense when we print out the
110  * reservation type.  We step through the table linearly when looking for
111  * the text name for a particular numeric reservation type value.
112  */
113 static struct scsi_nv persist_type_table[] = {
114 	{ "read_shared", SPR_TYPE_RD_SHARED },
115 	{ "write_exclusive", SPR_TYPE_WR_EX },
116 	{ "wr_ex", SPR_TYPE_WR_EX },
117 	{ "read_exclusive", SPR_TYPE_RD_EX },
118 	{ "rd_ex", SPR_TYPE_RD_EX },
119 	{ "exclusive_access", SPR_TYPE_EX_AC },
120 	{ "ex_ac", SPR_TYPE_EX_AC },
121 	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
122 	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
123 	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
124 	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
125 	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
126 	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
127 	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
128 	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
129 };
130 
131 /*
132  * Print out the standard scope/type field.
133  */
134 static void
135 persist_print_scopetype(uint8_t scopetype)
136 {
137 	const char *tmpstr;
138 	int num_entries;
139 
140 	num_entries = sizeof(persist_scope_table) /
141 		      sizeof(persist_scope_table[0]);
142 	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
143 				scopetype & SPR_SCOPE_MASK);
144 	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
145 		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
146 
147 	num_entries = sizeof(persist_type_table) /
148 		      sizeof(persist_type_table[0]);
149 	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
150 				scopetype & SPR_TYPE_MASK);
151 	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
152 		"Unknown", scopetype & SPR_TYPE_MASK);
153 }
154 
155 static void
156 persist_print_transportid(uint8_t *buf, uint32_t len)
157 {
158 	struct sbuf *sb;
159 
160 	sb = sbuf_new_auto();
161 	if (sb == NULL)
162 		fprintf(stderr, "Unable to allocate sbuf\n");
163 
164 	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
165 
166 	sbuf_finish(sb);
167 
168 	fprintf(stdout, "%s\n", sbuf_data(sb));
169 
170 	sbuf_delete(sb);
171 }
172 
173 /*
174  * Print out a persistent reservation.  This is used with the READ
175  * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
176  */
177 static void
178 persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
179 {
180 	uint32_t length;
181 	struct scsi_per_res_in_rsrv *res;
182 
183 	length = scsi_4btoul(hdr->length);
184 	length = MIN(length, valid_len);
185 
186 	res = (struct scsi_per_res_in_rsrv *)hdr;
187 
188 	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
189 		if (length == 0)
190 			fprintf(stdout, "No reservations.\n");
191 		else
192 			warnx("unable to print reservation, only got %u "
193 			      "valid bytes", length);
194 		return;
195 	}
196 	fprintf(stdout, "PRgeneration: %#x\n",
197 		scsi_4btoul(res->header.generation));
198 	fprintf(stdout, "Reservation Key: %#jx\n",
199 		(uintmax_t)scsi_8btou64(res->data.reservation));
200 	fprintf(stdout, "Scope address: %#x\n",
201 		scsi_4btoul(res->data.scope_addr));
202 
203 	persist_print_scopetype(res->data.scopetype);
204 
205 	fprintf(stdout, "Extent length: %u\n",
206 		scsi_2btoul(res->data.extent_length));
207 }
208 
209 /*
210  * Print out persistent reservation keys.  This is used with the READ KEYS
211  * service action of the PERSISTENT RESERVE IN command.
212  */
213 static void
214 persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
215 {
216 	uint32_t length, num_keys, i;
217 	struct scsi_per_res_key *key;
218 
219 	length = scsi_4btoul(hdr->length);
220 	length = MIN(length, valid_len);
221 
222 	num_keys = length / sizeof(*key);
223 
224 	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
225 	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
226 		(num_keys == 0) ? "." : ":");
227 
228 	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
229 	     i++, key++) {
230 		fprintf(stdout, "%u: %#jx\n", i,
231 			(uintmax_t)scsi_8btou64(key->key));
232 	}
233 }
234 
235 /*
236  * Print out persistent reservation capabilities.  This is used with the
237  * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
238  */
239 static void
240 persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
241 {
242 	uint32_t length;
243 	int check_type_mask = 0;
244 
245 	length = scsi_2btoul(cap->length);
246 	length = MIN(length, valid_len);
247 
248 	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
249 		fprintf(stdout, "Insufficient data (%u bytes) to report "
250 			"full capabilities\n", length);
251 		return;
252 	}
253 	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
254 		check_type_mask = 1;
255 
256 	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
257 		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
258 	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
259 		(cap->flags1 & SPRI_CRH) ? 1 : 0);
260 	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
261 		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
262 	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
263 		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
264 	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
265 		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
266 	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
267 		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
268 	/*
269 	 * These cases are cut-and-pasted from SPC4r36l.  There is no
270 	 * succinct way to describe these otherwise, and even with the
271 	 * verbose description, the user will probably have to refer to
272 	 * the spec to fully understand what is going on.
273 	 */
274 	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
275 	case SPRI_ALLOW_1:
276 		fprintf(stdout,
277 "    The device server allows the TEST UNIT READY command through Write\n"
278 "    Exclusive type reservations and Exclusive Access type reservations\n"
279 "    and does not provide information about whether the following commands\n"
280 "    are allowed through Write Exclusive type reservations:\n"
281 "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
282 "           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
283 "           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
284 "           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
285 "        b) the READ DEFECT DATA command (see SBC-3).\n");
286 		break;
287 	case SPRI_ALLOW_2:
288 		fprintf(stdout,
289 "    The device server allows the TEST UNIT READY command through Write\n"
290 "    Exclusive type reservations and Exclusive Access type reservations\n"
291 "    and does not allow the following commands through Write Exclusive type\n"
292 "    reservations:\n"
293 "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
294 "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
295 "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
296 "           FUNCTION command; and\n"
297 "        b) the READ DEFECT DATA command.\n"
298 "    The device server does not allow the RECEIVE COPY RESULTS command\n"
299 "    through Write Exclusive type reservations or Exclusive Access type\n"
300 "    reservations.\n");
301 		break;
302 	case SPRI_ALLOW_3:
303 		fprintf(stdout,
304 "    The device server allows the TEST UNIT READY command through Write\n"
305 "    Exclusive type reservations and Exclusive Access type reservations\n"
306 "    and allows the following commands through Write Exclusive type\n"
307 "    reservations:\n"
308 "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
309 "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
310 "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
311 "           FUNCTION command; and\n"
312 "        b) the READ DEFECT DATA command.\n"
313 "    The device server does not allow the RECEIVE COPY RESULTS command\n"
314 "    through Write Exclusive type reservations or Exclusive Access type\n"
315 "    reservations.\n");
316 		break;
317 	case SPRI_ALLOW_4:
318 		fprintf(stdout,
319 "    The device server allows the TEST UNIT READY command and the RECEIVE\n"
320 "    COPY RESULTS command through Write Exclusive type reservations and\n"
321 "    Exclusive Access type reservations and allows the following commands\n"
322 "    through Write Exclusive type reservations:\n"
323 "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
324 "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
325 "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
326 "           FUNCTION command; and\n"
327 "        b) the READ DEFECT DATA command.\n");
328 		break;
329 	case SPRI_ALLOW_NA:
330 		fprintf(stdout,
331 "    No information is provided about whether certain commands are allowed\n"
332 "    through certain types of persistent reservations.\n");
333 		break;
334 	default:
335 		fprintf(stdout,
336 "    Unknown ALLOW COMMANDS value %#x\n",
337 			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
338 			SPRI_ALLOW_CMD_SHIFT);
339 		break;
340 	}
341 	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
342 		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
343 	if ((check_type_mask != 0)
344 	 && (cap->flags2 & SPRI_TMV)) {
345 		fprintf(stdout, "Supported Persistent Reservation Types:\n");
346 		fprintf(stdout, "    Write Exclusive - All Registrants "
347 			"(WR_EX_AR): %d\n",
348 			(cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0);
349 		fprintf(stdout, "    Exclusive Access - Registrants Only "
350 			"(EX_AC_RO): %d\n",
351 			(cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0);
352 		fprintf(stdout, "    Write Exclusive - Registrants Only "
353 			"(WR_EX_RO): %d\n",
354 			(cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0);
355 		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
356 			(cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0);
357 		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
358 			(cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0);
359 		fprintf(stdout, "    Exclusive Access - All Registrants "
360 			"(EX_AC_AR): %d\n",
361 			(cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0);
362 	} else {
363 		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
364 			"valid\n");
365 	}
366 
367 
368 }
369 
370 static void
371 persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
372 {
373 	uint32_t length, len_to_go = 0;
374 	struct scsi_per_res_in_full_desc *desc;
375 	uint8_t *cur_pos;
376 	int i;
377 
378 	length = scsi_4btoul(hdr->length);
379 	length = MIN(length, valid_len);
380 
381 	if (length < sizeof(*desc)) {
382 		if (length == 0)
383 			fprintf(stdout, "No reservations.\n");
384 		else
385 			warnx("unable to print reservation, only got %u "
386 			      "valid bytes", length);
387 		return;
388 	}
389 
390 	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
391 	cur_pos = (uint8_t *)&hdr[1];
392 	for (len_to_go = length, i = 0,
393 	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
394 	     len_to_go >= sizeof(*desc);
395 	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
396 		uint32_t additional_length, cur_length;
397 
398 
399 		fprintf(stdout, "Reservation Key: %#jx\n",
400 			(uintmax_t)scsi_8btou64(desc->res_key.key));
401 		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
402 			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
403 		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
404 			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
405 
406 		if (desc->flags & SPRI_FULL_R_HOLDER)
407 			persist_print_scopetype(desc->scopetype);
408 
409 		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
410 			fprintf(stdout, "Relative Target Port ID: %#x\n",
411 				scsi_2btoul(desc->rel_trgt_port_id));
412 
413 		additional_length = scsi_4btoul(desc->additional_length);
414 
415 		persist_print_transportid(desc->transport_id,
416 					  additional_length);
417 
418 		cur_length = sizeof(*desc) + additional_length;
419 		len_to_go -= cur_length;
420 		cur_pos += cur_length;
421 	}
422 }
423 
424 int
425 scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
426 	    int retry_count, int timeout, int verbosemode, int err_recover)
427 {
428 	union ccb *ccb = NULL;
429 	int c, in = 0, out = 0;
430 	int action = -1, num_ids = 0;
431 	int error = 0;
432 	uint32_t res_len = 0;
433 	unsigned long rel_tgt_port = 0;
434 	uint8_t *res_buf = NULL;
435 	int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 0;
436 	struct persist_transport_id *id, *id2;
437 	STAILQ_HEAD(, persist_transport_id) transport_id_list;
438 	uint64_t key = 0, sa_key = 0;
439 	struct scsi_nv *table = NULL;
440 	size_t table_size = 0, id_len = 0;
441 	uint32_t valid_len = 0;
442 	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
443 
444 	STAILQ_INIT(&transport_id_list);
445 
446 	ccb = cam_getccb(device);
447 	if (ccb == NULL) {
448 		warnx("%s: error allocating CCB", __func__);
449 		error = 1;
450 		goto bailout;
451 	}
452 
453 	bzero(&(&ccb->ccb_h)[1],
454 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
455 
456 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
457 		switch (c) {
458 		case 'a':
459 			all_tg_pt = 1;
460 			break;
461 		case 'I': {
462 			int error_str_len = 128;
463 			char error_str[error_str_len];
464 			char *id_str;
465 
466 			id = malloc(sizeof(*id));
467 			if (id == NULL) {
468 				warnx("%s: error allocating %zu bytes",
469 				    __func__, sizeof(*id));
470 				error = 1;
471 				goto bailout;
472 			}
473 			bzero(id, sizeof(*id));
474 
475 			id_str = strdup(optarg);
476 			if (id_str == NULL) {
477 				warnx("%s: error duplicating string %s",
478 				    __func__, optarg);
479 				free(id);
480 				error = 1;
481 				goto bailout;
482 			}
483 			error = scsi_parse_transportid(id_str, &id->hdr,
484 			    &id->alloc_len, error_str, error_str_len);
485 			if (error != 0) {
486 				warnx("%s", error_str);
487 				error = 1;
488 				free(id);
489 				free(id_str);
490 				goto bailout;
491 			}
492 			free(id_str);
493 
494 			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
495 			num_ids++;
496 			id_len += id->alloc_len;
497 			break;
498 		}
499 		case 'k':
500 		case 'K': {
501 			char *endptr;
502 			uint64_t tmpval;
503 
504 			tmpval = strtoumax(optarg, &endptr, 0);
505 			if (*endptr != '\0') {
506 				warnx("%s: invalid key argument %s", __func__,
507 				    optarg);
508 				error = 1;
509 				goto bailout;
510 			}
511 			if (c == 'k') {
512 				key = tmpval;
513 				key_set = 1;
514 			} else {
515 				sa_key = tmpval;
516 				sa_key_set = 1;
517 			}
518 			break;
519 		}
520 		case 'i':
521 		case 'o': {
522 			scsi_nv_status status;
523 			int table_entry = 0;
524 
525 			if (c == 'i') {
526 				in = 1;
527 				table = persist_in_actions;
528 				table_size = sizeof(persist_in_actions) /
529 					sizeof(persist_in_actions[0]);
530 			} else {
531 				out = 1;
532 				table = persist_out_actions;
533 				table_size = sizeof(persist_out_actions) /
534 					sizeof(persist_out_actions[0]);
535 			}
536 
537 			if ((in + out) > 1) {
538 				warnx("%s: only one in (-i) or out (-o) "
539 				    "action is allowed", __func__);
540 				error = 1;
541 				goto bailout;
542 			}
543 
544 			status = scsi_get_nv(table, table_size, optarg,
545 					     &table_entry,SCSI_NV_FLAG_IG_CASE);
546 			if (status == SCSI_NV_FOUND)
547 				action = table[table_entry].value;
548 			else {
549 				warnx("%s: %s %s option %s", __func__,
550 				    (status == SCSI_NV_AMBIGUOUS) ?
551 				    "ambiguous" : "invalid", in ? "in" :
552 				    "out", optarg);
553 				error = 1;
554 				goto bailout;
555 			}
556 			break;
557 		}
558 		case 'p':
559 			aptpl = 1;
560 			break;
561 		case 'R': {
562 			char *endptr;
563 
564 			rel_tgt_port = strtoul(optarg, &endptr, 0);
565 			if (*endptr != '\0') {
566 				warnx("%s: invalid relative target port %s",
567 				    __func__, optarg);
568 				error = 1;
569 				goto bailout;
570 			}
571 			rel_port_set = 1;
572 			break;
573 		}
574 		case 's': {
575 			size_t scope_size;
576 			struct scsi_nv *scope_table = NULL;
577 			scsi_nv_status status;
578 			int table_entry = 0;
579 			char *endptr;
580 
581 			/*
582 			 * First check to see if the user gave us a numeric
583 			 * argument.  If so, we'll try using it.
584 			 */
585 			if (isdigit(optarg[0])) {
586 				scope = strtol(optarg, &endptr, 0);
587 				if (*endptr != '\0') {
588 					warnx("%s: invalid scope %s",
589 					       __func__, optarg);
590 					error = 1;
591 					goto bailout;
592 				}
593 				scope = (scope << SPR_SCOPE_SHIFT) &
594 				    SPR_SCOPE_MASK;
595 				break;
596 			}
597 
598 			scope_size = sizeof(persist_scope_table) /
599 				     sizeof(persist_scope_table[0]);
600 			scope_table = persist_scope_table;
601 			status = scsi_get_nv(scope_table, scope_size, optarg,
602 					     &table_entry,SCSI_NV_FLAG_IG_CASE);
603 			if (status == SCSI_NV_FOUND)
604 				scope = scope_table[table_entry].value;
605 			else {
606 				warnx("%s: %s scope %s", __func__,
607 				      (status == SCSI_NV_AMBIGUOUS) ?
608 				      "ambiguous" : "invalid", optarg);
609 				error = 1;
610 				goto bailout;
611 			}
612 			break;
613 		}
614 		case 'S':
615 			spec_i_pt = 1;
616 			break;
617 		case 'T': {
618 			size_t res_type_size;
619 			struct scsi_nv *rtype_table = NULL;
620 			scsi_nv_status status;
621 			char *endptr;
622 			int table_entry = 0;
623 
624 			/*
625 			 * First check to see if the user gave us a numeric
626 			 * argument.  If so, we'll try using it.
627 			 */
628 			if (isdigit(optarg[0])) {
629 				res_type = strtol(optarg, &endptr, 0);
630 				if (*endptr != '\0') {
631 					warnx("%s: invalid reservation type %s",
632 					       __func__, optarg);
633 					error = 1;
634 					goto bailout;
635 				}
636 				break;
637 			}
638 
639 			res_type_size = sizeof(persist_type_table) /
640 					sizeof(persist_type_table[0]);
641 			rtype_table = persist_type_table;
642 			status = scsi_get_nv(rtype_table, res_type_size,
643 					     optarg, &table_entry,
644 					     SCSI_NV_FLAG_IG_CASE);
645 			if (status == SCSI_NV_FOUND)
646 				res_type = rtype_table[table_entry].value;
647 			else {
648 				warnx("%s: %s reservation type %s", __func__,
649 				      (status == SCSI_NV_AMBIGUOUS) ?
650 				      "ambiguous" : "invalid", optarg);
651 				error = 1;
652 				goto bailout;
653 			}
654 			break;
655 		}
656 		case 'U':
657 			unreg = 1;
658 			break;
659 		default:
660 			break;
661 		}
662 	}
663 
664 	if ((in + out) != 1) {
665 		warnx("%s: you must specify one of -i or -o", __func__);
666 		error = 1;
667 		goto bailout;
668 	}
669 
670 	/*
671 	 * Note that we don't really try to figure out whether the user
672 	 * needs to specify one or both keys.  There are a number of
673 	 * scenarios, and sometimes 0 is a valid and desired value.
674 	 */
675 	if (in != 0) {
676 		switch (action) {
677 		case SPRI_RK:
678 		case SPRI_RR:
679 		case SPRI_RS:
680 			/*
681 			 * Allocate the maximum length possible for these
682 			 * service actions.  According to the spec, the
683 			 * target is supposed to return the available
684 			 * length in the header, regardless of the
685 			 * allocation length.  In practice, though, with
686 			 * the READ FULL STATUS (SPRI_RS) service action,
687 			 * some Seagate drives (in particular a
688 			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
689 			 * don't return the available length if you only
690 			 * allocate the length of the header.  So just
691 			 * allocate the maximum here so we don't miss
692 			 * anything.
693 			 */
694 			res_len = SPRI_MAX_LEN;
695 			break;
696 		case SPRI_RC:
697 			res_len = sizeof(struct scsi_per_res_cap);
698 			break;
699 		default:
700 			/* In theory we should catch this above */
701 			warnx("%s: invalid action %d", __func__, action);
702 			error = 1;
703 			goto bailout;
704 			break;
705 		}
706 	} else {
707 
708 		/*
709 		 * XXX KDM need to add length for transport IDs for the
710 		 * register and move service action and the register
711 		 * service action with the SPEC_I_PT bit set.
712 		 */
713 		if (action == SPRO_REG_MOVE) {
714 			if (num_ids != 1) {
715 			    	warnx("%s: register and move requires a "
716 				    "single transport ID (-I)", __func__);
717 				error = 1;
718 				goto bailout;
719 			}
720 			if (rel_port_set == 0) {
721 				warnx("%s: register and move requires a "
722 				    "relative target port (-R)", __func__);
723 				error = 1;
724 				goto bailout;
725 			}
726 			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
727 		} else {
728 			res_len = sizeof(struct scsi_per_res_out_parms);
729 			if ((action == SPRO_REGISTER)
730 			 && (num_ids != 0)) {
731 				/*
732 				 * If the user specifies any IDs with the
733 				 * register service action, turn on the
734 				 * spec_i_pt bit.
735 				 */
736 				spec_i_pt = 1;
737 				res_len += id_len;
738 				res_len +=
739 				    sizeof(struct scsi_per_res_out_trans_ids);
740 			}
741 		}
742 	}
743 retry:
744 	if (res_buf != NULL) {
745 		free(res_buf);
746 		res_buf = NULL;
747 	}
748 	res_buf = malloc(res_len);
749 	if (res_buf == NULL) {
750 		warn("%s: error allocating %d bytes", __func__, res_len);
751 		error = 1;
752 		goto bailout;
753 	}
754 	bzero(res_buf, res_len);
755 
756 	if (in != 0) {
757 		scsi_persistent_reserve_in(&ccb->csio,
758 					   /*retries*/ retry_count,
759 					   /*cbfcnp*/ NULL,
760 					   /*tag_action*/ MSG_SIMPLE_Q_TAG,
761 					   /*service_action*/ action,
762 					   /*data_ptr*/ res_buf,
763 					   /*dxfer_len*/ res_len,
764 					   /*sense_len*/ SSD_FULL_SIZE,
765 					   /*timeout*/ timeout ? timeout :5000);
766 
767 	} else {
768 		switch (action) {
769 		case SPRO_REGISTER:
770 			if (spec_i_pt != 0) {
771 				struct scsi_per_res_out_trans_ids *id_hdr;
772 				uint8_t *bufptr;
773 
774 				bufptr = res_buf +
775 				    sizeof(struct scsi_per_res_out_parms) +
776 				    sizeof(struct scsi_per_res_out_trans_ids);
777 				STAILQ_FOREACH(id, &transport_id_list, links) {
778 					bcopy(id->hdr, bufptr, id->alloc_len);
779 					bufptr += id->alloc_len;
780 				}
781 				id_hdr = (struct scsi_per_res_out_trans_ids *)
782 				    (res_buf +
783 				    sizeof(struct scsi_per_res_out_parms));
784 				scsi_ulto4b(id_len, id_hdr->additional_length);
785 			}
786 		case SPRO_REG_IGNO:
787 		case SPRO_PREEMPT:
788 		case SPRO_PRE_ABO:
789 		case SPRO_RESERVE:
790 		case SPRO_RELEASE:
791 		case SPRO_CLEAR:
792 		case SPRO_REPL_LOST_RES: {
793 			struct scsi_per_res_out_parms *parms;
794 
795 			parms = (struct scsi_per_res_out_parms *)res_buf;
796 
797 			scsi_u64to8b(key, parms->res_key.key);
798 			scsi_u64to8b(sa_key, parms->serv_act_res_key);
799 			if (spec_i_pt != 0)
800 				parms->flags |= SPR_SPEC_I_PT;
801 			if (all_tg_pt != 0)
802 				parms->flags |= SPR_ALL_TG_PT;
803 			if (aptpl != 0)
804 				parms->flags |= SPR_APTPL;
805 			break;
806 		}
807 		case SPRO_REG_MOVE: {
808 			struct scsi_per_res_reg_move *reg_move;
809 			uint8_t *bufptr;
810 
811 			reg_move = (struct scsi_per_res_reg_move *)res_buf;
812 
813 			scsi_u64to8b(key, reg_move->res_key.key);
814 			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
815 			if (unreg != 0)
816 				reg_move->flags |= SPR_REG_MOVE_UNREG;
817 			if (aptpl != 0)
818 				reg_move->flags |= SPR_REG_MOVE_APTPL;
819 			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
820 			id = STAILQ_FIRST(&transport_id_list);
821 			/*
822 			 * This shouldn't happen, since we already checked
823 			 * the number of IDs above.
824 			 */
825 			if (id == NULL) {
826 				warnx("%s: No transport IDs found!", __func__);
827 				error = 1;
828 				goto bailout;
829 			}
830 			bufptr = (uint8_t *)&reg_move[1];
831 			bcopy(id->hdr, bufptr, id->alloc_len);
832 			scsi_ulto4b(id->alloc_len,
833 			    reg_move->transport_id_length);
834 			break;
835 		}
836 		default:
837 			break;
838 		}
839 		scsi_persistent_reserve_out(&ccb->csio,
840 					    /*retries*/ retry_count,
841 					    /*cbfcnp*/ NULL,
842 					    /*tag_action*/ MSG_SIMPLE_Q_TAG,
843 					    /*service_action*/ action,
844 					    /*scope*/ scope,
845 					    /*res_type*/ res_type,
846 					    /*data_ptr*/ res_buf,
847 					    /*dxfer_len*/ res_len,
848 					    /*sense_len*/ SSD_FULL_SIZE,
849 					    /*timeout*/ timeout ?timeout :5000);
850 	}
851 
852 	/* Disable freezing the device queue */
853 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
854 
855 	if (err_recover != 0)
856 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
857 
858 	if (cam_send_ccb(device, ccb) < 0) {
859 		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
860 		    "IN" : "OUT");
861 
862 		if (verbosemode != 0) {
863 			cam_error_print(device, ccb, CAM_ESF_ALL,
864 					CAM_EPF_ALL, stderr);
865 		}
866 
867 		error = 1;
868 		goto bailout;
869 	}
870 
871 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
872 		if (verbosemode != 0) {
873 			cam_error_print(device, ccb, CAM_ESF_ALL,
874 					CAM_EPF_ALL, stderr);
875 		}
876 		error = 1;
877 		goto bailout;
878 	}
879 
880 	if (in == 0)
881 		goto bailout;
882 
883 	valid_len = res_len - ccb->csio.resid;
884 
885 	switch (action) {
886 	case SPRI_RK:
887 	case SPRI_RR:
888 	case SPRI_RS: {
889 		struct scsi_per_res_in_header *hdr;
890 		uint32_t hdr_len;
891 
892 		if (valid_len < sizeof(*hdr)) {
893 			warnx("%s: only got %d valid bytes, need %zd",
894 			      __func__, valid_len, sizeof(*hdr));
895 			error = 1;
896 			goto bailout;
897 		}
898 		hdr = (struct scsi_per_res_in_header *)res_buf;
899 		hdr_len = scsi_4btoul(hdr->length);
900 
901 		if (hdr_len > (res_len - sizeof(*hdr))) {
902 			res_len = hdr_len + sizeof(*hdr);
903 			goto retry;
904 		}
905 
906 		if (action == SPRI_RK) {
907 			persist_print_keys(hdr, valid_len);
908 		} else if (action == SPRI_RR) {
909 			persist_print_res(hdr, valid_len);
910 		} else {
911 			persist_print_full(hdr, valid_len);
912 		}
913 		break;
914 	}
915 	case SPRI_RC: {
916 		struct scsi_per_res_cap *cap;
917 		uint32_t cap_len;
918 
919 		if (valid_len < sizeof(*cap)) {
920 			warnx("%s: only got %u valid bytes, need %zd",
921 			      __func__, valid_len, sizeof(*cap));
922 			error = 1;
923 			goto bailout;
924 		}
925 		cap = (struct scsi_per_res_cap *)res_buf;
926 		cap_len = scsi_2btoul(cap->length);
927 		if (cap_len != sizeof(*cap)) {
928 			/*
929 			 * We should be able to deal with this,
930 			 * it's just more trouble.
931 			 */
932 			warnx("%s: reported size %u is different "
933 			    "than expected size %zd", __func__,
934 			    cap_len, sizeof(*cap));
935 		}
936 
937 		/*
938 		 * If there is more data available, grab it all,
939 		 * even though we don't really know what to do with
940 		 * the extra data since it obviously wasn't in the
941 		 * spec when this code was written.
942 		 */
943 		if (cap_len > res_len) {
944 			res_len = cap_len;
945 			goto retry;
946 		}
947 		persist_print_cap(cap, valid_len);
948 		break;
949 	}
950 	default:
951 		break;
952 	}
953 
954 bailout:
955 	free(res_buf);
956 
957 	if (ccb != NULL)
958 		cam_freeccb(ccb);
959 
960 	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
961 		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
962 		    links);
963 		free(id);
964 	}
965 	return (error);
966 }
967