persist.c revision 302377
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 302377 2016-07-06 17:45:38Z truckman $");
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 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	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
454
455	while ((c = getopt(argc, argv, combinedopt)) != -1) {
456		switch (c) {
457		case 'a':
458			all_tg_pt = 1;
459			break;
460		case 'I': {
461			int error_str_len = 128;
462			char error_str[error_str_len];
463			char *id_str;
464
465			id = malloc(sizeof(*id));
466			if (id == NULL) {
467				warnx("%s: error allocating %zu bytes",
468				    __func__, sizeof(*id));
469				error = 1;
470				goto bailout;
471			}
472			bzero(id, sizeof(*id));
473
474			id_str = strdup(optarg);
475			if (id_str == NULL) {
476				warnx("%s: error duplicating string %s",
477				    __func__, optarg);
478				free(id);
479				error = 1;
480				goto bailout;
481			}
482			error = scsi_parse_transportid(id_str, &id->hdr,
483			    &id->alloc_len, error_str, error_str_len);
484			if (error != 0) {
485				warnx("%s", error_str);
486				error = 1;
487				free(id);
488				free(id_str);
489				goto bailout;
490			}
491			free(id_str);
492
493			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
494			num_ids++;
495			id_len += id->alloc_len;
496			break;
497		}
498		case 'k':
499		case 'K': {
500			char *endptr;
501			uint64_t tmpval;
502
503			tmpval = strtoumax(optarg, &endptr, 0);
504			if (*endptr != '\0') {
505				warnx("%s: invalid key argument %s", __func__,
506				    optarg);
507				error = 1;
508				goto bailout;
509			}
510			if (c == 'k') {
511				key = tmpval;
512				key_set = 1;
513			} else {
514				sa_key = tmpval;
515				sa_key_set = 1;
516			}
517			break;
518		}
519		case 'i':
520		case 'o': {
521			scsi_nv_status status;
522			int table_entry = 0;
523
524			if (c == 'i') {
525				in = 1;
526				table = persist_in_actions;
527				table_size = sizeof(persist_in_actions) /
528					sizeof(persist_in_actions[0]);
529			} else {
530				out = 1;
531				table = persist_out_actions;
532				table_size = sizeof(persist_out_actions) /
533					sizeof(persist_out_actions[0]);
534			}
535
536			if ((in + out) > 1) {
537				warnx("%s: only one in (-i) or out (-o) "
538				    "action is allowed", __func__);
539				error = 1;
540				goto bailout;
541			}
542
543			status = scsi_get_nv(table, table_size, optarg,
544					     &table_entry,SCSI_NV_FLAG_IG_CASE);
545			if (status == SCSI_NV_FOUND)
546				action = table[table_entry].value;
547			else {
548				warnx("%s: %s %s option %s", __func__,
549				    (status == SCSI_NV_AMBIGUOUS) ?
550				    "ambiguous" : "invalid", in ? "in" :
551				    "out", optarg);
552				error = 1;
553				goto bailout;
554			}
555			break;
556		}
557		case 'p':
558			aptpl = 1;
559			break;
560		case 'R': {
561			char *endptr;
562
563			rel_tgt_port = strtoul(optarg, &endptr, 0);
564			if (*endptr != '\0') {
565				warnx("%s: invalid relative target port %s",
566				    __func__, optarg);
567				error = 1;
568				goto bailout;
569			}
570			rel_port_set = 1;
571			break;
572		}
573		case 's': {
574			size_t scope_size;
575			struct scsi_nv *scope_table = NULL;
576			scsi_nv_status status;
577			int table_entry = 0;
578			char *endptr;
579
580			/*
581			 * First check to see if the user gave us a numeric
582			 * argument.  If so, we'll try using it.
583			 */
584			if (isdigit(optarg[0])) {
585				scope = strtol(optarg, &endptr, 0);
586				if (*endptr != '\0') {
587					warnx("%s: invalid scope %s",
588					       __func__, optarg);
589					error = 1;
590					goto bailout;
591				}
592				scope = (scope << SPR_SCOPE_SHIFT) &
593				    SPR_SCOPE_MASK;
594				break;
595			}
596
597			scope_size = sizeof(persist_scope_table) /
598				     sizeof(persist_scope_table[0]);
599			scope_table = persist_scope_table;
600			status = scsi_get_nv(scope_table, scope_size, optarg,
601					     &table_entry,SCSI_NV_FLAG_IG_CASE);
602			if (status == SCSI_NV_FOUND)
603				scope = scope_table[table_entry].value;
604			else {
605				warnx("%s: %s scope %s", __func__,
606				      (status == SCSI_NV_AMBIGUOUS) ?
607				      "ambiguous" : "invalid", optarg);
608				error = 1;
609				goto bailout;
610			}
611			break;
612		}
613		case 'S':
614			spec_i_pt = 1;
615			break;
616		case 'T': {
617			size_t res_type_size;
618			struct scsi_nv *rtype_table = NULL;
619			scsi_nv_status status;
620			char *endptr;
621			int table_entry = 0;
622
623			/*
624			 * First check to see if the user gave us a numeric
625			 * argument.  If so, we'll try using it.
626			 */
627			if (isdigit(optarg[0])) {
628				res_type = strtol(optarg, &endptr, 0);
629				if (*endptr != '\0') {
630					warnx("%s: invalid reservation type %s",
631					       __func__, optarg);
632					error = 1;
633					goto bailout;
634				}
635				break;
636			}
637
638			res_type_size = sizeof(persist_type_table) /
639					sizeof(persist_type_table[0]);
640			rtype_table = persist_type_table;
641			status = scsi_get_nv(rtype_table, res_type_size,
642					     optarg, &table_entry,
643					     SCSI_NV_FLAG_IG_CASE);
644			if (status == SCSI_NV_FOUND)
645				res_type = rtype_table[table_entry].value;
646			else {
647				warnx("%s: %s reservation type %s", __func__,
648				      (status == SCSI_NV_AMBIGUOUS) ?
649				      "ambiguous" : "invalid", optarg);
650				error = 1;
651				goto bailout;
652			}
653			break;
654		}
655		case 'U':
656			unreg = 1;
657			break;
658		default:
659			break;
660		}
661	}
662
663	if ((in + out) != 1) {
664		warnx("%s: you must specify one of -i or -o", __func__);
665		error = 1;
666		goto bailout;
667	}
668
669	/*
670	 * Note that we don't really try to figure out whether the user
671	 * needs to specify one or both keys.  There are a number of
672	 * scenarios, and sometimes 0 is a valid and desired value.
673	 */
674	if (in != 0) {
675		switch (action) {
676		case SPRI_RK:
677		case SPRI_RR:
678		case SPRI_RS:
679			/*
680			 * Allocate the maximum length possible for these
681			 * service actions.  According to the spec, the
682			 * target is supposed to return the available
683			 * length in the header, regardless of the
684			 * allocation length.  In practice, though, with
685			 * the READ FULL STATUS (SPRI_RS) service action,
686			 * some Seagate drives (in particular a
687			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
688			 * don't return the available length if you only
689			 * allocate the length of the header.  So just
690			 * allocate the maximum here so we don't miss
691			 * anything.
692			 */
693			res_len = SPRI_MAX_LEN;
694			break;
695		case SPRI_RC:
696			res_len = sizeof(struct scsi_per_res_cap);
697			break;
698		default:
699			/* In theory we should catch this above */
700			warnx("%s: invalid action %d", __func__, action);
701			error = 1;
702			goto bailout;
703			break;
704		}
705	} else {
706
707		/*
708		 * XXX KDM need to add length for transport IDs for the
709		 * register and move service action and the register
710		 * service action with the SPEC_I_PT bit set.
711		 */
712		if (action == SPRO_REG_MOVE) {
713			if (num_ids != 1) {
714			    	warnx("%s: register and move requires a "
715				    "single transport ID (-I)", __func__);
716				error = 1;
717				goto bailout;
718			}
719			if (rel_port_set == 0) {
720				warnx("%s: register and move requires a "
721				    "relative target port (-R)", __func__);
722				error = 1;
723				goto bailout;
724			}
725			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
726		} else {
727			res_len = sizeof(struct scsi_per_res_out_parms);
728			if ((action == SPRO_REGISTER)
729			 && (num_ids != 0)) {
730				/*
731				 * If the user specifies any IDs with the
732				 * register service action, turn on the
733				 * spec_i_pt bit.
734				 */
735				spec_i_pt = 1;
736				res_len += id_len;
737				res_len +=
738				    sizeof(struct scsi_per_res_out_trans_ids);
739			}
740		}
741	}
742retry:
743	if (res_buf != NULL) {
744		free(res_buf);
745		res_buf = NULL;
746	}
747	res_buf = malloc(res_len);
748	if (res_buf == NULL) {
749		warn("%s: error allocating %d bytes", __func__, res_len);
750		error = 1;
751		goto bailout;
752	}
753	bzero(res_buf, res_len);
754
755	if (in != 0) {
756		scsi_persistent_reserve_in(&ccb->csio,
757					   /*retries*/ retry_count,
758					   /*cbfcnp*/ NULL,
759					   /*tag_action*/ MSG_SIMPLE_Q_TAG,
760					   /*service_action*/ action,
761					   /*data_ptr*/ res_buf,
762					   /*dxfer_len*/ res_len,
763					   /*sense_len*/ SSD_FULL_SIZE,
764					   /*timeout*/ timeout ? timeout :5000);
765
766	} else {
767		switch (action) {
768		case SPRO_REGISTER:
769			if (spec_i_pt != 0) {
770				struct scsi_per_res_out_trans_ids *id_hdr;
771				uint8_t *bufptr;
772
773				bufptr = res_buf +
774				    sizeof(struct scsi_per_res_out_parms) +
775				    sizeof(struct scsi_per_res_out_trans_ids);
776				STAILQ_FOREACH(id, &transport_id_list, links) {
777					bcopy(id->hdr, bufptr, id->alloc_len);
778					bufptr += id->alloc_len;
779				}
780				id_hdr = (struct scsi_per_res_out_trans_ids *)
781				    (res_buf +
782				    sizeof(struct scsi_per_res_out_parms));
783				scsi_ulto4b(id_len, id_hdr->additional_length);
784			}
785		case SPRO_REG_IGNO:
786		case SPRO_PREEMPT:
787		case SPRO_PRE_ABO:
788		case SPRO_RESERVE:
789		case SPRO_RELEASE:
790		case SPRO_CLEAR:
791		case SPRO_REPL_LOST_RES: {
792			struct scsi_per_res_out_parms *parms;
793
794			parms = (struct scsi_per_res_out_parms *)res_buf;
795
796			scsi_u64to8b(key, parms->res_key.key);
797			scsi_u64to8b(sa_key, parms->serv_act_res_key);
798			if (spec_i_pt != 0)
799				parms->flags |= SPR_SPEC_I_PT;
800			if (all_tg_pt != 0)
801				parms->flags |= SPR_ALL_TG_PT;
802			if (aptpl != 0)
803				parms->flags |= SPR_APTPL;
804			break;
805		}
806		case SPRO_REG_MOVE: {
807			struct scsi_per_res_reg_move *reg_move;
808			uint8_t *bufptr;
809
810			reg_move = (struct scsi_per_res_reg_move *)res_buf;
811
812			scsi_u64to8b(key, reg_move->res_key.key);
813			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
814			if (unreg != 0)
815				reg_move->flags |= SPR_REG_MOVE_UNREG;
816			if (aptpl != 0)
817				reg_move->flags |= SPR_REG_MOVE_APTPL;
818			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
819			id = STAILQ_FIRST(&transport_id_list);
820			/*
821			 * This shouldn't happen, since we already checked
822			 * the number of IDs above.
823			 */
824			if (id == NULL) {
825				warnx("%s: No transport IDs found!", __func__);
826				error = 1;
827				goto bailout;
828			}
829			bufptr = (uint8_t *)&reg_move[1];
830			bcopy(id->hdr, bufptr, id->alloc_len);
831			scsi_ulto4b(id->alloc_len,
832			    reg_move->transport_id_length);
833			break;
834		}
835		default:
836			break;
837		}
838		scsi_persistent_reserve_out(&ccb->csio,
839					    /*retries*/ retry_count,
840					    /*cbfcnp*/ NULL,
841					    /*tag_action*/ MSG_SIMPLE_Q_TAG,
842					    /*service_action*/ action,
843					    /*scope*/ scope,
844					    /*res_type*/ res_type,
845					    /*data_ptr*/ res_buf,
846					    /*dxfer_len*/ res_len,
847					    /*sense_len*/ SSD_FULL_SIZE,
848					    /*timeout*/ timeout ?timeout :5000);
849	}
850
851	/* Disable freezing the device queue */
852	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
853
854	if (err_recover != 0)
855		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
856
857	if (cam_send_ccb(device, ccb) < 0) {
858		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
859		    "IN" : "OUT");
860
861		if (verbosemode != 0) {
862			cam_error_print(device, ccb, CAM_ESF_ALL,
863					CAM_EPF_ALL, stderr);
864		}
865
866		error = 1;
867		goto bailout;
868	}
869
870	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
871		if (verbosemode != 0) {
872			cam_error_print(device, ccb, CAM_ESF_ALL,
873					CAM_EPF_ALL, stderr);
874		}
875		error = 1;
876		goto bailout;
877	}
878
879	if (in == 0)
880		goto bailout;
881
882	valid_len = res_len - ccb->csio.resid;
883
884	switch (action) {
885	case SPRI_RK:
886	case SPRI_RR:
887	case SPRI_RS: {
888		struct scsi_per_res_in_header *hdr;
889		uint32_t hdr_len;
890
891		if (valid_len < sizeof(*hdr)) {
892			warnx("%s: only got %d valid bytes, need %zd",
893			      __func__, valid_len, sizeof(*hdr));
894			error = 1;
895			goto bailout;
896		}
897		hdr = (struct scsi_per_res_in_header *)res_buf;
898		hdr_len = scsi_4btoul(hdr->length);
899
900		if (hdr_len > (res_len - sizeof(*hdr))) {
901			res_len = hdr_len + sizeof(*hdr);
902			goto retry;
903		}
904
905		if (action == SPRI_RK) {
906			persist_print_keys(hdr, valid_len);
907		} else if (action == SPRI_RR) {
908			persist_print_res(hdr, valid_len);
909		} else {
910			persist_print_full(hdr, valid_len);
911		}
912		break;
913	}
914	case SPRI_RC: {
915		struct scsi_per_res_cap *cap;
916		uint32_t cap_len;
917
918		if (valid_len < sizeof(*cap)) {
919			warnx("%s: only got %u valid bytes, need %zd",
920			      __func__, valid_len, sizeof(*cap));
921			error = 1;
922			goto bailout;
923		}
924		cap = (struct scsi_per_res_cap *)res_buf;
925		cap_len = scsi_2btoul(cap->length);
926		if (cap_len != sizeof(*cap)) {
927			/*
928			 * We should be able to deal with this,
929			 * it's just more trouble.
930			 */
931			warnx("%s: reported size %u is different "
932			    "than expected size %zd", __func__,
933			    cap_len, sizeof(*cap));
934		}
935
936		/*
937		 * If there is more data available, grab it all,
938		 * even though we don't really know what to do with
939		 * the extra data since it obviously wasn't in the
940		 * spec when this code was written.
941		 */
942		if (cap_len > res_len) {
943			res_len = cap_len;
944			goto retry;
945		}
946		persist_print_cap(cap, valid_len);
947		break;
948	}
949	default:
950		break;
951	}
952
953bailout:
954	free(res_buf);
955
956	if (ccb != NULL)
957		cam_freeccb(ccb);
958
959	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
960		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
961		    links);
962		free(id);
963	}
964	return (error);
965}
966