xref: /freebsd/sbin/camcontrol/persist.c (revision 492a2ef5561ee921a972a8fefafc609a071fd54d)
108df2e3eSKenneth D. Merry /*-
208df2e3eSKenneth D. Merry  * Copyright (c) 2013 Spectra Logic Corporation
308df2e3eSKenneth D. Merry  * All rights reserved.
408df2e3eSKenneth D. Merry  *
508df2e3eSKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
608df2e3eSKenneth D. Merry  * modification, are permitted provided that the following conditions
708df2e3eSKenneth D. Merry  * are met:
808df2e3eSKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
908df2e3eSKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
1008df2e3eSKenneth D. Merry  *    without modification.
1108df2e3eSKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1208df2e3eSKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
1308df2e3eSKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
1408df2e3eSKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
1508df2e3eSKenneth D. Merry  *    binary redistribution.
1608df2e3eSKenneth D. Merry  *
1708df2e3eSKenneth D. Merry  * NO WARRANTY
1808df2e3eSKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1908df2e3eSKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2008df2e3eSKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
2108df2e3eSKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2208df2e3eSKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2308df2e3eSKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2408df2e3eSKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2508df2e3eSKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2608df2e3eSKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2708df2e3eSKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2808df2e3eSKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
2908df2e3eSKenneth D. Merry  *
3008df2e3eSKenneth D. Merry  * Authors: Ken Merry           (Spectra Logic Corporation)
3108df2e3eSKenneth D. Merry  */
3208df2e3eSKenneth D. Merry /*
3308df2e3eSKenneth D. Merry  * SCSI Persistent Reservation support for camcontrol(8).
3408df2e3eSKenneth D. Merry  */
3508df2e3eSKenneth D. Merry 
3608df2e3eSKenneth D. Merry #include <sys/cdefs.h>
3708df2e3eSKenneth D. Merry __FBSDID("$FreeBSD$");
3808df2e3eSKenneth D. Merry 
3908df2e3eSKenneth D. Merry #include <sys/ioctl.h>
4008df2e3eSKenneth D. Merry #include <sys/stdint.h>
4108df2e3eSKenneth D. Merry #include <sys/types.h>
4208df2e3eSKenneth D. Merry #include <sys/endian.h>
4308df2e3eSKenneth D. Merry #include <sys/sbuf.h>
4408df2e3eSKenneth D. Merry #include <sys/queue.h>
4508df2e3eSKenneth D. Merry 
4608df2e3eSKenneth D. Merry #include <stdio.h>
4708df2e3eSKenneth D. Merry #include <stdlib.h>
4808df2e3eSKenneth D. Merry #include <inttypes.h>
4908df2e3eSKenneth D. Merry #include <unistd.h>
5008df2e3eSKenneth D. Merry #include <string.h>
5108df2e3eSKenneth D. Merry #include <strings.h>
5208df2e3eSKenneth D. Merry #include <fcntl.h>
5308df2e3eSKenneth D. Merry #include <ctype.h>
5408df2e3eSKenneth D. Merry #include <limits.h>
5508df2e3eSKenneth D. Merry #include <err.h>
5608df2e3eSKenneth D. Merry 
5708df2e3eSKenneth D. Merry #include <cam/cam.h>
5808df2e3eSKenneth D. Merry #include <cam/cam_debug.h>
5908df2e3eSKenneth D. Merry #include <cam/cam_ccb.h>
6008df2e3eSKenneth D. Merry #include <cam/scsi/scsi_all.h>
6108df2e3eSKenneth D. Merry #include <cam/scsi/scsi_pass.h>
6208df2e3eSKenneth D. Merry #include <cam/scsi/scsi_message.h>
6308df2e3eSKenneth D. Merry #include <camlib.h>
6408df2e3eSKenneth D. Merry #include "camcontrol.h"
6508df2e3eSKenneth D. Merry 
6608df2e3eSKenneth D. Merry struct persist_transport_id {
6708df2e3eSKenneth D. Merry 	struct scsi_transportid_header *hdr;
6808df2e3eSKenneth D. Merry 	unsigned int alloc_len;
6908df2e3eSKenneth D. Merry 	STAILQ_ENTRY(persist_transport_id) links;
7008df2e3eSKenneth D. Merry };
7108df2e3eSKenneth D. Merry 
7208df2e3eSKenneth D. Merry /*
7308df2e3eSKenneth D. Merry  * Service Actions for PERSISTENT RESERVE IN.
7408df2e3eSKenneth D. Merry  */
7508df2e3eSKenneth D. Merry static struct scsi_nv persist_in_actions[] = {
7608df2e3eSKenneth D. Merry 	{ "read_keys", SPRI_RK },
7708df2e3eSKenneth D. Merry 	{ "read_reservation", SPRI_RR },
7808df2e3eSKenneth D. Merry 	{ "report_capabilities", SPRI_RC },
7908df2e3eSKenneth D. Merry 	{ "read_full_status", SPRI_RS }
8008df2e3eSKenneth D. Merry };
8108df2e3eSKenneth D. Merry 
8208df2e3eSKenneth D. Merry /*
8308df2e3eSKenneth D. Merry  * Service Actions for PERSISTENT RESERVE OUT.
8408df2e3eSKenneth D. Merry  */
8508df2e3eSKenneth D. Merry static struct scsi_nv persist_out_actions[] = {
8608df2e3eSKenneth D. Merry 	{ "register", SPRO_REGISTER },
8708df2e3eSKenneth D. Merry 	{ "reserve", SPRO_RESERVE },
8808df2e3eSKenneth D. Merry 	{ "release" , SPRO_RELEASE },
8908df2e3eSKenneth D. Merry 	{ "clear", SPRO_CLEAR },
9008df2e3eSKenneth D. Merry 	{ "preempt", SPRO_PREEMPT },
9108df2e3eSKenneth D. Merry 	{ "preempt_abort", SPRO_PRE_ABO },
9208df2e3eSKenneth D. Merry 	{ "register_ignore", SPRO_REG_IGNO },
9308df2e3eSKenneth D. Merry 	{ "register_move", SPRO_REG_MOVE },
9408df2e3eSKenneth D. Merry 	{ "replace_lost", SPRO_REPL_LOST_RES }
9508df2e3eSKenneth D. Merry };
9608df2e3eSKenneth D. Merry 
9708df2e3eSKenneth D. Merry /*
9808df2e3eSKenneth D. Merry  * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
9908df2e3eSKenneth D. Merry  * spec.  The others are obsolete.
10008df2e3eSKenneth D. Merry  */
10108df2e3eSKenneth D. Merry static struct scsi_nv persist_scope_table[] = {
10208df2e3eSKenneth D. Merry 	{ "lun", SPR_LU_SCOPE },
10308df2e3eSKenneth D. Merry 	{ "extent", SPR_EXTENT_SCOPE },
10408df2e3eSKenneth D. Merry 	{ "element", SPR_ELEMENT_SCOPE }
10508df2e3eSKenneth D. Merry };
10608df2e3eSKenneth D. Merry 
10708df2e3eSKenneth D. Merry /*
10808df2e3eSKenneth D. Merry  * Reservation types.  The longer name for a given reservation type is
10908df2e3eSKenneth D. Merry  * listed first, so that it makes more sense when we print out the
11008df2e3eSKenneth D. Merry  * reservation type.  We step through the table linearly when looking for
11108df2e3eSKenneth D. Merry  * the text name for a particular numeric reservation type value.
11208df2e3eSKenneth D. Merry  */
11308df2e3eSKenneth D. Merry static struct scsi_nv persist_type_table[] = {
11408df2e3eSKenneth D. Merry 	{ "read_shared", SPR_TYPE_RD_SHARED },
11508df2e3eSKenneth D. Merry 	{ "write_exclusive", SPR_TYPE_WR_EX },
11608df2e3eSKenneth D. Merry 	{ "wr_ex", SPR_TYPE_WR_EX },
11708df2e3eSKenneth D. Merry 	{ "read_exclusive", SPR_TYPE_RD_EX },
11808df2e3eSKenneth D. Merry 	{ "rd_ex", SPR_TYPE_RD_EX },
11908df2e3eSKenneth D. Merry 	{ "exclusive_access", SPR_TYPE_EX_AC },
12008df2e3eSKenneth D. Merry 	{ "ex_ac", SPR_TYPE_EX_AC },
12108df2e3eSKenneth D. Merry 	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
12208df2e3eSKenneth D. Merry 	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
12308df2e3eSKenneth D. Merry 	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
12408df2e3eSKenneth D. Merry 	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
12508df2e3eSKenneth D. Merry 	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
12608df2e3eSKenneth D. Merry 	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
12708df2e3eSKenneth D. Merry 	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
12808df2e3eSKenneth D. Merry 	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
12908df2e3eSKenneth D. Merry };
13008df2e3eSKenneth D. Merry 
13108df2e3eSKenneth D. Merry /*
13208df2e3eSKenneth D. Merry  * Print out the standard scope/type field.
13308df2e3eSKenneth D. Merry  */
13408df2e3eSKenneth D. Merry static void
13508df2e3eSKenneth D. Merry persist_print_scopetype(uint8_t scopetype)
13608df2e3eSKenneth D. Merry {
13708df2e3eSKenneth D. Merry 	const char *tmpstr;
13808df2e3eSKenneth D. Merry 	int num_entries;
13908df2e3eSKenneth D. Merry 
14008df2e3eSKenneth D. Merry 	num_entries = sizeof(persist_scope_table) /
14108df2e3eSKenneth D. Merry 		      sizeof(persist_scope_table[0]);
14208df2e3eSKenneth D. Merry 	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
14308df2e3eSKenneth D. Merry 				scopetype & SPR_SCOPE_MASK);
14408df2e3eSKenneth D. Merry 	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
14508df2e3eSKenneth D. Merry 		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
14608df2e3eSKenneth D. Merry 
14708df2e3eSKenneth D. Merry 	num_entries = sizeof(persist_type_table) /
14808df2e3eSKenneth D. Merry 		      sizeof(persist_type_table[0]);
14908df2e3eSKenneth D. Merry 	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
15008df2e3eSKenneth D. Merry 				scopetype & SPR_TYPE_MASK);
15108df2e3eSKenneth D. Merry 	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
15208df2e3eSKenneth D. Merry 		"Unknown", scopetype & SPR_TYPE_MASK);
15308df2e3eSKenneth D. Merry }
15408df2e3eSKenneth D. Merry 
15508df2e3eSKenneth D. Merry static void
15608df2e3eSKenneth D. Merry persist_print_transportid(uint8_t *buf, uint32_t len)
15708df2e3eSKenneth D. Merry {
15808df2e3eSKenneth D. Merry 	struct sbuf *sb;
15908df2e3eSKenneth D. Merry 
16008df2e3eSKenneth D. Merry 	sb = sbuf_new_auto();
16108df2e3eSKenneth D. Merry 	if (sb == NULL)
16208df2e3eSKenneth D. Merry 		fprintf(stderr, "Unable to allocate sbuf\n");
16308df2e3eSKenneth D. Merry 
16408df2e3eSKenneth D. Merry 	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
16508df2e3eSKenneth D. Merry 
16608df2e3eSKenneth D. Merry 	sbuf_finish(sb);
16708df2e3eSKenneth D. Merry 
16808df2e3eSKenneth D. Merry 	fprintf(stdout, "%s\n", sbuf_data(sb));
16908df2e3eSKenneth D. Merry 
17008df2e3eSKenneth D. Merry 	sbuf_delete(sb);
17108df2e3eSKenneth D. Merry }
17208df2e3eSKenneth D. Merry 
17308df2e3eSKenneth D. Merry /*
17408df2e3eSKenneth D. Merry  * Print out a persistent reservation.  This is used with the READ
17508df2e3eSKenneth D. Merry  * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
17608df2e3eSKenneth D. Merry  */
17708df2e3eSKenneth D. Merry static void
17808df2e3eSKenneth D. Merry persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
17908df2e3eSKenneth D. Merry {
18008df2e3eSKenneth D. Merry 	uint32_t length;
18108df2e3eSKenneth D. Merry 	struct scsi_per_res_in_rsrv *res;
18208df2e3eSKenneth D. Merry 
18308df2e3eSKenneth D. Merry 	length = scsi_4btoul(hdr->length);
18408df2e3eSKenneth D. Merry 	length = MIN(length, valid_len);
18508df2e3eSKenneth D. Merry 
18608df2e3eSKenneth D. Merry 	res = (struct scsi_per_res_in_rsrv *)hdr;
18708df2e3eSKenneth D. Merry 
18808df2e3eSKenneth D. Merry 	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
18908df2e3eSKenneth D. Merry 		if (length == 0)
19008df2e3eSKenneth D. Merry 			fprintf(stdout, "No reservations.\n");
19108df2e3eSKenneth D. Merry 		else
19208df2e3eSKenneth D. Merry 			warnx("unable to print reservation, only got %u "
19308df2e3eSKenneth D. Merry 			      "valid bytes", length);
19408df2e3eSKenneth D. Merry 		return;
19508df2e3eSKenneth D. Merry 	}
19608df2e3eSKenneth D. Merry 	fprintf(stdout, "PRgeneration: %#x\n",
19708df2e3eSKenneth D. Merry 		scsi_4btoul(res->header.generation));
19808df2e3eSKenneth D. Merry 	fprintf(stdout, "Reservation Key: %#jx\n",
19908df2e3eSKenneth D. Merry 		(uintmax_t)scsi_8btou64(res->data.reservation));
20008df2e3eSKenneth D. Merry 	fprintf(stdout, "Scope address: %#x\n",
20108df2e3eSKenneth D. Merry 		scsi_4btoul(res->data.scope_addr));
20208df2e3eSKenneth D. Merry 
20308df2e3eSKenneth D. Merry 	persist_print_scopetype(res->data.scopetype);
20408df2e3eSKenneth D. Merry 
20508df2e3eSKenneth D. Merry 	fprintf(stdout, "Extent length: %u\n",
20608df2e3eSKenneth D. Merry 		scsi_2btoul(res->data.extent_length));
20708df2e3eSKenneth D. Merry }
20808df2e3eSKenneth D. Merry 
20908df2e3eSKenneth D. Merry /*
21008df2e3eSKenneth D. Merry  * Print out persistent reservation keys.  This is used with the READ KEYS
21108df2e3eSKenneth D. Merry  * service action of the PERSISTENT RESERVE IN command.
21208df2e3eSKenneth D. Merry  */
21308df2e3eSKenneth D. Merry static void
21408df2e3eSKenneth D. Merry persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
21508df2e3eSKenneth D. Merry {
21608df2e3eSKenneth D. Merry 	uint32_t length, num_keys, i;
21708df2e3eSKenneth D. Merry 	struct scsi_per_res_key *key;
21808df2e3eSKenneth D. Merry 
21908df2e3eSKenneth D. Merry 	length = scsi_4btoul(hdr->length);
22008df2e3eSKenneth D. Merry 	length = MIN(length, valid_len);
22108df2e3eSKenneth D. Merry 
22208df2e3eSKenneth D. Merry 	num_keys = length / sizeof(*key);
22308df2e3eSKenneth D. Merry 
22408df2e3eSKenneth D. Merry 	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
22508df2e3eSKenneth D. Merry 	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
22608df2e3eSKenneth D. Merry 		(num_keys == 0) ? "." : ":");
22708df2e3eSKenneth D. Merry 
22808df2e3eSKenneth D. Merry 	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
22908df2e3eSKenneth D. Merry 	     i++, key++) {
23008df2e3eSKenneth D. Merry 		fprintf(stdout, "%u: %#jx\n", i,
23108df2e3eSKenneth D. Merry 			(uintmax_t)scsi_8btou64(key->key));
23208df2e3eSKenneth D. Merry 	}
23308df2e3eSKenneth D. Merry }
23408df2e3eSKenneth D. Merry 
23508df2e3eSKenneth D. Merry /*
23608df2e3eSKenneth D. Merry  * Print out persistent reservation capabilities.  This is used with the
23708df2e3eSKenneth D. Merry  * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
23808df2e3eSKenneth D. Merry  */
23908df2e3eSKenneth D. Merry static void
24008df2e3eSKenneth D. Merry persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
24108df2e3eSKenneth D. Merry {
24208df2e3eSKenneth D. Merry 	uint32_t length;
24308df2e3eSKenneth D. Merry 	int check_type_mask = 0;
24477d9b0efSAlan Somers 	uint32_t type_mask;
24508df2e3eSKenneth D. Merry 
24608df2e3eSKenneth D. Merry 	length = scsi_2btoul(cap->length);
24708df2e3eSKenneth D. Merry 	length = MIN(length, valid_len);
24877d9b0efSAlan Somers 	type_mask = scsi_2btoul(cap->type_mask);
24908df2e3eSKenneth D. Merry 
25008df2e3eSKenneth D. Merry 	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
25108df2e3eSKenneth D. Merry 		fprintf(stdout, "Insufficient data (%u bytes) to report "
25208df2e3eSKenneth D. Merry 			"full capabilities\n", length);
25308df2e3eSKenneth D. Merry 		return;
25408df2e3eSKenneth D. Merry 	}
25508df2e3eSKenneth D. Merry 	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
25608df2e3eSKenneth D. Merry 		check_type_mask = 1;
25708df2e3eSKenneth D. Merry 
25808df2e3eSKenneth D. Merry 	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
25908df2e3eSKenneth D. Merry 		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
26008df2e3eSKenneth D. Merry 	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
26108df2e3eSKenneth D. Merry 		(cap->flags1 & SPRI_CRH) ? 1 : 0);
26208df2e3eSKenneth D. Merry 	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
26308df2e3eSKenneth D. Merry 		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
26408df2e3eSKenneth D. Merry 	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
26508df2e3eSKenneth D. Merry 		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
26608df2e3eSKenneth D. Merry 	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
26708df2e3eSKenneth D. Merry 		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
26808df2e3eSKenneth D. Merry 	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
26908df2e3eSKenneth D. Merry 		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
27008df2e3eSKenneth D. Merry 	/*
27108df2e3eSKenneth D. Merry 	 * These cases are cut-and-pasted from SPC4r36l.  There is no
27208df2e3eSKenneth D. Merry 	 * succinct way to describe these otherwise, and even with the
27308df2e3eSKenneth D. Merry 	 * verbose description, the user will probably have to refer to
27408df2e3eSKenneth D. Merry 	 * the spec to fully understand what is going on.
27508df2e3eSKenneth D. Merry 	 */
27608df2e3eSKenneth D. Merry 	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
27708df2e3eSKenneth D. Merry 	case SPRI_ALLOW_1:
27808df2e3eSKenneth D. Merry 		fprintf(stdout,
27908df2e3eSKenneth D. Merry "    The device server allows the TEST UNIT READY command through Write\n"
28008df2e3eSKenneth D. Merry "    Exclusive type reservations and Exclusive Access type reservations\n"
28108df2e3eSKenneth D. Merry "    and does not provide information about whether the following commands\n"
28208df2e3eSKenneth D. Merry "    are allowed through Write Exclusive type reservations:\n"
28308df2e3eSKenneth D. Merry "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
28408df2e3eSKenneth D. Merry "           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
28508df2e3eSKenneth D. Merry "           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
28608df2e3eSKenneth D. Merry "           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
28708df2e3eSKenneth D. Merry "        b) the READ DEFECT DATA command (see SBC-3).\n");
28808df2e3eSKenneth D. Merry 		break;
28908df2e3eSKenneth D. Merry 	case SPRI_ALLOW_2:
29008df2e3eSKenneth D. Merry 		fprintf(stdout,
29108df2e3eSKenneth D. Merry "    The device server allows the TEST UNIT READY command through Write\n"
29208df2e3eSKenneth D. Merry "    Exclusive type reservations and Exclusive Access type reservations\n"
29308df2e3eSKenneth D. Merry "    and does not allow the following commands through Write Exclusive type\n"
29408df2e3eSKenneth D. Merry "    reservations:\n"
29508df2e3eSKenneth D. Merry "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
29608df2e3eSKenneth D. Merry "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
29708df2e3eSKenneth D. Merry "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
29808df2e3eSKenneth D. Merry "           FUNCTION command; and\n"
29908df2e3eSKenneth D. Merry "        b) the READ DEFECT DATA command.\n"
30008df2e3eSKenneth D. Merry "    The device server does not allow the RECEIVE COPY RESULTS command\n"
30108df2e3eSKenneth D. Merry "    through Write Exclusive type reservations or Exclusive Access type\n"
30208df2e3eSKenneth D. Merry "    reservations.\n");
30308df2e3eSKenneth D. Merry 		break;
30408df2e3eSKenneth D. Merry 	case SPRI_ALLOW_3:
30508df2e3eSKenneth D. Merry 		fprintf(stdout,
30608df2e3eSKenneth D. Merry "    The device server allows the TEST UNIT READY command through Write\n"
30708df2e3eSKenneth D. Merry "    Exclusive type reservations and Exclusive Access type reservations\n"
30808df2e3eSKenneth D. Merry "    and allows the following commands through Write Exclusive type\n"
30908df2e3eSKenneth D. Merry "    reservations:\n"
31008df2e3eSKenneth D. Merry "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
31108df2e3eSKenneth D. Merry "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
31208df2e3eSKenneth D. Merry "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
31308df2e3eSKenneth D. Merry "           FUNCTION command; and\n"
31408df2e3eSKenneth D. Merry "        b) the READ DEFECT DATA command.\n"
31508df2e3eSKenneth D. Merry "    The device server does not allow the RECEIVE COPY RESULTS command\n"
31608df2e3eSKenneth D. Merry "    through Write Exclusive type reservations or Exclusive Access type\n"
31708df2e3eSKenneth D. Merry "    reservations.\n");
31808df2e3eSKenneth D. Merry 		break;
31908df2e3eSKenneth D. Merry 	case SPRI_ALLOW_4:
32008df2e3eSKenneth D. Merry 		fprintf(stdout,
32108df2e3eSKenneth D. Merry "    The device server allows the TEST UNIT READY command and the RECEIVE\n"
32208df2e3eSKenneth D. Merry "    COPY RESULTS command through Write Exclusive type reservations and\n"
32308df2e3eSKenneth D. Merry "    Exclusive Access type reservations and allows the following commands\n"
32408df2e3eSKenneth D. Merry "    through Write Exclusive type reservations:\n"
32508df2e3eSKenneth D. Merry "        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
32608df2e3eSKenneth D. Merry "           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
32708df2e3eSKenneth D. Merry "           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
32808df2e3eSKenneth D. Merry "           FUNCTION command; and\n"
32908df2e3eSKenneth D. Merry "        b) the READ DEFECT DATA command.\n");
33008df2e3eSKenneth D. Merry 		break;
33108df2e3eSKenneth D. Merry 	case SPRI_ALLOW_NA:
33208df2e3eSKenneth D. Merry 		fprintf(stdout,
33308df2e3eSKenneth D. Merry "    No information is provided about whether certain commands are allowed\n"
33408df2e3eSKenneth D. Merry "    through certain types of persistent reservations.\n");
33508df2e3eSKenneth D. Merry 		break;
33608df2e3eSKenneth D. Merry 	default:
33708df2e3eSKenneth D. Merry 		fprintf(stdout,
33808df2e3eSKenneth D. Merry "    Unknown ALLOW COMMANDS value %#x\n",
33908df2e3eSKenneth D. Merry 			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
34008df2e3eSKenneth D. Merry 			SPRI_ALLOW_CMD_SHIFT);
34108df2e3eSKenneth D. Merry 		break;
34208df2e3eSKenneth D. Merry 	}
34308df2e3eSKenneth D. Merry 	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
34408df2e3eSKenneth D. Merry 		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
34508df2e3eSKenneth D. Merry 	if ((check_type_mask != 0)
34608df2e3eSKenneth D. Merry 	 && (cap->flags2 & SPRI_TMV)) {
34708df2e3eSKenneth D. Merry 		fprintf(stdout, "Supported Persistent Reservation Types:\n");
34808df2e3eSKenneth D. Merry 		fprintf(stdout, "    Write Exclusive - All Registrants "
34908df2e3eSKenneth D. Merry 			"(WR_EX_AR): %d\n",
35077d9b0efSAlan Somers 			(type_mask & SPRI_TM_WR_EX_AR)? 1 : 0);
35108df2e3eSKenneth D. Merry 		fprintf(stdout, "    Exclusive Access - Registrants Only "
35208df2e3eSKenneth D. Merry 			"(EX_AC_RO): %d\n",
35377d9b0efSAlan Somers 			(type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0);
35408df2e3eSKenneth D. Merry 		fprintf(stdout, "    Write Exclusive - Registrants Only "
35508df2e3eSKenneth D. Merry 			"(WR_EX_RO): %d\n",
35677d9b0efSAlan Somers 			(type_mask & SPRI_TM_WR_EX_RO)? 1 : 0);
35708df2e3eSKenneth D. Merry 		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
35877d9b0efSAlan Somers 			(type_mask & SPRI_TM_EX_AC) ? 1 : 0);
35908df2e3eSKenneth D. Merry 		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
36077d9b0efSAlan Somers 			(type_mask & SPRI_TM_WR_EX) ? 1 : 0);
36108df2e3eSKenneth D. Merry 		fprintf(stdout, "    Exclusive Access - All Registrants "
36208df2e3eSKenneth D. Merry 			"(EX_AC_AR): %d\n",
36377d9b0efSAlan Somers 			(type_mask & SPRI_TM_EX_AC_AR) ? 1 : 0);
36408df2e3eSKenneth D. Merry 	} else {
36508df2e3eSKenneth D. Merry 		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
36608df2e3eSKenneth D. Merry 			"valid\n");
36708df2e3eSKenneth D. Merry 	}
36808df2e3eSKenneth D. Merry 
36908df2e3eSKenneth D. Merry 
37008df2e3eSKenneth D. Merry }
37108df2e3eSKenneth D. Merry 
37208df2e3eSKenneth D. Merry static void
37308df2e3eSKenneth D. Merry persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
37408df2e3eSKenneth D. Merry {
37508df2e3eSKenneth D. Merry 	uint32_t length, len_to_go = 0;
37608df2e3eSKenneth D. Merry 	struct scsi_per_res_in_full_desc *desc;
37708df2e3eSKenneth D. Merry 	uint8_t *cur_pos;
37808df2e3eSKenneth D. Merry 	int i;
37908df2e3eSKenneth D. Merry 
38008df2e3eSKenneth D. Merry 	length = scsi_4btoul(hdr->length);
38108df2e3eSKenneth D. Merry 	length = MIN(length, valid_len);
38208df2e3eSKenneth D. Merry 
38308df2e3eSKenneth D. Merry 	if (length < sizeof(*desc)) {
38408df2e3eSKenneth D. Merry 		if (length == 0)
38508df2e3eSKenneth D. Merry 			fprintf(stdout, "No reservations.\n");
38608df2e3eSKenneth D. Merry 		else
38708df2e3eSKenneth D. Merry 			warnx("unable to print reservation, only got %u "
38808df2e3eSKenneth D. Merry 			      "valid bytes", length);
38908df2e3eSKenneth D. Merry 		return;
39008df2e3eSKenneth D. Merry 	}
39108df2e3eSKenneth D. Merry 
39208df2e3eSKenneth D. Merry 	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
39308df2e3eSKenneth D. Merry 	cur_pos = (uint8_t *)&hdr[1];
39408df2e3eSKenneth D. Merry 	for (len_to_go = length, i = 0,
39508df2e3eSKenneth D. Merry 	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
39608df2e3eSKenneth D. Merry 	     len_to_go >= sizeof(*desc);
39708df2e3eSKenneth D. Merry 	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
39808df2e3eSKenneth D. Merry 		uint32_t additional_length, cur_length;
39908df2e3eSKenneth D. Merry 
40008df2e3eSKenneth D. Merry 
40108df2e3eSKenneth D. Merry 		fprintf(stdout, "Reservation Key: %#jx\n",
40208df2e3eSKenneth D. Merry 			(uintmax_t)scsi_8btou64(desc->res_key.key));
40308df2e3eSKenneth D. Merry 		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
40408df2e3eSKenneth D. Merry 			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
40508df2e3eSKenneth D. Merry 		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
40608df2e3eSKenneth D. Merry 			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
40708df2e3eSKenneth D. Merry 
40808df2e3eSKenneth D. Merry 		if (desc->flags & SPRI_FULL_R_HOLDER)
40908df2e3eSKenneth D. Merry 			persist_print_scopetype(desc->scopetype);
41008df2e3eSKenneth D. Merry 
41108df2e3eSKenneth D. Merry 		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
41208df2e3eSKenneth D. Merry 			fprintf(stdout, "Relative Target Port ID: %#x\n",
41308df2e3eSKenneth D. Merry 				scsi_2btoul(desc->rel_trgt_port_id));
41408df2e3eSKenneth D. Merry 
41508df2e3eSKenneth D. Merry 		additional_length = scsi_4btoul(desc->additional_length);
41608df2e3eSKenneth D. Merry 
41708df2e3eSKenneth D. Merry 		persist_print_transportid(desc->transport_id,
41808df2e3eSKenneth D. Merry 					  additional_length);
41908df2e3eSKenneth D. Merry 
42008df2e3eSKenneth D. Merry 		cur_length = sizeof(*desc) + additional_length;
42108df2e3eSKenneth D. Merry 		len_to_go -= cur_length;
42208df2e3eSKenneth D. Merry 		cur_pos += cur_length;
42308df2e3eSKenneth D. Merry 	}
42408df2e3eSKenneth D. Merry }
42508df2e3eSKenneth D. Merry 
42608df2e3eSKenneth D. Merry int
42708df2e3eSKenneth D. Merry scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
428*492a2ef5SKenneth D. Merry 	    int task_attr, int retry_count, int timeout, int verbosemode,
429*492a2ef5SKenneth D. Merry 	    int err_recover)
43008df2e3eSKenneth D. Merry {
43108df2e3eSKenneth D. Merry 	union ccb *ccb = NULL;
43208df2e3eSKenneth D. Merry 	int c, in = 0, out = 0;
43308df2e3eSKenneth D. Merry 	int action = -1, num_ids = 0;
43408df2e3eSKenneth D. Merry 	int error = 0;
43508df2e3eSKenneth D. Merry 	uint32_t res_len = 0;
43608df2e3eSKenneth D. Merry 	unsigned long rel_tgt_port = 0;
43708df2e3eSKenneth D. Merry 	uint8_t *res_buf = NULL;
4381bfa92baSMarcelo Araujo 	int scope = SPR_LU_SCOPE, res_type = 0;
43908df2e3eSKenneth D. Merry 	struct persist_transport_id *id, *id2;
44008df2e3eSKenneth D. Merry 	STAILQ_HEAD(, persist_transport_id) transport_id_list;
44108df2e3eSKenneth D. Merry 	uint64_t key = 0, sa_key = 0;
44208df2e3eSKenneth D. Merry 	struct scsi_nv *table = NULL;
44308df2e3eSKenneth D. Merry 	size_t table_size = 0, id_len = 0;
44408df2e3eSKenneth D. Merry 	uint32_t valid_len = 0;
44508df2e3eSKenneth D. Merry 	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
44608df2e3eSKenneth D. Merry 
44708df2e3eSKenneth D. Merry 	STAILQ_INIT(&transport_id_list);
44808df2e3eSKenneth D. Merry 
44908df2e3eSKenneth D. Merry 	ccb = cam_getccb(device);
45008df2e3eSKenneth D. Merry 	if (ccb == NULL) {
45108df2e3eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
45208df2e3eSKenneth D. Merry 		error = 1;
45308df2e3eSKenneth D. Merry 		goto bailout;
45408df2e3eSKenneth D. Merry 	}
45508df2e3eSKenneth D. Merry 
45695320aceSDon Lewis 	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
45708df2e3eSKenneth D. Merry 
45808df2e3eSKenneth D. Merry 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
45908df2e3eSKenneth D. Merry 		switch (c) {
46008df2e3eSKenneth D. Merry 		case 'a':
46108df2e3eSKenneth D. Merry 			all_tg_pt = 1;
46208df2e3eSKenneth D. Merry 			break;
46308df2e3eSKenneth D. Merry 		case 'I': {
46408df2e3eSKenneth D. Merry 			int error_str_len = 128;
46508df2e3eSKenneth D. Merry 			char error_str[error_str_len];
46608df2e3eSKenneth D. Merry 			char *id_str;
46708df2e3eSKenneth D. Merry 
46808df2e3eSKenneth D. Merry 			id = malloc(sizeof(*id));
46908df2e3eSKenneth D. Merry 			if (id == NULL) {
47008df2e3eSKenneth D. Merry 				warnx("%s: error allocating %zu bytes",
47108df2e3eSKenneth D. Merry 				    __func__, sizeof(*id));
47208df2e3eSKenneth D. Merry 				error = 1;
47308df2e3eSKenneth D. Merry 				goto bailout;
47408df2e3eSKenneth D. Merry 			}
47508df2e3eSKenneth D. Merry 			bzero(id, sizeof(*id));
47608df2e3eSKenneth D. Merry 
47708df2e3eSKenneth D. Merry 			id_str = strdup(optarg);
47808df2e3eSKenneth D. Merry 			if (id_str == NULL) {
47908df2e3eSKenneth D. Merry 				warnx("%s: error duplicating string %s",
48008df2e3eSKenneth D. Merry 				    __func__, optarg);
48108df2e3eSKenneth D. Merry 				free(id);
48208df2e3eSKenneth D. Merry 				error = 1;
48308df2e3eSKenneth D. Merry 				goto bailout;
48408df2e3eSKenneth D. Merry 			}
48508df2e3eSKenneth D. Merry 			error = scsi_parse_transportid(id_str, &id->hdr,
48608df2e3eSKenneth D. Merry 			    &id->alloc_len, error_str, error_str_len);
48708df2e3eSKenneth D. Merry 			if (error != 0) {
48808df2e3eSKenneth D. Merry 				warnx("%s", error_str);
48908df2e3eSKenneth D. Merry 				error = 1;
49008df2e3eSKenneth D. Merry 				free(id);
49108df2e3eSKenneth D. Merry 				free(id_str);
49208df2e3eSKenneth D. Merry 				goto bailout;
49308df2e3eSKenneth D. Merry 			}
49408df2e3eSKenneth D. Merry 			free(id_str);
49508df2e3eSKenneth D. Merry 
49608df2e3eSKenneth D. Merry 			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
49708df2e3eSKenneth D. Merry 			num_ids++;
49808df2e3eSKenneth D. Merry 			id_len += id->alloc_len;
49908df2e3eSKenneth D. Merry 			break;
50008df2e3eSKenneth D. Merry 		}
50108df2e3eSKenneth D. Merry 		case 'k':
50208df2e3eSKenneth D. Merry 		case 'K': {
50308df2e3eSKenneth D. Merry 			char *endptr;
50408df2e3eSKenneth D. Merry 			uint64_t tmpval;
50508df2e3eSKenneth D. Merry 
50608df2e3eSKenneth D. Merry 			tmpval = strtoumax(optarg, &endptr, 0);
50708df2e3eSKenneth D. Merry 			if (*endptr != '\0') {
50808df2e3eSKenneth D. Merry 				warnx("%s: invalid key argument %s", __func__,
50908df2e3eSKenneth D. Merry 				    optarg);
51008df2e3eSKenneth D. Merry 				error = 1;
51108df2e3eSKenneth D. Merry 				goto bailout;
51208df2e3eSKenneth D. Merry 			}
51308df2e3eSKenneth D. Merry 			if (c == 'k') {
51408df2e3eSKenneth D. Merry 				key = tmpval;
51508df2e3eSKenneth D. Merry 			} else {
51608df2e3eSKenneth D. Merry 				sa_key = tmpval;
51708df2e3eSKenneth D. Merry 			}
51808df2e3eSKenneth D. Merry 			break;
51908df2e3eSKenneth D. Merry 		}
52008df2e3eSKenneth D. Merry 		case 'i':
52108df2e3eSKenneth D. Merry 		case 'o': {
52208df2e3eSKenneth D. Merry 			scsi_nv_status status;
52308df2e3eSKenneth D. Merry 			int table_entry = 0;
52408df2e3eSKenneth D. Merry 
52508df2e3eSKenneth D. Merry 			if (c == 'i') {
52608df2e3eSKenneth D. Merry 				in = 1;
52708df2e3eSKenneth D. Merry 				table = persist_in_actions;
52808df2e3eSKenneth D. Merry 				table_size = sizeof(persist_in_actions) /
52908df2e3eSKenneth D. Merry 					sizeof(persist_in_actions[0]);
53008df2e3eSKenneth D. Merry 			} else {
53108df2e3eSKenneth D. Merry 				out = 1;
53208df2e3eSKenneth D. Merry 				table = persist_out_actions;
53308df2e3eSKenneth D. Merry 				table_size = sizeof(persist_out_actions) /
53408df2e3eSKenneth D. Merry 					sizeof(persist_out_actions[0]);
53508df2e3eSKenneth D. Merry 			}
53608df2e3eSKenneth D. Merry 
53708df2e3eSKenneth D. Merry 			if ((in + out) > 1) {
53808df2e3eSKenneth D. Merry 				warnx("%s: only one in (-i) or out (-o) "
53908df2e3eSKenneth D. Merry 				    "action is allowed", __func__);
54008df2e3eSKenneth D. Merry 				error = 1;
54108df2e3eSKenneth D. Merry 				goto bailout;
54208df2e3eSKenneth D. Merry 			}
54308df2e3eSKenneth D. Merry 
54408df2e3eSKenneth D. Merry 			status = scsi_get_nv(table, table_size, optarg,
54508df2e3eSKenneth D. Merry 					     &table_entry,SCSI_NV_FLAG_IG_CASE);
54608df2e3eSKenneth D. Merry 			if (status == SCSI_NV_FOUND)
54708df2e3eSKenneth D. Merry 				action = table[table_entry].value;
54808df2e3eSKenneth D. Merry 			else {
54908df2e3eSKenneth D. Merry 				warnx("%s: %s %s option %s", __func__,
55008df2e3eSKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
55108df2e3eSKenneth D. Merry 				    "ambiguous" : "invalid", in ? "in" :
55208df2e3eSKenneth D. Merry 				    "out", optarg);
55308df2e3eSKenneth D. Merry 				error = 1;
55408df2e3eSKenneth D. Merry 				goto bailout;
55508df2e3eSKenneth D. Merry 			}
55608df2e3eSKenneth D. Merry 			break;
55708df2e3eSKenneth D. Merry 		}
55808df2e3eSKenneth D. Merry 		case 'p':
55908df2e3eSKenneth D. Merry 			aptpl = 1;
56008df2e3eSKenneth D. Merry 			break;
56108df2e3eSKenneth D. Merry 		case 'R': {
56208df2e3eSKenneth D. Merry 			char *endptr;
56308df2e3eSKenneth D. Merry 
56408df2e3eSKenneth D. Merry 			rel_tgt_port = strtoul(optarg, &endptr, 0);
56508df2e3eSKenneth D. Merry 			if (*endptr != '\0') {
56608df2e3eSKenneth D. Merry 				warnx("%s: invalid relative target port %s",
56708df2e3eSKenneth D. Merry 				    __func__, optarg);
56808df2e3eSKenneth D. Merry 				error = 1;
56908df2e3eSKenneth D. Merry 				goto bailout;
57008df2e3eSKenneth D. Merry 			}
57108df2e3eSKenneth D. Merry 			rel_port_set = 1;
57208df2e3eSKenneth D. Merry 			break;
57308df2e3eSKenneth D. Merry 		}
57408df2e3eSKenneth D. Merry 		case 's': {
57508df2e3eSKenneth D. Merry 			size_t scope_size;
57608df2e3eSKenneth D. Merry 			struct scsi_nv *scope_table = NULL;
57708df2e3eSKenneth D. Merry 			scsi_nv_status status;
57808df2e3eSKenneth D. Merry 			int table_entry = 0;
57908df2e3eSKenneth D. Merry 			char *endptr;
58008df2e3eSKenneth D. Merry 
58108df2e3eSKenneth D. Merry 			/*
58208df2e3eSKenneth D. Merry 			 * First check to see if the user gave us a numeric
58308df2e3eSKenneth D. Merry 			 * argument.  If so, we'll try using it.
58408df2e3eSKenneth D. Merry 			 */
58508df2e3eSKenneth D. Merry 			if (isdigit(optarg[0])) {
58608df2e3eSKenneth D. Merry 				scope = strtol(optarg, &endptr, 0);
58708df2e3eSKenneth D. Merry 				if (*endptr != '\0') {
58808df2e3eSKenneth D. Merry 					warnx("%s: invalid scope %s",
58908df2e3eSKenneth D. Merry 					       __func__, optarg);
59008df2e3eSKenneth D. Merry 					error = 1;
59108df2e3eSKenneth D. Merry 					goto bailout;
59208df2e3eSKenneth D. Merry 				}
59308df2e3eSKenneth D. Merry 				scope = (scope << SPR_SCOPE_SHIFT) &
59408df2e3eSKenneth D. Merry 				    SPR_SCOPE_MASK;
59508df2e3eSKenneth D. Merry 				break;
59608df2e3eSKenneth D. Merry 			}
59708df2e3eSKenneth D. Merry 
59808df2e3eSKenneth D. Merry 			scope_size = sizeof(persist_scope_table) /
59908df2e3eSKenneth D. Merry 				     sizeof(persist_scope_table[0]);
60008df2e3eSKenneth D. Merry 			scope_table = persist_scope_table;
60108df2e3eSKenneth D. Merry 			status = scsi_get_nv(scope_table, scope_size, optarg,
60208df2e3eSKenneth D. Merry 					     &table_entry,SCSI_NV_FLAG_IG_CASE);
60308df2e3eSKenneth D. Merry 			if (status == SCSI_NV_FOUND)
60408df2e3eSKenneth D. Merry 				scope = scope_table[table_entry].value;
60508df2e3eSKenneth D. Merry 			else {
60608df2e3eSKenneth D. Merry 				warnx("%s: %s scope %s", __func__,
60708df2e3eSKenneth D. Merry 				      (status == SCSI_NV_AMBIGUOUS) ?
60808df2e3eSKenneth D. Merry 				      "ambiguous" : "invalid", optarg);
60908df2e3eSKenneth D. Merry 				error = 1;
61008df2e3eSKenneth D. Merry 				goto bailout;
61108df2e3eSKenneth D. Merry 			}
61208df2e3eSKenneth D. Merry 			break;
61308df2e3eSKenneth D. Merry 		}
61408df2e3eSKenneth D. Merry 		case 'S':
61508df2e3eSKenneth D. Merry 			spec_i_pt = 1;
61608df2e3eSKenneth D. Merry 			break;
61708df2e3eSKenneth D. Merry 		case 'T': {
61808df2e3eSKenneth D. Merry 			size_t res_type_size;
61908df2e3eSKenneth D. Merry 			struct scsi_nv *rtype_table = NULL;
62008df2e3eSKenneth D. Merry 			scsi_nv_status status;
62108df2e3eSKenneth D. Merry 			char *endptr;
62208df2e3eSKenneth D. Merry 			int table_entry = 0;
62308df2e3eSKenneth D. Merry 
62408df2e3eSKenneth D. Merry 			/*
62508df2e3eSKenneth D. Merry 			 * First check to see if the user gave us a numeric
62608df2e3eSKenneth D. Merry 			 * argument.  If so, we'll try using it.
62708df2e3eSKenneth D. Merry 			 */
62808df2e3eSKenneth D. Merry 			if (isdigit(optarg[0])) {
62908df2e3eSKenneth D. Merry 				res_type = strtol(optarg, &endptr, 0);
63008df2e3eSKenneth D. Merry 				if (*endptr != '\0') {
63108df2e3eSKenneth D. Merry 					warnx("%s: invalid reservation type %s",
63208df2e3eSKenneth D. Merry 					       __func__, optarg);
63308df2e3eSKenneth D. Merry 					error = 1;
63408df2e3eSKenneth D. Merry 					goto bailout;
63508df2e3eSKenneth D. Merry 				}
63608df2e3eSKenneth D. Merry 				break;
63708df2e3eSKenneth D. Merry 			}
63808df2e3eSKenneth D. Merry 
63908df2e3eSKenneth D. Merry 			res_type_size = sizeof(persist_type_table) /
64008df2e3eSKenneth D. Merry 					sizeof(persist_type_table[0]);
64108df2e3eSKenneth D. Merry 			rtype_table = persist_type_table;
64208df2e3eSKenneth D. Merry 			status = scsi_get_nv(rtype_table, res_type_size,
64308df2e3eSKenneth D. Merry 					     optarg, &table_entry,
64408df2e3eSKenneth D. Merry 					     SCSI_NV_FLAG_IG_CASE);
64508df2e3eSKenneth D. Merry 			if (status == SCSI_NV_FOUND)
64608df2e3eSKenneth D. Merry 				res_type = rtype_table[table_entry].value;
64708df2e3eSKenneth D. Merry 			else {
64808df2e3eSKenneth D. Merry 				warnx("%s: %s reservation type %s", __func__,
64908df2e3eSKenneth D. Merry 				      (status == SCSI_NV_AMBIGUOUS) ?
65008df2e3eSKenneth D. Merry 				      "ambiguous" : "invalid", optarg);
65108df2e3eSKenneth D. Merry 				error = 1;
65208df2e3eSKenneth D. Merry 				goto bailout;
65308df2e3eSKenneth D. Merry 			}
65408df2e3eSKenneth D. Merry 			break;
65508df2e3eSKenneth D. Merry 		}
65608df2e3eSKenneth D. Merry 		case 'U':
65708df2e3eSKenneth D. Merry 			unreg = 1;
65808df2e3eSKenneth D. Merry 			break;
65908df2e3eSKenneth D. Merry 		default:
66008df2e3eSKenneth D. Merry 			break;
66108df2e3eSKenneth D. Merry 		}
66208df2e3eSKenneth D. Merry 	}
66308df2e3eSKenneth D. Merry 
66408df2e3eSKenneth D. Merry 	if ((in + out) != 1) {
66508df2e3eSKenneth D. Merry 		warnx("%s: you must specify one of -i or -o", __func__);
66608df2e3eSKenneth D. Merry 		error = 1;
66708df2e3eSKenneth D. Merry 		goto bailout;
66808df2e3eSKenneth D. Merry 	}
66908df2e3eSKenneth D. Merry 
67008df2e3eSKenneth D. Merry 	/*
67108df2e3eSKenneth D. Merry 	 * Note that we don't really try to figure out whether the user
67208df2e3eSKenneth D. Merry 	 * needs to specify one or both keys.  There are a number of
67308df2e3eSKenneth D. Merry 	 * scenarios, and sometimes 0 is a valid and desired value.
67408df2e3eSKenneth D. Merry 	 */
67508df2e3eSKenneth D. Merry 	if (in != 0) {
67608df2e3eSKenneth D. Merry 		switch (action) {
67708df2e3eSKenneth D. Merry 		case SPRI_RK:
67808df2e3eSKenneth D. Merry 		case SPRI_RR:
67908df2e3eSKenneth D. Merry 		case SPRI_RS:
68008df2e3eSKenneth D. Merry 			/*
68108df2e3eSKenneth D. Merry 			 * Allocate the maximum length possible for these
68208df2e3eSKenneth D. Merry 			 * service actions.  According to the spec, the
68308df2e3eSKenneth D. Merry 			 * target is supposed to return the available
68408df2e3eSKenneth D. Merry 			 * length in the header, regardless of the
68508df2e3eSKenneth D. Merry 			 * allocation length.  In practice, though, with
68608df2e3eSKenneth D. Merry 			 * the READ FULL STATUS (SPRI_RS) service action,
68708df2e3eSKenneth D. Merry 			 * some Seagate drives (in particular a
68808df2e3eSKenneth D. Merry 			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
68908df2e3eSKenneth D. Merry 			 * don't return the available length if you only
69008df2e3eSKenneth D. Merry 			 * allocate the length of the header.  So just
69108df2e3eSKenneth D. Merry 			 * allocate the maximum here so we don't miss
69208df2e3eSKenneth D. Merry 			 * anything.
69308df2e3eSKenneth D. Merry 			 */
69408df2e3eSKenneth D. Merry 			res_len = SPRI_MAX_LEN;
69508df2e3eSKenneth D. Merry 			break;
69608df2e3eSKenneth D. Merry 		case SPRI_RC:
69708df2e3eSKenneth D. Merry 			res_len = sizeof(struct scsi_per_res_cap);
69808df2e3eSKenneth D. Merry 			break;
69908df2e3eSKenneth D. Merry 		default:
70008df2e3eSKenneth D. Merry 			/* In theory we should catch this above */
70108df2e3eSKenneth D. Merry 			warnx("%s: invalid action %d", __func__, action);
70208df2e3eSKenneth D. Merry 			error = 1;
70308df2e3eSKenneth D. Merry 			goto bailout;
70408df2e3eSKenneth D. Merry 			break;
70508df2e3eSKenneth D. Merry 		}
70608df2e3eSKenneth D. Merry 	} else {
70708df2e3eSKenneth D. Merry 
70808df2e3eSKenneth D. Merry 		/*
70908df2e3eSKenneth D. Merry 		 * XXX KDM need to add length for transport IDs for the
71008df2e3eSKenneth D. Merry 		 * register and move service action and the register
71108df2e3eSKenneth D. Merry 		 * service action with the SPEC_I_PT bit set.
71208df2e3eSKenneth D. Merry 		 */
71308df2e3eSKenneth D. Merry 		if (action == SPRO_REG_MOVE) {
71408df2e3eSKenneth D. Merry 			if (num_ids != 1) {
71508df2e3eSKenneth D. Merry 			    	warnx("%s: register and move requires a "
71608df2e3eSKenneth D. Merry 				    "single transport ID (-I)", __func__);
71708df2e3eSKenneth D. Merry 				error = 1;
71808df2e3eSKenneth D. Merry 				goto bailout;
71908df2e3eSKenneth D. Merry 			}
72008df2e3eSKenneth D. Merry 			if (rel_port_set == 0) {
72108df2e3eSKenneth D. Merry 				warnx("%s: register and move requires a "
72208df2e3eSKenneth D. Merry 				    "relative target port (-R)", __func__);
72308df2e3eSKenneth D. Merry 				error = 1;
72408df2e3eSKenneth D. Merry 				goto bailout;
72508df2e3eSKenneth D. Merry 			}
72608df2e3eSKenneth D. Merry 			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
72708df2e3eSKenneth D. Merry 		} else {
72808df2e3eSKenneth D. Merry 			res_len = sizeof(struct scsi_per_res_out_parms);
72908df2e3eSKenneth D. Merry 			if ((action == SPRO_REGISTER)
73008df2e3eSKenneth D. Merry 			 && (num_ids != 0)) {
73108df2e3eSKenneth D. Merry 				/*
73208df2e3eSKenneth D. Merry 				 * If the user specifies any IDs with the
73308df2e3eSKenneth D. Merry 				 * register service action, turn on the
73408df2e3eSKenneth D. Merry 				 * spec_i_pt bit.
73508df2e3eSKenneth D. Merry 				 */
73608df2e3eSKenneth D. Merry 				spec_i_pt = 1;
73708df2e3eSKenneth D. Merry 				res_len += id_len;
73808df2e3eSKenneth D. Merry 				res_len +=
73908df2e3eSKenneth D. Merry 				    sizeof(struct scsi_per_res_out_trans_ids);
74008df2e3eSKenneth D. Merry 			}
74108df2e3eSKenneth D. Merry 		}
74208df2e3eSKenneth D. Merry 	}
74308df2e3eSKenneth D. Merry retry:
74408df2e3eSKenneth D. Merry 	if (res_buf != NULL) {
74508df2e3eSKenneth D. Merry 		free(res_buf);
74608df2e3eSKenneth D. Merry 		res_buf = NULL;
74708df2e3eSKenneth D. Merry 	}
74808df2e3eSKenneth D. Merry 	res_buf = malloc(res_len);
74908df2e3eSKenneth D. Merry 	if (res_buf == NULL) {
75008df2e3eSKenneth D. Merry 		warn("%s: error allocating %d bytes", __func__, res_len);
75108df2e3eSKenneth D. Merry 		error = 1;
75208df2e3eSKenneth D. Merry 		goto bailout;
75308df2e3eSKenneth D. Merry 	}
75408df2e3eSKenneth D. Merry 	bzero(res_buf, res_len);
75508df2e3eSKenneth D. Merry 
75608df2e3eSKenneth D. Merry 	if (in != 0) {
75708df2e3eSKenneth D. Merry 		scsi_persistent_reserve_in(&ccb->csio,
75808df2e3eSKenneth D. Merry 					   /*retries*/ retry_count,
75908df2e3eSKenneth D. Merry 					   /*cbfcnp*/ NULL,
760*492a2ef5SKenneth D. Merry 					   /*tag_action*/ task_attr,
76108df2e3eSKenneth D. Merry 					   /*service_action*/ action,
76208df2e3eSKenneth D. Merry 					   /*data_ptr*/ res_buf,
76308df2e3eSKenneth D. Merry 					   /*dxfer_len*/ res_len,
76408df2e3eSKenneth D. Merry 					   /*sense_len*/ SSD_FULL_SIZE,
76508df2e3eSKenneth D. Merry 					   /*timeout*/ timeout ? timeout :5000);
76608df2e3eSKenneth D. Merry 
76708df2e3eSKenneth D. Merry 	} else {
76808df2e3eSKenneth D. Merry 		switch (action) {
76908df2e3eSKenneth D. Merry 		case SPRO_REGISTER:
77008df2e3eSKenneth D. Merry 			if (spec_i_pt != 0) {
77108df2e3eSKenneth D. Merry 				struct scsi_per_res_out_trans_ids *id_hdr;
77208df2e3eSKenneth D. Merry 				uint8_t *bufptr;
77308df2e3eSKenneth D. Merry 
77408df2e3eSKenneth D. Merry 				bufptr = res_buf +
77508df2e3eSKenneth D. Merry 				    sizeof(struct scsi_per_res_out_parms) +
77608df2e3eSKenneth D. Merry 				    sizeof(struct scsi_per_res_out_trans_ids);
77708df2e3eSKenneth D. Merry 				STAILQ_FOREACH(id, &transport_id_list, links) {
77808df2e3eSKenneth D. Merry 					bcopy(id->hdr, bufptr, id->alloc_len);
77908df2e3eSKenneth D. Merry 					bufptr += id->alloc_len;
78008df2e3eSKenneth D. Merry 				}
78108df2e3eSKenneth D. Merry 				id_hdr = (struct scsi_per_res_out_trans_ids *)
78208df2e3eSKenneth D. Merry 				    (res_buf +
78308df2e3eSKenneth D. Merry 				    sizeof(struct scsi_per_res_out_parms));
78408df2e3eSKenneth D. Merry 				scsi_ulto4b(id_len, id_hdr->additional_length);
78508df2e3eSKenneth D. Merry 			}
78608df2e3eSKenneth D. Merry 		case SPRO_REG_IGNO:
78708df2e3eSKenneth D. Merry 		case SPRO_PREEMPT:
78808df2e3eSKenneth D. Merry 		case SPRO_PRE_ABO:
78908df2e3eSKenneth D. Merry 		case SPRO_RESERVE:
79008df2e3eSKenneth D. Merry 		case SPRO_RELEASE:
79108df2e3eSKenneth D. Merry 		case SPRO_CLEAR:
79208df2e3eSKenneth D. Merry 		case SPRO_REPL_LOST_RES: {
79308df2e3eSKenneth D. Merry 			struct scsi_per_res_out_parms *parms;
79408df2e3eSKenneth D. Merry 
79508df2e3eSKenneth D. Merry 			parms = (struct scsi_per_res_out_parms *)res_buf;
79608df2e3eSKenneth D. Merry 
79708df2e3eSKenneth D. Merry 			scsi_u64to8b(key, parms->res_key.key);
79808df2e3eSKenneth D. Merry 			scsi_u64to8b(sa_key, parms->serv_act_res_key);
79908df2e3eSKenneth D. Merry 			if (spec_i_pt != 0)
80008df2e3eSKenneth D. Merry 				parms->flags |= SPR_SPEC_I_PT;
80108df2e3eSKenneth D. Merry 			if (all_tg_pt != 0)
80208df2e3eSKenneth D. Merry 				parms->flags |= SPR_ALL_TG_PT;
80308df2e3eSKenneth D. Merry 			if (aptpl != 0)
80408df2e3eSKenneth D. Merry 				parms->flags |= SPR_APTPL;
80508df2e3eSKenneth D. Merry 			break;
80608df2e3eSKenneth D. Merry 		}
80708df2e3eSKenneth D. Merry 		case SPRO_REG_MOVE: {
80808df2e3eSKenneth D. Merry 			struct scsi_per_res_reg_move *reg_move;
80908df2e3eSKenneth D. Merry 			uint8_t *bufptr;
81008df2e3eSKenneth D. Merry 
81108df2e3eSKenneth D. Merry 			reg_move = (struct scsi_per_res_reg_move *)res_buf;
81208df2e3eSKenneth D. Merry 
81308df2e3eSKenneth D. Merry 			scsi_u64to8b(key, reg_move->res_key.key);
81408df2e3eSKenneth D. Merry 			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
81508df2e3eSKenneth D. Merry 			if (unreg != 0)
81608df2e3eSKenneth D. Merry 				reg_move->flags |= SPR_REG_MOVE_UNREG;
81708df2e3eSKenneth D. Merry 			if (aptpl != 0)
81808df2e3eSKenneth D. Merry 				reg_move->flags |= SPR_REG_MOVE_APTPL;
81908df2e3eSKenneth D. Merry 			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
82008df2e3eSKenneth D. Merry 			id = STAILQ_FIRST(&transport_id_list);
82108df2e3eSKenneth D. Merry 			/*
82208df2e3eSKenneth D. Merry 			 * This shouldn't happen, since we already checked
82308df2e3eSKenneth D. Merry 			 * the number of IDs above.
82408df2e3eSKenneth D. Merry 			 */
82508df2e3eSKenneth D. Merry 			if (id == NULL) {
82608df2e3eSKenneth D. Merry 				warnx("%s: No transport IDs found!", __func__);
82708df2e3eSKenneth D. Merry 				error = 1;
82808df2e3eSKenneth D. Merry 				goto bailout;
82908df2e3eSKenneth D. Merry 			}
83008df2e3eSKenneth D. Merry 			bufptr = (uint8_t *)&reg_move[1];
83108df2e3eSKenneth D. Merry 			bcopy(id->hdr, bufptr, id->alloc_len);
83208df2e3eSKenneth D. Merry 			scsi_ulto4b(id->alloc_len,
83308df2e3eSKenneth D. Merry 			    reg_move->transport_id_length);
83408df2e3eSKenneth D. Merry 			break;
83508df2e3eSKenneth D. Merry 		}
83608df2e3eSKenneth D. Merry 		default:
83708df2e3eSKenneth D. Merry 			break;
83808df2e3eSKenneth D. Merry 		}
83908df2e3eSKenneth D. Merry 		scsi_persistent_reserve_out(&ccb->csio,
84008df2e3eSKenneth D. Merry 					    /*retries*/ retry_count,
84108df2e3eSKenneth D. Merry 					    /*cbfcnp*/ NULL,
842*492a2ef5SKenneth D. Merry 					    /*tag_action*/ task_attr,
84308df2e3eSKenneth D. Merry 					    /*service_action*/ action,
84408df2e3eSKenneth D. Merry 					    /*scope*/ scope,
84508df2e3eSKenneth D. Merry 					    /*res_type*/ res_type,
84608df2e3eSKenneth D. Merry 					    /*data_ptr*/ res_buf,
84708df2e3eSKenneth D. Merry 					    /*dxfer_len*/ res_len,
84808df2e3eSKenneth D. Merry 					    /*sense_len*/ SSD_FULL_SIZE,
84908df2e3eSKenneth D. Merry 					    /*timeout*/ timeout ?timeout :5000);
85008df2e3eSKenneth D. Merry 	}
85108df2e3eSKenneth D. Merry 
85208df2e3eSKenneth D. Merry 	/* Disable freezing the device queue */
85308df2e3eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
85408df2e3eSKenneth D. Merry 
85508df2e3eSKenneth D. Merry 	if (err_recover != 0)
85608df2e3eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
85708df2e3eSKenneth D. Merry 
85808df2e3eSKenneth D. Merry 	if (cam_send_ccb(device, ccb) < 0) {
85908df2e3eSKenneth D. Merry 		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
86008df2e3eSKenneth D. Merry 		    "IN" : "OUT");
86108df2e3eSKenneth D. Merry 
86208df2e3eSKenneth D. Merry 		if (verbosemode != 0) {
86308df2e3eSKenneth D. Merry 			cam_error_print(device, ccb, CAM_ESF_ALL,
86408df2e3eSKenneth D. Merry 					CAM_EPF_ALL, stderr);
86508df2e3eSKenneth D. Merry 		}
86608df2e3eSKenneth D. Merry 
86708df2e3eSKenneth D. Merry 		error = 1;
86808df2e3eSKenneth D. Merry 		goto bailout;
86908df2e3eSKenneth D. Merry 	}
87008df2e3eSKenneth D. Merry 
87108df2e3eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
87208df2e3eSKenneth D. Merry 		if (verbosemode != 0) {
87308df2e3eSKenneth D. Merry 			cam_error_print(device, ccb, CAM_ESF_ALL,
87408df2e3eSKenneth D. Merry 					CAM_EPF_ALL, stderr);
87508df2e3eSKenneth D. Merry 		}
87608df2e3eSKenneth D. Merry 		error = 1;
87708df2e3eSKenneth D. Merry 		goto bailout;
87808df2e3eSKenneth D. Merry 	}
87908df2e3eSKenneth D. Merry 
88008df2e3eSKenneth D. Merry 	if (in == 0)
88108df2e3eSKenneth D. Merry 		goto bailout;
88208df2e3eSKenneth D. Merry 
88308df2e3eSKenneth D. Merry 	valid_len = res_len - ccb->csio.resid;
88408df2e3eSKenneth D. Merry 
88508df2e3eSKenneth D. Merry 	switch (action) {
88608df2e3eSKenneth D. Merry 	case SPRI_RK:
88708df2e3eSKenneth D. Merry 	case SPRI_RR:
88808df2e3eSKenneth D. Merry 	case SPRI_RS: {
88908df2e3eSKenneth D. Merry 		struct scsi_per_res_in_header *hdr;
89008df2e3eSKenneth D. Merry 		uint32_t hdr_len;
89108df2e3eSKenneth D. Merry 
89208df2e3eSKenneth D. Merry 		if (valid_len < sizeof(*hdr)) {
89308df2e3eSKenneth D. Merry 			warnx("%s: only got %d valid bytes, need %zd",
89408df2e3eSKenneth D. Merry 			      __func__, valid_len, sizeof(*hdr));
89508df2e3eSKenneth D. Merry 			error = 1;
89608df2e3eSKenneth D. Merry 			goto bailout;
89708df2e3eSKenneth D. Merry 		}
89808df2e3eSKenneth D. Merry 		hdr = (struct scsi_per_res_in_header *)res_buf;
89908df2e3eSKenneth D. Merry 		hdr_len = scsi_4btoul(hdr->length);
90008df2e3eSKenneth D. Merry 
90108df2e3eSKenneth D. Merry 		if (hdr_len > (res_len - sizeof(*hdr))) {
90208df2e3eSKenneth D. Merry 			res_len = hdr_len + sizeof(*hdr);
90308df2e3eSKenneth D. Merry 			goto retry;
90408df2e3eSKenneth D. Merry 		}
90508df2e3eSKenneth D. Merry 
90608df2e3eSKenneth D. Merry 		if (action == SPRI_RK) {
90708df2e3eSKenneth D. Merry 			persist_print_keys(hdr, valid_len);
90808df2e3eSKenneth D. Merry 		} else if (action == SPRI_RR) {
90908df2e3eSKenneth D. Merry 			persist_print_res(hdr, valid_len);
91008df2e3eSKenneth D. Merry 		} else {
91108df2e3eSKenneth D. Merry 			persist_print_full(hdr, valid_len);
91208df2e3eSKenneth D. Merry 		}
91308df2e3eSKenneth D. Merry 		break;
91408df2e3eSKenneth D. Merry 	}
91508df2e3eSKenneth D. Merry 	case SPRI_RC: {
91608df2e3eSKenneth D. Merry 		struct scsi_per_res_cap *cap;
91708df2e3eSKenneth D. Merry 		uint32_t cap_len;
91808df2e3eSKenneth D. Merry 
91908df2e3eSKenneth D. Merry 		if (valid_len < sizeof(*cap)) {
92008df2e3eSKenneth D. Merry 			warnx("%s: only got %u valid bytes, need %zd",
92108df2e3eSKenneth D. Merry 			      __func__, valid_len, sizeof(*cap));
92208df2e3eSKenneth D. Merry 			error = 1;
92308df2e3eSKenneth D. Merry 			goto bailout;
92408df2e3eSKenneth D. Merry 		}
92508df2e3eSKenneth D. Merry 		cap = (struct scsi_per_res_cap *)res_buf;
92608df2e3eSKenneth D. Merry 		cap_len = scsi_2btoul(cap->length);
92708df2e3eSKenneth D. Merry 		if (cap_len != sizeof(*cap)) {
92808df2e3eSKenneth D. Merry 			/*
92908df2e3eSKenneth D. Merry 			 * We should be able to deal with this,
93008df2e3eSKenneth D. Merry 			 * it's just more trouble.
93108df2e3eSKenneth D. Merry 			 */
93208df2e3eSKenneth D. Merry 			warnx("%s: reported size %u is different "
93308df2e3eSKenneth D. Merry 			    "than expected size %zd", __func__,
93408df2e3eSKenneth D. Merry 			    cap_len, sizeof(*cap));
93508df2e3eSKenneth D. Merry 		}
93608df2e3eSKenneth D. Merry 
93708df2e3eSKenneth D. Merry 		/*
93808df2e3eSKenneth D. Merry 		 * If there is more data available, grab it all,
93908df2e3eSKenneth D. Merry 		 * even though we don't really know what to do with
94008df2e3eSKenneth D. Merry 		 * the extra data since it obviously wasn't in the
94108df2e3eSKenneth D. Merry 		 * spec when this code was written.
94208df2e3eSKenneth D. Merry 		 */
94308df2e3eSKenneth D. Merry 		if (cap_len > res_len) {
94408df2e3eSKenneth D. Merry 			res_len = cap_len;
94508df2e3eSKenneth D. Merry 			goto retry;
94608df2e3eSKenneth D. Merry 		}
94708df2e3eSKenneth D. Merry 		persist_print_cap(cap, valid_len);
94808df2e3eSKenneth D. Merry 		break;
94908df2e3eSKenneth D. Merry 	}
95008df2e3eSKenneth D. Merry 	default:
95108df2e3eSKenneth D. Merry 		break;
95208df2e3eSKenneth D. Merry 	}
95308df2e3eSKenneth D. Merry 
95408df2e3eSKenneth D. Merry bailout:
95508df2e3eSKenneth D. Merry 	free(res_buf);
95608df2e3eSKenneth D. Merry 
95708df2e3eSKenneth D. Merry 	if (ccb != NULL)
95808df2e3eSKenneth D. Merry 		cam_freeccb(ccb);
95908df2e3eSKenneth D. Merry 
96008df2e3eSKenneth D. Merry 	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
96108df2e3eSKenneth D. Merry 		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
96208df2e3eSKenneth D. Merry 		    links);
96308df2e3eSKenneth D. Merry 		free(id);
96408df2e3eSKenneth D. Merry 	}
96508df2e3eSKenneth D. Merry 	return (error);
96608df2e3eSKenneth D. Merry }
967