[Midnightbsd-cvs] src [11239] trunk/sbin/camcontrol: add missing files

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun Jul 1 17:17:32 EDT 2018


Revision: 11239
          http://svnweb.midnightbsd.org/src/?rev=11239
Author:   laffer1
Date:     2018-07-01 17:17:31 -0400 (Sun, 01 Jul 2018)
Log Message:
-----------
add missing files

Added Paths:
-----------
    trunk/sbin/camcontrol/attrib.c
    trunk/sbin/camcontrol/persist.c

Added: trunk/sbin/camcontrol/attrib.c
===================================================================
--- trunk/sbin/camcontrol/attrib.c	                        (rev 0)
+++ trunk/sbin/camcontrol/attrib.c	2018-07-01 21:17:31 UTC (rev 11239)
@@ -0,0 +1,510 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Ken Merry           (Spectra Logic Corporation)
+ */
+/*
+ * SCSI Read and Write Attribute support for camcontrol(8).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/attrib.c 314221 2017-02-24 20:51:39Z ken $");
+
+#include <sys/ioctl.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+#include <sys/chio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+#include <locale.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_pass.h>
+#include <cam/scsi/scsi_ch.h>
+#include <cam/scsi/scsi_message.h>
+#include <camlib.h>
+#include "camcontrol.h"
+
+#if 0
+struct scsi_attr_desc {
+	int attr_id;
+	
+	STAILQ_ENTRY(scsi_attr_desc) links;
+};
+#endif
+
+static struct scsi_nv elem_type_map[] = {
+	{ "all", ELEMENT_TYPE_ALL },
+	{ "picker", ELEMENT_TYPE_MT },
+	{ "slot", ELEMENT_TYPE_ST },
+	{ "portal", ELEMENT_TYPE_IE },
+	{ "drive", ELEMENT_TYPE_DT },
+};
+
+static struct scsi_nv sa_map[] = {
+	{ "attr_values", SRA_SA_ATTR_VALUES },
+	{ "attr_list", SRA_SA_ATTR_LIST },
+	{ "lv_list", SRA_SA_LOG_VOL_LIST },
+	{ "part_list", SRA_SA_PART_LIST },
+	{ "supp_attr", SRA_SA_SUPPORTED_ATTRS }
+};
+
+static struct scsi_nv output_format_map[] = {
+	{ "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
+	{ "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
+	{ "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
+	{ "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
+	{ "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
+	{ "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
+	{ "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
+	{ "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
+	{ "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
+	{ "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
+	{ "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
+};
+
+int
+scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
+	   int task_attr, int retry_count, int timeout, int verbosemode,
+	   int err_recover)
+{
+	union ccb *ccb = NULL;
+	int attr_num = -1;
+#if 0
+	int num_attrs = 0;
+#endif
+	int start_attr = 0;
+	int cached_attr = 0;
+	int read_service_action = -1;
+	int read_attr = 0, write_attr = 0;
+	int element_address = 0;
+	int element_type = ELEMENT_TYPE_ALL;
+	int partition = 0;
+	int logical_volume = 0;
+	char *endptr;
+	uint8_t *data_buf = NULL;
+	uint32_t dxfer_len = UINT16_MAX - 1;
+	uint32_t valid_len;
+	uint32_t output_format;
+	STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
+	int error = 0;
+	int c;
+
+	ccb = cam_getccb(device);
+	if (ccb == NULL) {
+		warnx("%s: error allocating CCB", __func__);
+		error = 1;
+		goto bailout;
+	}
+
+	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+	STAILQ_INIT(&write_attr_list);
+
+	/*
+	 * By default, when displaying attribute values, we trim out
+	 * non-ASCII characters in ASCII fields.  We display all fields
+	 * (description, attribute number, attribute size, and readonly
+	 * status).  We default to displaying raw text.
+	 *
+	 * XXX KDM need to port this to stable/10 and newer FreeBSD
+	 * versions that have iconv built in and can convert codesets.
+	 */
+	output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
+			SCSI_ATTR_OUTPUT_FIELD_ALL | 
+			SCSI_ATTR_OUTPUT_TEXT_RAW;
+
+	data_buf = malloc(dxfer_len);
+	if (data_buf == NULL) {
+		warn("%s: error allocating %u bytes", __func__, dxfer_len);
+		error = 1;
+		goto bailout;
+	}
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch (c) {
+		case 'a':
+			attr_num = strtol(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid attribute number %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			start_attr = attr_num;
+			break;
+		case 'c':
+			cached_attr = 1;
+			break;
+		case 'e':
+			element_address = strtol(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid element address %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		case 'F': {
+			scsi_nv_status status;
+			scsi_attrib_output_flags new_outflags;
+			int entry_num = 0;
+			char *tmpstr;
+
+			if (isdigit(optarg[0])) {
+				output_format = strtoul(optarg, &endptr, 0); 
+				if (*endptr != '\0') {
+					warnx("%s: invalid numeric output "
+					    "format argument %s", __func__,
+					    optarg);
+					error = 1;
+					goto bailout;
+				}
+				break;
+			}
+			new_outflags = SCSI_ATTR_OUTPUT_NONE;
+
+			while ((tmpstr = strsep(&optarg, ",")) != NULL) {
+				status = scsi_get_nv(output_format_map,
+				    sizeof(output_format_map) /
+				    sizeof(output_format_map[0]), tmpstr,
+				    &entry_num, SCSI_NV_FLAG_IG_CASE);
+
+				if (status == SCSI_NV_FOUND)
+					new_outflags |=
+					    output_format_map[entry_num].value;
+				else {
+					warnx("%s: %s format option %s",
+					    __func__,
+					    (status == SCSI_NV_AMBIGUOUS) ?
+					    "ambiguous" : "invalid", tmpstr);
+					error = 1;
+					goto bailout;
+				}
+			}
+			output_format = new_outflags;
+			break;
+		}
+		case 'p':
+			partition = strtol(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid partition number %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		case 'r': {
+			scsi_nv_status status;
+			int entry_num = 0;
+
+			status = scsi_get_nv(sa_map, sizeof(sa_map) /
+			    sizeof(sa_map[0]), optarg, &entry_num,
+			    SCSI_NV_FLAG_IG_CASE);
+			if (status == SCSI_NV_FOUND)
+				read_service_action = sa_map[entry_num].value;
+			else {
+				warnx("%s: %s %s option %s", __func__,
+				    (status == SCSI_NV_AMBIGUOUS) ?
+				    "ambiguous" : "invalid", "service action",
+				    optarg);
+				error = 1;
+				goto bailout;
+			}
+			read_attr = 1;
+			break;
+		}
+		case 's':
+			start_attr = strtol(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid starting attr argument %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		case 'T': {
+			scsi_nv_status status;
+			int entry_num = 0;
+
+			status = scsi_get_nv(elem_type_map,
+			    sizeof(elem_type_map) / sizeof(elem_type_map[0]),
+			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
+			if (status == SCSI_NV_FOUND)
+				element_type = elem_type_map[entry_num].value;
+			else {
+				warnx("%s: %s %s option %s", __func__,
+				    (status == SCSI_NV_AMBIGUOUS) ?
+				    "ambiguous" : "invalid", "element type",
+				    optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		}
+		case 'w':
+			warnx("%s: writing attributes is not implemented yet",
+			      __func__);
+			error = 1;
+			goto bailout;
+			break;
+		case 'V':
+			logical_volume = strtol(optarg, &endptr, 0);
+
+			if (*endptr != '\0') {
+				warnx("%s: invalid logical volume argument %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * Default to reading attributes 
+	 */
+	if (((read_attr == 0) && (write_attr == 0))
+	 || ((read_attr != 0) && (write_attr != 0))) {
+		warnx("%s: Must specify either -r or -w", __func__);
+		error = 1;
+		goto bailout;
+	}
+
+	if (read_attr != 0) {
+		scsi_read_attribute(&ccb->csio,
+				    /*retries*/ retry_count,
+				    /*cbfcnp*/ NULL,
+				    /*tag_action*/ task_attr,
+				    /*service_action*/ read_service_action,
+				    /*element*/ element_address,
+				    /*elem_type*/ element_type,
+				    /*logical_volume*/ logical_volume,
+				    /*partition*/ partition,
+				    /*first_attribute*/ start_attr,
+				    /*cache*/ cached_attr,
+				    /*data_ptr*/ data_buf,
+				    /*length*/ dxfer_len,
+			            /*sense_len*/ SSD_FULL_SIZE,
+				    /*timeout*/ timeout ? timeout : 60000);
+#if 0
+	} else {
+#endif
+
+	}
+
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+	if (err_recover != 0)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+	if (cam_send_ccb(device, ccb) < 0) {
+		warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
+		    "READ" : "WRITE");
+
+		if (verbosemode != 0) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+
+		error = 1;
+		goto bailout;
+	}
+
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		if (verbosemode != 0) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+		error = 1;
+		goto bailout;
+	}
+
+	if (read_attr == 0)
+		goto bailout;
+
+	valid_len = dxfer_len - ccb->csio.resid;
+
+	switch (read_service_action) {
+	case SRA_SA_ATTR_VALUES: {
+		uint32_t len_left, hdr_len, cur_len;
+		struct scsi_read_attribute_values *hdr;
+		struct scsi_mam_attribute_header *cur_id;
+		char error_str[512];
+		uint8_t *cur_pos;
+		struct sbuf *sb;
+
+		hdr = (struct scsi_read_attribute_values *)data_buf;
+
+		if (valid_len < sizeof(*hdr)) {
+			fprintf(stdout, "No attributes returned.\n");
+			error = 0;
+			goto bailout;
+		}
+
+		sb = sbuf_new_auto();
+		if (sb == NULL) {
+			warn("%s: Unable to allocate sbuf", __func__);
+			error = 1;
+			goto bailout;
+		}
+		/*
+		 * XXX KDM grab more data if it is available.
+		 */
+		hdr_len = scsi_4btoul(hdr->length);
+
+		for (len_left = MIN(valid_len, hdr_len),
+		     cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
+		     len_left -= cur_len, cur_pos += cur_len) {
+			int cur_attr_num;
+			cur_id = (struct scsi_mam_attribute_header *)cur_pos;
+			cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
+			cur_attr_num = scsi_2btoul(cur_id->id);
+
+			if ((attr_num != -1)
+			 && (cur_attr_num != attr_num))
+				continue;
+
+			error = scsi_attrib_sbuf(sb, cur_id, len_left,
+			    /*user_table*/ NULL, /*num_user_entries*/ 0,
+			    /*prefer_user_table*/ 0, output_format, error_str,
+			    sizeof(error_str));
+			if (error != 0) {
+				warnx("%s: %s", __func__, error_str);
+				sbuf_delete(sb);
+				error = 1;
+				goto bailout;
+			}
+			if (attr_num != -1)
+				break;
+		}
+
+		sbuf_finish(sb);
+		fprintf(stdout, "%s", sbuf_data(sb));
+		sbuf_delete(sb);
+		break;
+	}
+	case SRA_SA_SUPPORTED_ATTRS:
+	case SRA_SA_ATTR_LIST: {
+		uint32_t len_left, hdr_len;
+		struct scsi_attrib_list_header *hdr;
+		struct scsi_attrib_table_entry *entry = NULL;
+		const char *sa_name = "Supported Attributes";
+		const char *at_name = "Available Attributes";
+		int attr_id;
+		uint8_t *cur_id;
+
+		hdr = (struct scsi_attrib_list_header *)data_buf;
+		if (valid_len < sizeof(*hdr)) {
+			fprintf(stdout, "No %s\n",
+				(read_service_action == SRA_SA_SUPPORTED_ATTRS)?
+				 sa_name : at_name);
+			error = 0;
+			goto bailout;
+		}
+		fprintf(stdout, "%s:\n",
+			(read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
+			 sa_name : at_name);
+		hdr_len = scsi_4btoul(hdr->length);
+		for (len_left = MIN(valid_len, hdr_len),
+		     cur_id = &hdr->first_attr_0[0]; len_left > 1;
+		     len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
+			attr_id = scsi_2btoul(cur_id);
+
+			if ((attr_num != -1)
+			 && (attr_id != attr_num))
+				continue;
+
+			entry = scsi_get_attrib_entry(attr_id);
+			fprintf(stdout, "0x%.4x", attr_id);
+			if (entry == NULL)
+				fprintf(stdout, "\n");
+			else
+				fprintf(stdout, ": %s\n", entry->desc);
+
+			if (attr_num != -1)
+				break;
+		}
+		break;
+	}
+	case SRA_SA_PART_LIST:
+	case SRA_SA_LOG_VOL_LIST: {
+		struct scsi_attrib_lv_list *lv_list;
+		const char *partition_name = "Partition";
+		const char *lv_name = "Logical Volume";
+
+		if (valid_len < sizeof(*lv_list)) {
+			fprintf(stdout, "No %s list returned\n",
+				(read_service_action == SRA_SA_PART_LIST) ?
+				partition_name : lv_name);
+			error = 0;
+			goto bailout;
+		}
+
+		lv_list = (struct scsi_attrib_lv_list *)data_buf;
+
+		fprintf(stdout, "First %s: %d\n",
+			(read_service_action == SRA_SA_PART_LIST) ?
+			partition_name : lv_name,
+			lv_list->first_lv_number);
+		fprintf(stdout, "Number of %ss: %d\n",
+			(read_service_action == SRA_SA_PART_LIST) ?
+			partition_name : lv_name,
+			lv_list->num_logical_volumes);
+		break;
+	}
+	default:
+		break;
+	}
+bailout:
+	if (ccb != NULL)
+		cam_freeccb(ccb);
+
+	free(data_buf);
+
+	return (error);
+}


Property changes on: trunk/sbin/camcontrol/attrib.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sbin/camcontrol/persist.c
===================================================================
--- trunk/sbin/camcontrol/persist.c	                        (rev 0)
+++ trunk/sbin/camcontrol/persist.c	2018-07-01 21:17:31 UTC (rev 11239)
@@ -0,0 +1,967 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Ken Merry           (Spectra Logic Corporation)
+ */
+/*
+ * SCSI Persistent Reservation support for camcontrol(8).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/persist.c 314221 2017-02-24 20:51:39Z ken $");
+
+#include <sys/ioctl.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_pass.h>
+#include <cam/scsi/scsi_message.h>
+#include <camlib.h>
+#include "camcontrol.h"
+
+struct persist_transport_id {
+	struct scsi_transportid_header *hdr;
+	unsigned int alloc_len;
+	STAILQ_ENTRY(persist_transport_id) links;
+};
+
+/*
+ * Service Actions for PERSISTENT RESERVE IN.
+ */
+static struct scsi_nv persist_in_actions[] = {
+	{ "read_keys", SPRI_RK },
+	{ "read_reservation", SPRI_RR },
+	{ "report_capabilities", SPRI_RC },
+	{ "read_full_status", SPRI_RS }
+};
+
+/*
+ * Service Actions for PERSISTENT RESERVE OUT.
+ */
+static struct scsi_nv persist_out_actions[] = {
+	{ "register", SPRO_REGISTER },
+	{ "reserve", SPRO_RESERVE },
+	{ "release" , SPRO_RELEASE },
+	{ "clear", SPRO_CLEAR },
+	{ "preempt", SPRO_PREEMPT },
+	{ "preempt_abort", SPRO_PRE_ABO },
+	{ "register_ignore", SPRO_REG_IGNO },
+	{ "register_move", SPRO_REG_MOVE },
+	{ "replace_lost", SPRO_REPL_LOST_RES }
+};
+
+/*
+ * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
+ * spec.  The others are obsolete.
+ */
+static struct scsi_nv persist_scope_table[] = {
+	{ "lun", SPR_LU_SCOPE },
+	{ "extent", SPR_EXTENT_SCOPE },
+	{ "element", SPR_ELEMENT_SCOPE }
+};
+
+/*
+ * Reservation types.  The longer name for a given reservation type is
+ * listed first, so that it makes more sense when we print out the
+ * reservation type.  We step through the table linearly when looking for
+ * the text name for a particular numeric reservation type value.
+ */
+static struct scsi_nv persist_type_table[] = {
+	{ "read_shared", SPR_TYPE_RD_SHARED },
+	{ "write_exclusive", SPR_TYPE_WR_EX },
+	{ "wr_ex", SPR_TYPE_WR_EX },
+	{ "read_exclusive", SPR_TYPE_RD_EX },
+	{ "rd_ex", SPR_TYPE_RD_EX },
+	{ "exclusive_access", SPR_TYPE_EX_AC },
+	{ "ex_ac", SPR_TYPE_EX_AC },
+	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
+	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
+	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
+	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
+	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
+	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
+	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
+	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
+};
+
+/*
+ * Print out the standard scope/type field.
+ */
+static void
+persist_print_scopetype(uint8_t scopetype)
+{
+	const char *tmpstr;
+	int num_entries;
+
+	num_entries = sizeof(persist_scope_table) /
+		      sizeof(persist_scope_table[0]);
+	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
+				scopetype & SPR_SCOPE_MASK);
+	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
+		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
+
+	num_entries = sizeof(persist_type_table) /
+		      sizeof(persist_type_table[0]);
+	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
+				scopetype & SPR_TYPE_MASK);
+	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
+		"Unknown", scopetype & SPR_TYPE_MASK);
+}
+
+static void
+persist_print_transportid(uint8_t *buf, uint32_t len)
+{
+	struct sbuf *sb;
+
+	sb = sbuf_new_auto();
+	if (sb == NULL)
+		fprintf(stderr, "Unable to allocate sbuf\n");
+
+	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
+
+	sbuf_finish(sb);
+
+	fprintf(stdout, "%s\n", sbuf_data(sb));
+
+	sbuf_delete(sb);
+}
+
+/*
+ * Print out a persistent reservation.  This is used with the READ
+ * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
+ */
+static void
+persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
+{
+	uint32_t length;
+	struct scsi_per_res_in_rsrv *res;
+
+	length = scsi_4btoul(hdr->length);
+	length = MIN(length, valid_len);
+
+	res = (struct scsi_per_res_in_rsrv *)hdr;
+
+	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
+		if (length == 0)
+			fprintf(stdout, "No reservations.\n");
+		else
+			warnx("unable to print reservation, only got %u "
+			      "valid bytes", length);
+		return;
+	}
+	fprintf(stdout, "PRgeneration: %#x\n",
+		scsi_4btoul(res->header.generation));
+	fprintf(stdout, "Reservation Key: %#jx\n",
+		(uintmax_t)scsi_8btou64(res->data.reservation));
+	fprintf(stdout, "Scope address: %#x\n",
+		scsi_4btoul(res->data.scope_addr));
+
+	persist_print_scopetype(res->data.scopetype);
+
+	fprintf(stdout, "Extent length: %u\n",
+		scsi_2btoul(res->data.extent_length));
+}
+
+/*
+ * Print out persistent reservation keys.  This is used with the READ KEYS
+ * service action of the PERSISTENT RESERVE IN command.
+ */
+static void
+persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
+{
+	uint32_t length, num_keys, i;
+	struct scsi_per_res_key *key;
+
+	length = scsi_4btoul(hdr->length);
+	length = MIN(length, valid_len);
+
+	num_keys = length / sizeof(*key);
+
+	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
+	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
+		(num_keys == 0) ? "." : ":");
+
+	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
+	     i++, key++) {
+		fprintf(stdout, "%u: %#jx\n", i,
+			(uintmax_t)scsi_8btou64(key->key));
+	}
+}
+
+/*
+ * Print out persistent reservation capabilities.  This is used with the
+ * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
+ */
+static void
+persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
+{
+	uint32_t length;
+	int check_type_mask = 0;
+
+	length = scsi_2btoul(cap->length);
+	length = MIN(length, valid_len);
+
+	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
+		fprintf(stdout, "Insufficient data (%u bytes) to report "
+			"full capabilities\n", length);
+		return;
+	}
+	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
+		check_type_mask = 1;
+	
+	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
+		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
+	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
+		(cap->flags1 & SPRI_CRH) ? 1 : 0);
+	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
+		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
+	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
+		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
+	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
+		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
+	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
+		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
+	/*
+	 * These cases are cut-and-pasted from SPC4r36l.  There is no
+	 * succinct way to describe these otherwise, and even with the
+	 * verbose description, the user will probably have to refer to
+	 * the spec to fully understand what is going on.
+	 */
+	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
+	case SPRI_ALLOW_1:
+		fprintf(stdout,
+"    The device server allows the TEST UNIT READY command through Write\n"
+"    Exclusive type reservations and Exclusive Access type reservations\n"
+"    and does not provide information about whether the following commands\n"
+"    are allowed through Write Exclusive type reservations:\n"
+"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
+"           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
+"           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
+"           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
+"        b) the READ DEFECT DATA command (see SBC-3).\n");
+		break;
+	case SPRI_ALLOW_2:
+		fprintf(stdout,
+"    The device server allows the TEST UNIT READY command through Write\n"
+"    Exclusive type reservations and Exclusive Access type reservations\n"
+"    and does not allow the following commands through Write Exclusive type\n"
+"    reservations:\n"
+"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
+"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
+"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
+"           FUNCTION command; and\n"
+"        b) the READ DEFECT DATA command.\n"
+"    The device server does not allow the RECEIVE COPY RESULTS command\n"
+"    through Write Exclusive type reservations or Exclusive Access type\n"
+"    reservations.\n");
+		break;
+	case SPRI_ALLOW_3:
+		fprintf(stdout,
+"    The device server allows the TEST UNIT READY command through Write\n"
+"    Exclusive type reservations and Exclusive Access type reservations\n"
+"    and allows the following commands through Write Exclusive type\n"
+"    reservations:\n"
+"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
+"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
+"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
+"           FUNCTION command; and\n"
+"        b) the READ DEFECT DATA command.\n"
+"    The device server does not allow the RECEIVE COPY RESULTS command\n"
+"    through Write Exclusive type reservations or Exclusive Access type\n"
+"    reservations.\n");
+		break;
+	case SPRI_ALLOW_4:
+		fprintf(stdout,
+"    The device server allows the TEST UNIT READY command and the RECEIVE\n"
+"    COPY RESULTS command through Write Exclusive type reservations and\n"
+"    Exclusive Access type reservations and allows the following commands\n"
+"    through Write Exclusive type reservations:\n"
+"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
+"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
+"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
+"           FUNCTION command; and\n"
+"        b) the READ DEFECT DATA command.\n");
+		break;
+	case SPRI_ALLOW_NA:
+		fprintf(stdout,
+"    No information is provided about whether certain commands are allowed\n"
+"    through certain types of persistent reservations.\n");
+		break;
+	default:
+		fprintf(stdout,
+"    Unknown ALLOW COMMANDS value %#x\n",
+			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
+			SPRI_ALLOW_CMD_SHIFT);
+		break;
+	}
+	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
+		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
+	if ((check_type_mask != 0)
+	 && (cap->flags2 & SPRI_TMV)) {
+		fprintf(stdout, "Supported Persistent Reservation Types:\n");
+		fprintf(stdout, "    Write Exclusive - All Registrants "
+			"(WR_EX_AR): %d\n",
+			(cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0);
+		fprintf(stdout, "    Exclusive Access - Registrants Only "
+			"(EX_AC_RO): %d\n",
+			(cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0);
+		fprintf(stdout, "    Write Exclusive - Registrants Only "
+			"(WR_EX_RO): %d\n",
+			(cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0);
+		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
+			(cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0);
+		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
+			(cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0);
+		fprintf(stdout, "    Exclusive Access - All Registrants "
+			"(EX_AC_AR): %d\n",
+			(cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0);
+	} else {
+		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
+			"valid\n");
+	}
+
+	
+}
+
+static void
+persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
+{
+	uint32_t length, len_to_go = 0;
+	struct scsi_per_res_in_full_desc *desc;
+	uint8_t *cur_pos;
+	int i;
+
+	length = scsi_4btoul(hdr->length);
+	length = MIN(length, valid_len);
+
+	if (length < sizeof(*desc)) {
+		if (length == 0)
+			fprintf(stdout, "No reservations.\n");
+		else
+			warnx("unable to print reservation, only got %u "
+			      "valid bytes", length);
+		return;
+	}
+
+	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
+	cur_pos = (uint8_t *)&hdr[1];
+	for (len_to_go = length, i = 0,
+	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
+	     len_to_go >= sizeof(*desc);
+	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
+		uint32_t additional_length, cur_length;
+
+
+		fprintf(stdout, "Reservation Key: %#jx\n",
+			(uintmax_t)scsi_8btou64(desc->res_key.key));
+		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
+			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
+		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
+			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
+		
+		if (desc->flags & SPRI_FULL_R_HOLDER)
+			persist_print_scopetype(desc->scopetype);
+
+		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
+			fprintf(stdout, "Relative Target Port ID: %#x\n",
+				scsi_2btoul(desc->rel_trgt_port_id));
+
+		additional_length = scsi_4btoul(desc->additional_length);
+
+		persist_print_transportid(desc->transport_id,
+					  additional_length);
+
+		cur_length = sizeof(*desc) + additional_length;
+		len_to_go -= cur_length;
+		cur_pos += cur_length;
+	}
+}
+
+int
+scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
+	    int task_attr, int retry_count, int timeout, int verbosemode,
+	    int err_recover)
+{
+	union ccb *ccb = NULL;
+	int c, in = 0, out = 0;
+	int action = -1, num_ids = 0;
+	int error = 0;
+	uint32_t res_len = 0;
+	unsigned long rel_tgt_port = 0;
+	uint8_t *res_buf = NULL;
+	int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 0;
+	struct persist_transport_id *id, *id2;
+	STAILQ_HEAD(, persist_transport_id) transport_id_list;
+	uint64_t key = 0, sa_key = 0;
+	struct scsi_nv *table = NULL;
+	size_t table_size = 0, id_len = 0;
+	uint32_t valid_len = 0;
+	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
+
+	STAILQ_INIT(&transport_id_list);
+
+	ccb = cam_getccb(device);
+	if (ccb == NULL) {
+		warnx("%s: error allocating CCB", __func__);
+		error = 1;
+		goto bailout;
+	}
+
+	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch (c) {
+		case 'a':
+			all_tg_pt = 1;
+			break;
+		case 'I': {
+			int error_str_len = 128;
+			char error_str[error_str_len];
+			char *id_str;
+
+			id = malloc(sizeof(*id));
+			if (id == NULL) {
+				warnx("%s: error allocating %zu bytes",
+				    __func__, sizeof(*id));
+				error = 1;
+				goto bailout;
+			}
+			bzero(id, sizeof(*id));
+
+			id_str = strdup(optarg);
+			if (id_str == NULL) {
+				warnx("%s: error duplicating string %s",
+				    __func__, optarg);
+				free(id);
+				error = 1;
+				goto bailout;
+			}
+			error = scsi_parse_transportid(id_str, &id->hdr,
+			    &id->alloc_len, error_str, error_str_len);
+			if (error != 0) {
+				warnx("%s", error_str);
+				error = 1;
+				free(id);
+				free(id_str);
+				goto bailout;
+			}
+			free(id_str);
+
+			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
+			num_ids++;
+			id_len += id->alloc_len;
+			break;
+		}
+		case 'k': 
+		case 'K': {
+			char *endptr;
+			uint64_t tmpval;
+
+			tmpval = strtoumax(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid key argument %s", __func__,
+				    optarg);
+				error = 1;
+				goto bailout;
+			}
+			if (c == 'k') {
+				key = tmpval;
+				key_set = 1;
+			} else {
+				sa_key = tmpval;
+				sa_key_set = 1;
+			}
+			break;
+		}
+		case 'i':
+		case 'o': {
+			scsi_nv_status status;
+			int table_entry = 0;
+
+			if (c == 'i') {
+				in = 1;
+				table = persist_in_actions;
+				table_size = sizeof(persist_in_actions) /
+					sizeof(persist_in_actions[0]);
+			} else {
+				out = 1;
+				table = persist_out_actions;
+				table_size = sizeof(persist_out_actions) /
+					sizeof(persist_out_actions[0]);
+			}
+
+			if ((in + out) > 1) {
+				warnx("%s: only one in (-i) or out (-o) "
+				    "action is allowed", __func__);
+				error = 1;
+				goto bailout;
+			}
+
+			status = scsi_get_nv(table, table_size, optarg,
+					     &table_entry,SCSI_NV_FLAG_IG_CASE);
+			if (status == SCSI_NV_FOUND)
+				action = table[table_entry].value;
+			else {
+				warnx("%s: %s %s option %s", __func__,
+				    (status == SCSI_NV_AMBIGUOUS) ?
+				    "ambiguous" : "invalid", in ? "in" :
+				    "out", optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		}
+		case 'p':
+			aptpl = 1;
+			break;
+		case 'R': {
+			char *endptr;
+
+			rel_tgt_port = strtoul(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				warnx("%s: invalid relative target port %s",
+				    __func__, optarg);
+				error = 1;
+				goto bailout;
+			}
+			rel_port_set = 1;
+			break;
+		}
+		case 's': {
+			size_t scope_size;
+			struct scsi_nv *scope_table = NULL;
+			scsi_nv_status status;
+			int table_entry = 0;
+			char *endptr;
+
+			/*
+			 * First check to see if the user gave us a numeric
+			 * argument.  If so, we'll try using it.
+			 */
+			if (isdigit(optarg[0])) {
+				scope = strtol(optarg, &endptr, 0);
+				if (*endptr != '\0') {
+					warnx("%s: invalid scope %s",
+					       __func__, optarg);
+					error = 1;
+					goto bailout;
+				}
+				scope = (scope << SPR_SCOPE_SHIFT) &
+				    SPR_SCOPE_MASK;
+				break;
+			}
+
+			scope_size = sizeof(persist_scope_table) /
+				     sizeof(persist_scope_table[0]);
+			scope_table = persist_scope_table;
+			status = scsi_get_nv(scope_table, scope_size, optarg,
+					     &table_entry,SCSI_NV_FLAG_IG_CASE);
+			if (status == SCSI_NV_FOUND)
+				scope = scope_table[table_entry].value;
+			else {
+				warnx("%s: %s scope %s", __func__,
+				      (status == SCSI_NV_AMBIGUOUS) ?
+				      "ambiguous" : "invalid", optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		}
+		case 'S':
+			spec_i_pt = 1;
+			break;
+		case 'T': {
+			size_t res_type_size;
+			struct scsi_nv *rtype_table = NULL;
+			scsi_nv_status status;
+			char *endptr;
+			int table_entry = 0;
+
+			/*
+			 * First check to see if the user gave us a numeric
+			 * argument.  If so, we'll try using it.
+			 */
+			if (isdigit(optarg[0])) {
+				res_type = strtol(optarg, &endptr, 0);
+				if (*endptr != '\0') {
+					warnx("%s: invalid reservation type %s",
+					       __func__, optarg);
+					error = 1;
+					goto bailout;
+				}
+				break;
+			}
+
+			res_type_size = sizeof(persist_type_table) /
+					sizeof(persist_type_table[0]);
+			rtype_table = persist_type_table;
+			status = scsi_get_nv(rtype_table, res_type_size,
+					     optarg, &table_entry,
+					     SCSI_NV_FLAG_IG_CASE);
+			if (status == SCSI_NV_FOUND)
+				res_type = rtype_table[table_entry].value;
+			else {
+				warnx("%s: %s reservation type %s", __func__,
+				      (status == SCSI_NV_AMBIGUOUS) ?
+				      "ambiguous" : "invalid", optarg);
+				error = 1;
+				goto bailout;
+			}
+			break;
+		}
+		case 'U':
+			unreg = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if ((in + out) != 1) {
+		warnx("%s: you must specify one of -i or -o", __func__);
+		error = 1;
+		goto bailout;
+	}
+
+	/*
+	 * Note that we don't really try to figure out whether the user
+	 * needs to specify one or both keys.  There are a number of
+	 * scenarios, and sometimes 0 is a valid and desired value.
+	 */
+	if (in != 0) {
+		switch (action) {
+		case SPRI_RK:
+		case SPRI_RR:
+		case SPRI_RS:
+			/*
+			 * Allocate the maximum length possible for these
+			 * service actions.  According to the spec, the
+			 * target is supposed to return the available
+			 * length in the header, regardless of the
+			 * allocation length.  In practice, though, with
+			 * the READ FULL STATUS (SPRI_RS) service action,
+			 * some Seagate drives (in particular a
+			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
+			 * don't return the available length if you only
+			 * allocate the length of the header.  So just
+			 * allocate the maximum here so we don't miss
+			 * anything.
+			 */
+			res_len = SPRI_MAX_LEN;
+			break;
+		case SPRI_RC:
+			res_len = sizeof(struct scsi_per_res_cap);
+			break;
+		default:
+			/* In theory we should catch this above */
+			warnx("%s: invalid action %d", __func__, action);
+			error = 1;
+			goto bailout;
+			break;
+		}
+	} else {
+
+		/*
+		 * XXX KDM need to add length for transport IDs for the
+		 * register and move service action and the register
+		 * service action with the SPEC_I_PT bit set.
+		 */
+		if (action == SPRO_REG_MOVE) {
+			if (num_ids != 1) {
+			    	warnx("%s: register and move requires a "
+				    "single transport ID (-I)", __func__);
+				error = 1;
+				goto bailout;
+			}
+			if (rel_port_set == 0) {
+				warnx("%s: register and move requires a "
+				    "relative target port (-R)", __func__);
+				error = 1;
+				goto bailout;
+			}
+			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
+		} else {
+			res_len = sizeof(struct scsi_per_res_out_parms);
+			if ((action == SPRO_REGISTER)
+			 && (num_ids != 0)) {
+				/*
+				 * If the user specifies any IDs with the
+				 * register service action, turn on the
+				 * spec_i_pt bit.
+				 */
+				spec_i_pt = 1;
+				res_len += id_len;
+				res_len +=
+				    sizeof(struct scsi_per_res_out_trans_ids);
+			}
+		}
+	}
+retry:
+	if (res_buf != NULL) {
+		free(res_buf);
+		res_buf = NULL;
+	}
+	res_buf = malloc(res_len);
+	if (res_buf == NULL) {
+		warn("%s: error allocating %d bytes", __func__, res_len);
+		error = 1;
+		goto bailout;
+	}
+	bzero(res_buf, res_len);
+
+	if (in != 0) {
+		scsi_persistent_reserve_in(&ccb->csio,
+					   /*retries*/ retry_count,
+					   /*cbfcnp*/ NULL,
+					   /*tag_action*/ task_attr,
+					   /*service_action*/ action,
+					   /*data_ptr*/ res_buf,
+					   /*dxfer_len*/ res_len,
+					   /*sense_len*/ SSD_FULL_SIZE,
+					   /*timeout*/ timeout ? timeout :5000);
+
+	} else {
+		switch (action) {
+		case SPRO_REGISTER:
+			if (spec_i_pt != 0) {
+				struct scsi_per_res_out_trans_ids *id_hdr;
+				uint8_t *bufptr;
+
+				bufptr = res_buf +
+				    sizeof(struct scsi_per_res_out_parms) +
+				    sizeof(struct scsi_per_res_out_trans_ids);
+				STAILQ_FOREACH(id, &transport_id_list, links) {
+					bcopy(id->hdr, bufptr, id->alloc_len);
+					bufptr += id->alloc_len;
+				}
+				id_hdr = (struct scsi_per_res_out_trans_ids *)
+				    (res_buf +
+				    sizeof(struct scsi_per_res_out_parms));
+				scsi_ulto4b(id_len, id_hdr->additional_length);
+			}
+		case SPRO_REG_IGNO:
+		case SPRO_PREEMPT:
+		case SPRO_PRE_ABO:
+		case SPRO_RESERVE:
+		case SPRO_RELEASE:
+		case SPRO_CLEAR:
+		case SPRO_REPL_LOST_RES: {
+			struct scsi_per_res_out_parms *parms;
+
+			parms = (struct scsi_per_res_out_parms *)res_buf;
+
+			scsi_u64to8b(key, parms->res_key.key);
+			scsi_u64to8b(sa_key, parms->serv_act_res_key);
+			if (spec_i_pt != 0)
+				parms->flags |= SPR_SPEC_I_PT;
+			if (all_tg_pt != 0)
+				parms->flags |= SPR_ALL_TG_PT;
+			if (aptpl != 0)
+				parms->flags |= SPR_APTPL;
+			break;
+		}
+		case SPRO_REG_MOVE: {
+			struct scsi_per_res_reg_move *reg_move;
+			uint8_t *bufptr;
+
+			reg_move = (struct scsi_per_res_reg_move *)res_buf;
+
+			scsi_u64to8b(key, reg_move->res_key.key);
+			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
+			if (unreg != 0)
+				reg_move->flags |= SPR_REG_MOVE_UNREG;
+			if (aptpl != 0)
+				reg_move->flags |= SPR_REG_MOVE_APTPL;
+			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
+			id = STAILQ_FIRST(&transport_id_list);
+			/*
+			 * This shouldn't happen, since we already checked
+			 * the number of IDs above.
+			 */
+			if (id == NULL) {
+				warnx("%s: No transport IDs found!", __func__);
+				error = 1;
+				goto bailout;
+			}
+			bufptr = (uint8_t *)&reg_move[1];
+			bcopy(id->hdr, bufptr, id->alloc_len);
+			scsi_ulto4b(id->alloc_len,
+			    reg_move->transport_id_length);
+			break;
+		}
+		default:
+			break;
+		}
+		scsi_persistent_reserve_out(&ccb->csio,
+					    /*retries*/ retry_count,
+					    /*cbfcnp*/ NULL,
+					    /*tag_action*/ task_attr,
+					    /*service_action*/ action,
+					    /*scope*/ scope,
+					    /*res_type*/ res_type,
+					    /*data_ptr*/ res_buf,
+					    /*dxfer_len*/ res_len,
+					    /*sense_len*/ SSD_FULL_SIZE,
+					    /*timeout*/ timeout ?timeout :5000);
+	}
+
+	/* Disable freezing the device queue */
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+	if (err_recover != 0)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+	if (cam_send_ccb(device, ccb) < 0) {
+		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
+		    "IN" : "OUT");
+
+		if (verbosemode != 0) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+
+		error = 1;
+		goto bailout;
+	}
+
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		if (verbosemode != 0) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+		error = 1;
+		goto bailout;
+	}
+
+	if (in == 0)
+		goto bailout;
+
+	valid_len = res_len - ccb->csio.resid;
+
+	switch (action) {
+	case SPRI_RK:
+	case SPRI_RR:
+	case SPRI_RS: {
+		struct scsi_per_res_in_header *hdr;
+		uint32_t hdr_len;
+
+		if (valid_len < sizeof(*hdr)) {
+			warnx("%s: only got %d valid bytes, need %zd",
+			      __func__, valid_len, sizeof(*hdr));
+			error = 1;
+			goto bailout;
+		}
+		hdr = (struct scsi_per_res_in_header *)res_buf;
+		hdr_len = scsi_4btoul(hdr->length);
+
+		if (hdr_len > (res_len - sizeof(*hdr))) {
+			res_len = hdr_len + sizeof(*hdr);
+			goto retry;
+		}
+
+		if (action == SPRI_RK) {
+			persist_print_keys(hdr, valid_len);
+		} else if (action == SPRI_RR) {
+			persist_print_res(hdr, valid_len);
+		} else {
+			persist_print_full(hdr, valid_len);
+		}
+		break;
+	}
+	case SPRI_RC: {
+		struct scsi_per_res_cap *cap;
+		uint32_t cap_len;
+
+		if (valid_len < sizeof(*cap)) {
+			warnx("%s: only got %u valid bytes, need %zd",
+			      __func__, valid_len, sizeof(*cap));
+			error = 1;
+			goto bailout;
+		}
+		cap = (struct scsi_per_res_cap *)res_buf;
+		cap_len = scsi_2btoul(cap->length);
+		if (cap_len != sizeof(*cap)) {
+			/*
+			 * We should be able to deal with this,
+			 * it's just more trouble.
+			 */
+			warnx("%s: reported size %u is different "
+			    "than expected size %zd", __func__,
+			    cap_len, sizeof(*cap));
+		}
+
+		/*
+		 * If there is more data available, grab it all,
+		 * even though we don't really know what to do with
+		 * the extra data since it obviously wasn't in the
+		 * spec when this code was written.
+		 */
+		if (cap_len > res_len) {
+			res_len = cap_len;
+			goto retry;
+		}
+		persist_print_cap(cap, valid_len);
+		break;
+	}
+	default:
+		break;
+	}
+
+bailout:
+	free(res_buf);
+
+	if (ccb != NULL)
+		cam_freeccb(ccb);
+
+	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
+		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
+		    links);
+		free(id);
+	}
+	return (error);
+}


Property changes on: trunk/sbin/camcontrol/persist.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list