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 *)®_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