persist.c revision 314221
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: stable/10/sbin/camcontrol/persist.c 314221 2017-02-24 20:51:39Z ken $");
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
66struct 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 */
75static 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 */
85static 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 */
101static 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 */
113static 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 */
134static void
135persist_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
155static void
156persist_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 */
177static void
178persist_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 */
213static void
214persist_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 */
239static void
240persist_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
370static void
371persist_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
424int
425scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
426	    int task_attr, int retry_count, int timeout, int verbosemode,
427	    int err_recover)
428{
429	union ccb *ccb = NULL;
430	int c, in = 0, out = 0;
431	int action = -1, num_ids = 0;
432	int error = 0;
433	uint32_t res_len = 0;
434	unsigned long rel_tgt_port = 0;
435	uint8_t *res_buf = NULL;
436	int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 0;
437	struct persist_transport_id *id, *id2;
438	STAILQ_HEAD(, persist_transport_id) transport_id_list;
439	uint64_t key = 0, sa_key = 0;
440	struct scsi_nv *table = NULL;
441	size_t table_size = 0, id_len = 0;
442	uint32_t valid_len = 0;
443	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
444
445	STAILQ_INIT(&transport_id_list);
446
447	ccb = cam_getccb(device);
448	if (ccb == NULL) {
449		warnx("%s: error allocating CCB", __func__);
450		error = 1;
451		goto bailout;
452	}
453
454	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
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	}
743retry:
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*/ task_attr,
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*/ task_attr,
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
954bailout:
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