1268240Sken/*- 2268240Sken * Copyright (c) 2013 Spectra Logic Corporation 3268240Sken * All rights reserved. 4268240Sken * 5268240Sken * Redistribution and use in source and binary forms, with or without 6268240Sken * modification, are permitted provided that the following conditions 7268240Sken * are met: 8268240Sken * 1. Redistributions of source code must retain the above copyright 9268240Sken * notice, this list of conditions, and the following disclaimer, 10268240Sken * without modification. 11268240Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12268240Sken * substantially similar to the "NO WARRANTY" disclaimer below 13268240Sken * ("Disclaimer") and any redistribution must be conditioned upon 14268240Sken * including a substantially similar Disclaimer requirement for further 15268240Sken * binary redistribution. 16268240Sken * 17268240Sken * NO WARRANTY 18268240Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19268240Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20268240Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21268240Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22268240Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23268240Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24268240Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25268240Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26268240Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27268240Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28268240Sken * POSSIBILITY OF SUCH DAMAGES. 29268240Sken * 30268240Sken * Authors: Ken Merry (Spectra Logic Corporation) 31268240Sken */ 32268240Sken/* 33268240Sken * SCSI Persistent Reservation support for camcontrol(8). 34268240Sken */ 35268240Sken 36268240Sken#include <sys/cdefs.h> 37268240Sken__FBSDID("$FreeBSD: stable/11/sbin/camcontrol/persist.c 352289 2019-09-13 15:13:21Z mav $"); 38268240Sken 39268240Sken#include <sys/ioctl.h> 40268240Sken#include <sys/stdint.h> 41268240Sken#include <sys/types.h> 42268240Sken#include <sys/endian.h> 43268240Sken#include <sys/sbuf.h> 44268240Sken#include <sys/queue.h> 45268240Sken 46268240Sken#include <stdio.h> 47268240Sken#include <stdlib.h> 48268240Sken#include <inttypes.h> 49268240Sken#include <unistd.h> 50268240Sken#include <string.h> 51268240Sken#include <strings.h> 52268240Sken#include <fcntl.h> 53268240Sken#include <ctype.h> 54268240Sken#include <limits.h> 55268240Sken#include <err.h> 56268240Sken 57268240Sken#include <cam/cam.h> 58268240Sken#include <cam/cam_debug.h> 59268240Sken#include <cam/cam_ccb.h> 60268240Sken#include <cam/scsi/scsi_all.h> 61268240Sken#include <cam/scsi/scsi_pass.h> 62268240Sken#include <cam/scsi/scsi_message.h> 63268240Sken#include <camlib.h> 64268240Sken#include "camcontrol.h" 65268240Sken 66268240Skenstruct persist_transport_id { 67268240Sken struct scsi_transportid_header *hdr; 68268240Sken unsigned int alloc_len; 69268240Sken STAILQ_ENTRY(persist_transport_id) links; 70268240Sken}; 71268240Sken 72268240Sken/* 73268240Sken * Service Actions for PERSISTENT RESERVE IN. 74268240Sken */ 75268240Skenstatic struct scsi_nv persist_in_actions[] = { 76268240Sken { "read_keys", SPRI_RK }, 77268240Sken { "read_reservation", SPRI_RR }, 78268240Sken { "report_capabilities", SPRI_RC }, 79268240Sken { "read_full_status", SPRI_RS } 80268240Sken}; 81268240Sken 82268240Sken/* 83268240Sken * Service Actions for PERSISTENT RESERVE OUT. 84268240Sken */ 85268240Skenstatic struct scsi_nv persist_out_actions[] = { 86268240Sken { "register", SPRO_REGISTER }, 87268240Sken { "reserve", SPRO_RESERVE }, 88268240Sken { "release" , SPRO_RELEASE }, 89268240Sken { "clear", SPRO_CLEAR }, 90268240Sken { "preempt", SPRO_PREEMPT }, 91268240Sken { "preempt_abort", SPRO_PRE_ABO }, 92268240Sken { "register_ignore", SPRO_REG_IGNO }, 93268240Sken { "register_move", SPRO_REG_MOVE }, 94268240Sken { "replace_lost", SPRO_REPL_LOST_RES } 95268240Sken}; 96268240Sken 97268240Sken/* 98268240Sken * Known reservation scopes. As of SPC-4, only LU_SCOPE is used in the 99268240Sken * spec. The others are obsolete. 100268240Sken */ 101268240Skenstatic struct scsi_nv persist_scope_table[] = { 102268240Sken { "lun", SPR_LU_SCOPE }, 103268240Sken { "extent", SPR_EXTENT_SCOPE }, 104268240Sken { "element", SPR_ELEMENT_SCOPE } 105268240Sken}; 106268240Sken 107268240Sken/* 108268240Sken * Reservation types. The longer name for a given reservation type is 109268240Sken * listed first, so that it makes more sense when we print out the 110268240Sken * reservation type. We step through the table linearly when looking for 111268240Sken * the text name for a particular numeric reservation type value. 112268240Sken */ 113268240Skenstatic struct scsi_nv persist_type_table[] = { 114268240Sken { "read_shared", SPR_TYPE_RD_SHARED }, 115268240Sken { "write_exclusive", SPR_TYPE_WR_EX }, 116268240Sken { "wr_ex", SPR_TYPE_WR_EX }, 117268240Sken { "read_exclusive", SPR_TYPE_RD_EX }, 118268240Sken { "rd_ex", SPR_TYPE_RD_EX }, 119268240Sken { "exclusive_access", SPR_TYPE_EX_AC }, 120268240Sken { "ex_ac", SPR_TYPE_EX_AC }, 121268240Sken { "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO }, 122268240Sken { "wr_ex_ro", SPR_TYPE_WR_EX_RO }, 123268240Sken { "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO }, 124268240Sken { "ex_ac_ro", SPR_TYPE_EX_AC_RO }, 125268240Sken { "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR }, 126268240Sken { "wr_ex_ar", SPR_TYPE_WR_EX_AR }, 127268240Sken { "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR }, 128268240Sken { "ex_ac_ar", SPR_TYPE_EX_AC_AR } 129268240Sken}; 130268240Sken 131268240Sken/* 132268240Sken * Print out the standard scope/type field. 133268240Sken */ 134268240Skenstatic void 135268240Skenpersist_print_scopetype(uint8_t scopetype) 136268240Sken{ 137268240Sken const char *tmpstr; 138268240Sken int num_entries; 139268240Sken 140268240Sken num_entries = sizeof(persist_scope_table) / 141268240Sken sizeof(persist_scope_table[0]); 142268240Sken tmpstr = scsi_nv_to_str(persist_scope_table, num_entries, 143268240Sken scopetype & SPR_SCOPE_MASK); 144268240Sken fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr : 145268240Sken "Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT); 146268240Sken 147268240Sken num_entries = sizeof(persist_type_table) / 148268240Sken sizeof(persist_type_table[0]); 149268240Sken tmpstr = scsi_nv_to_str(persist_type_table, num_entries, 150268240Sken scopetype & SPR_TYPE_MASK); 151268240Sken fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr : 152268240Sken "Unknown", scopetype & SPR_TYPE_MASK); 153268240Sken} 154268240Sken 155268240Skenstatic void 156268240Skenpersist_print_transportid(uint8_t *buf, uint32_t len) 157268240Sken{ 158268240Sken struct sbuf *sb; 159268240Sken 160268240Sken sb = sbuf_new_auto(); 161268240Sken if (sb == NULL) 162268240Sken fprintf(stderr, "Unable to allocate sbuf\n"); 163268240Sken 164268240Sken scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len); 165268240Sken 166268240Sken sbuf_finish(sb); 167268240Sken 168268240Sken fprintf(stdout, "%s\n", sbuf_data(sb)); 169268240Sken 170268240Sken sbuf_delete(sb); 171268240Sken} 172268240Sken 173268240Sken/* 174268240Sken * Print out a persistent reservation. This is used with the READ 175268240Sken * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command. 176268240Sken */ 177268240Skenstatic void 178268240Skenpersist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len) 179268240Sken{ 180268240Sken uint32_t length; 181268240Sken struct scsi_per_res_in_rsrv *res; 182268240Sken 183268240Sken length = scsi_4btoul(hdr->length); 184268240Sken length = MIN(length, valid_len); 185268240Sken 186268240Sken res = (struct scsi_per_res_in_rsrv *)hdr; 187268240Sken 188268240Sken if (length < sizeof(res->data) - sizeof(res->data.extent_length)) { 189268240Sken if (length == 0) 190268240Sken fprintf(stdout, "No reservations.\n"); 191268240Sken else 192268240Sken warnx("unable to print reservation, only got %u " 193268240Sken "valid bytes", length); 194268240Sken return; 195268240Sken } 196268240Sken fprintf(stdout, "PRgeneration: %#x\n", 197268240Sken scsi_4btoul(res->header.generation)); 198268240Sken fprintf(stdout, "Reservation Key: %#jx\n", 199268240Sken (uintmax_t)scsi_8btou64(res->data.reservation)); 200268240Sken fprintf(stdout, "Scope address: %#x\n", 201268240Sken scsi_4btoul(res->data.scope_addr)); 202268240Sken 203268240Sken persist_print_scopetype(res->data.scopetype); 204268240Sken 205268240Sken fprintf(stdout, "Extent length: %u\n", 206268240Sken scsi_2btoul(res->data.extent_length)); 207268240Sken} 208268240Sken 209268240Sken/* 210268240Sken * Print out persistent reservation keys. This is used with the READ KEYS 211268240Sken * service action of the PERSISTENT RESERVE IN command. 212268240Sken */ 213268240Skenstatic void 214268240Skenpersist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len) 215268240Sken{ 216268240Sken uint32_t length, num_keys, i; 217268240Sken struct scsi_per_res_key *key; 218268240Sken 219268240Sken length = scsi_4btoul(hdr->length); 220268240Sken length = MIN(length, valid_len); 221268240Sken 222268240Sken num_keys = length / sizeof(*key); 223268240Sken 224268240Sken fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation)); 225268240Sken fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s", 226268240Sken (num_keys == 0) ? "." : ":"); 227268240Sken 228268240Sken for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys; 229268240Sken i++, key++) { 230268240Sken fprintf(stdout, "%u: %#jx\n", i, 231268240Sken (uintmax_t)scsi_8btou64(key->key)); 232268240Sken } 233268240Sken} 234268240Sken 235268240Sken/* 236268240Sken * Print out persistent reservation capabilities. This is used with the 237268240Sken * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command. 238268240Sken */ 239268240Skenstatic void 240268240Skenpersist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len) 241268240Sken{ 242268240Sken uint32_t length; 243268240Sken int check_type_mask = 0; 244317374Sasomers uint32_t type_mask; 245268240Sken 246268240Sken length = scsi_2btoul(cap->length); 247268240Sken length = MIN(length, valid_len); 248317374Sasomers type_mask = scsi_2btoul(cap->type_mask); 249268240Sken 250268240Sken if (length < __offsetof(struct scsi_per_res_cap, type_mask)) { 251268240Sken fprintf(stdout, "Insufficient data (%u bytes) to report " 252268240Sken "full capabilities\n", length); 253268240Sken return; 254268240Sken } 255268240Sken if (length >= __offsetof(struct scsi_per_res_cap, reserved)) 256268240Sken check_type_mask = 1; 257268240Sken 258268240Sken fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n", 259268240Sken (cap->flags1 & SPRI_RLR_C) ? 1 : 0); 260268240Sken fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n", 261268240Sken (cap->flags1 & SPRI_CRH) ? 1 : 0); 262268240Sken fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n", 263268240Sken (cap->flags1 & SPRI_SIP_C) ? 1 : 0); 264268240Sken fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n", 265268240Sken (cap->flags1 & SPRI_ATP_C) ? 1 : 0); 266268240Sken fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n", 267268240Sken (cap->flags1 & SPRI_PTPL_C) ? 1 : 0); 268268240Sken fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n", 269268240Sken (cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT); 270268240Sken /* 271268240Sken * These cases are cut-and-pasted from SPC4r36l. There is no 272268240Sken * succinct way to describe these otherwise, and even with the 273268240Sken * verbose description, the user will probably have to refer to 274268240Sken * the spec to fully understand what is going on. 275268240Sken */ 276268240Sken switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) { 277268240Sken case SPRI_ALLOW_1: 278268240Sken fprintf(stdout, 279268240Sken" The device server allows the TEST UNIT READY command through Write\n" 280268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 281268240Sken" and does not provide information about whether the following commands\n" 282268240Sken" are allowed through Write Exclusive type reservations:\n" 283268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 284268240Sken" command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n" 285268240Sken" RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n" 286268240Sken" and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n" 287268240Sken" b) the READ DEFECT DATA command (see SBC-3).\n"); 288268240Sken break; 289268240Sken case SPRI_ALLOW_2: 290268240Sken fprintf(stdout, 291268240Sken" The device server allows the TEST UNIT READY command through Write\n" 292268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 293268240Sken" and does not allow the following commands through Write Exclusive type\n" 294268240Sken" reservations:\n" 295268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 296268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 297268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 298268240Sken" FUNCTION command; and\n" 299268240Sken" b) the READ DEFECT DATA command.\n" 300268240Sken" The device server does not allow the RECEIVE COPY RESULTS command\n" 301268240Sken" through Write Exclusive type reservations or Exclusive Access type\n" 302268240Sken" reservations.\n"); 303268240Sken break; 304268240Sken case SPRI_ALLOW_3: 305268240Sken fprintf(stdout, 306268240Sken" The device server allows the TEST UNIT READY command through Write\n" 307268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 308268240Sken" and allows the following commands through Write Exclusive type\n" 309268240Sken" reservations:\n" 310268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 311268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 312268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 313268240Sken" FUNCTION command; and\n" 314268240Sken" b) the READ DEFECT DATA command.\n" 315268240Sken" The device server does not allow the RECEIVE COPY RESULTS command\n" 316268240Sken" through Write Exclusive type reservations or Exclusive Access type\n" 317268240Sken" reservations.\n"); 318268240Sken break; 319268240Sken case SPRI_ALLOW_4: 320268240Sken fprintf(stdout, 321268240Sken" The device server allows the TEST UNIT READY command and the RECEIVE\n" 322268240Sken" COPY RESULTS command through Write Exclusive type reservations and\n" 323268240Sken" Exclusive Access type reservations and allows the following commands\n" 324268240Sken" through Write Exclusive type reservations:\n" 325268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 326268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 327268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 328268240Sken" FUNCTION command; and\n" 329268240Sken" b) the READ DEFECT DATA command.\n"); 330268240Sken break; 331268240Sken case SPRI_ALLOW_NA: 332268240Sken fprintf(stdout, 333268240Sken" No information is provided about whether certain commands are allowed\n" 334268240Sken" through certain types of persistent reservations.\n"); 335268240Sken break; 336268240Sken default: 337268240Sken fprintf(stdout, 338268240Sken" Unknown ALLOW COMMANDS value %#x\n", 339268240Sken (cap->flags2 & SPRI_ALLOW_CMD_MASK) >> 340268240Sken SPRI_ALLOW_CMD_SHIFT); 341268240Sken break; 342268240Sken } 343268240Sken fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n", 344268240Sken (cap->flags2 & SPRI_PTPL_A) ? 1 : 0); 345268240Sken if ((check_type_mask != 0) 346268240Sken && (cap->flags2 & SPRI_TMV)) { 347268240Sken fprintf(stdout, "Supported Persistent Reservation Types:\n"); 348268240Sken fprintf(stdout, " Write Exclusive - All Registrants " 349268240Sken "(WR_EX_AR): %d\n", 350317374Sasomers (type_mask & SPRI_TM_WR_EX_AR)? 1 : 0); 351268240Sken fprintf(stdout, " Exclusive Access - Registrants Only " 352268240Sken "(EX_AC_RO): %d\n", 353317374Sasomers (type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0); 354268240Sken fprintf(stdout, " Write Exclusive - Registrants Only " 355268240Sken "(WR_EX_RO): %d\n", 356317374Sasomers (type_mask & SPRI_TM_WR_EX_RO)? 1 : 0); 357268240Sken fprintf(stdout, " Exclusive Access (EX_AC): %d\n", 358317374Sasomers (type_mask & SPRI_TM_EX_AC) ? 1 : 0); 359268240Sken fprintf(stdout, " Write Exclusive (WR_EX): %d\n", 360317374Sasomers (type_mask & SPRI_TM_WR_EX) ? 1 : 0); 361268240Sken fprintf(stdout, " Exclusive Access - All Registrants " 362268240Sken "(EX_AC_AR): %d\n", 363317374Sasomers (type_mask & SPRI_TM_EX_AC_AR) ? 1 : 0); 364268240Sken } else { 365268240Sken fprintf(stdout, "Persistent Reservation Type Mask is NOT " 366268240Sken "valid\n"); 367268240Sken } 368268240Sken 369268240Sken 370268240Sken} 371268240Sken 372268240Skenstatic void 373268240Skenpersist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len) 374268240Sken{ 375268240Sken uint32_t length, len_to_go = 0; 376268240Sken struct scsi_per_res_in_full_desc *desc; 377268240Sken uint8_t *cur_pos; 378268240Sken int i; 379268240Sken 380268240Sken length = scsi_4btoul(hdr->length); 381268240Sken length = MIN(length, valid_len); 382268240Sken 383268240Sken if (length < sizeof(*desc)) { 384268240Sken if (length == 0) 385268240Sken fprintf(stdout, "No reservations.\n"); 386268240Sken else 387268240Sken warnx("unable to print reservation, only got %u " 388268240Sken "valid bytes", length); 389268240Sken return; 390268240Sken } 391268240Sken 392268240Sken fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation)); 393268240Sken cur_pos = (uint8_t *)&hdr[1]; 394268240Sken for (len_to_go = length, i = 0, 395268240Sken desc = (struct scsi_per_res_in_full_desc *)cur_pos; 396268240Sken len_to_go >= sizeof(*desc); 397268240Sken desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) { 398268240Sken uint32_t additional_length, cur_length; 399268240Sken 400268240Sken 401268240Sken fprintf(stdout, "Reservation Key: %#jx\n", 402268240Sken (uintmax_t)scsi_8btou64(desc->res_key.key)); 403268240Sken fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n", 404268240Sken (desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0); 405268240Sken fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n", 406268240Sken (desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0); 407268240Sken 408268240Sken if (desc->flags & SPRI_FULL_R_HOLDER) 409268240Sken persist_print_scopetype(desc->scopetype); 410268240Sken 411268240Sken if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0) 412268240Sken fprintf(stdout, "Relative Target Port ID: %#x\n", 413268240Sken scsi_2btoul(desc->rel_trgt_port_id)); 414268240Sken 415268240Sken additional_length = scsi_4btoul(desc->additional_length); 416268240Sken 417268240Sken persist_print_transportid(desc->transport_id, 418268240Sken additional_length); 419268240Sken 420268240Sken cur_length = sizeof(*desc) + additional_length; 421268240Sken len_to_go -= cur_length; 422268240Sken cur_pos += cur_length; 423268240Sken } 424268240Sken} 425268240Sken 426268240Skenint 427268240Skenscsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt, 428314220Sken int task_attr, int retry_count, int timeout, int verbosemode, 429314220Sken int err_recover) 430268240Sken{ 431268240Sken union ccb *ccb = NULL; 432268240Sken int c, in = 0, out = 0; 433268240Sken int action = -1, num_ids = 0; 434268240Sken int error = 0; 435268240Sken uint32_t res_len = 0; 436268240Sken unsigned long rel_tgt_port = 0; 437268240Sken uint8_t *res_buf = NULL; 438283907Saraujo int scope = SPR_LU_SCOPE, res_type = 0; 439268240Sken struct persist_transport_id *id, *id2; 440268240Sken STAILQ_HEAD(, persist_transport_id) transport_id_list; 441268240Sken uint64_t key = 0, sa_key = 0; 442268240Sken struct scsi_nv *table = NULL; 443268240Sken size_t table_size = 0, id_len = 0; 444268240Sken uint32_t valid_len = 0; 445268240Sken int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0; 446268240Sken 447268240Sken STAILQ_INIT(&transport_id_list); 448268240Sken 449268240Sken ccb = cam_getccb(device); 450268240Sken if (ccb == NULL) { 451268240Sken warnx("%s: error allocating CCB", __func__); 452268240Sken error = 1; 453268240Sken goto bailout; 454268240Sken } 455268240Sken 456300547Struckman CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); 457268240Sken 458268240Sken while ((c = getopt(argc, argv, combinedopt)) != -1) { 459268240Sken switch (c) { 460268240Sken case 'a': 461268240Sken all_tg_pt = 1; 462268240Sken break; 463268240Sken case 'I': { 464268240Sken int error_str_len = 128; 465268240Sken char error_str[error_str_len]; 466268240Sken char *id_str; 467268240Sken 468268240Sken id = malloc(sizeof(*id)); 469268240Sken if (id == NULL) { 470268240Sken warnx("%s: error allocating %zu bytes", 471268240Sken __func__, sizeof(*id)); 472268240Sken error = 1; 473268240Sken goto bailout; 474268240Sken } 475268240Sken bzero(id, sizeof(*id)); 476268240Sken 477268240Sken id_str = strdup(optarg); 478268240Sken if (id_str == NULL) { 479268240Sken warnx("%s: error duplicating string %s", 480268240Sken __func__, optarg); 481268240Sken free(id); 482268240Sken error = 1; 483268240Sken goto bailout; 484268240Sken } 485268240Sken error = scsi_parse_transportid(id_str, &id->hdr, 486268240Sken &id->alloc_len, error_str, error_str_len); 487268240Sken if (error != 0) { 488268240Sken warnx("%s", error_str); 489268240Sken error = 1; 490268240Sken free(id); 491268240Sken free(id_str); 492268240Sken goto bailout; 493268240Sken } 494268240Sken free(id_str); 495268240Sken 496268240Sken STAILQ_INSERT_TAIL(&transport_id_list, id, links); 497268240Sken num_ids++; 498268240Sken id_len += id->alloc_len; 499268240Sken break; 500268240Sken } 501268240Sken case 'k': 502268240Sken case 'K': { 503268240Sken char *endptr; 504268240Sken uint64_t tmpval; 505268240Sken 506268240Sken tmpval = strtoumax(optarg, &endptr, 0); 507268240Sken if (*endptr != '\0') { 508268240Sken warnx("%s: invalid key argument %s", __func__, 509268240Sken optarg); 510268240Sken error = 1; 511268240Sken goto bailout; 512268240Sken } 513268240Sken if (c == 'k') { 514268240Sken key = tmpval; 515268240Sken } else { 516268240Sken sa_key = tmpval; 517268240Sken } 518268240Sken break; 519268240Sken } 520268240Sken case 'i': 521268240Sken case 'o': { 522268240Sken scsi_nv_status status; 523268240Sken int table_entry = 0; 524268240Sken 525268240Sken if (c == 'i') { 526268240Sken in = 1; 527268240Sken table = persist_in_actions; 528268240Sken table_size = sizeof(persist_in_actions) / 529268240Sken sizeof(persist_in_actions[0]); 530268240Sken } else { 531268240Sken out = 1; 532268240Sken table = persist_out_actions; 533268240Sken table_size = sizeof(persist_out_actions) / 534268240Sken sizeof(persist_out_actions[0]); 535268240Sken } 536268240Sken 537268240Sken if ((in + out) > 1) { 538268240Sken warnx("%s: only one in (-i) or out (-o) " 539268240Sken "action is allowed", __func__); 540268240Sken error = 1; 541268240Sken goto bailout; 542268240Sken } 543268240Sken 544268240Sken status = scsi_get_nv(table, table_size, optarg, 545268240Sken &table_entry,SCSI_NV_FLAG_IG_CASE); 546268240Sken if (status == SCSI_NV_FOUND) 547268240Sken action = table[table_entry].value; 548268240Sken else { 549268240Sken warnx("%s: %s %s option %s", __func__, 550268240Sken (status == SCSI_NV_AMBIGUOUS) ? 551268240Sken "ambiguous" : "invalid", in ? "in" : 552268240Sken "out", optarg); 553268240Sken error = 1; 554268240Sken goto bailout; 555268240Sken } 556268240Sken break; 557268240Sken } 558268240Sken case 'p': 559268240Sken aptpl = 1; 560268240Sken break; 561268240Sken case 'R': { 562268240Sken char *endptr; 563268240Sken 564268240Sken rel_tgt_port = strtoul(optarg, &endptr, 0); 565268240Sken if (*endptr != '\0') { 566268240Sken warnx("%s: invalid relative target port %s", 567268240Sken __func__, optarg); 568268240Sken error = 1; 569268240Sken goto bailout; 570268240Sken } 571268240Sken rel_port_set = 1; 572268240Sken break; 573268240Sken } 574268240Sken case 's': { 575268240Sken size_t scope_size; 576268240Sken struct scsi_nv *scope_table = NULL; 577268240Sken scsi_nv_status status; 578268240Sken int table_entry = 0; 579268240Sken char *endptr; 580268240Sken 581268240Sken /* 582268240Sken * First check to see if the user gave us a numeric 583268240Sken * argument. If so, we'll try using it. 584268240Sken */ 585268240Sken if (isdigit(optarg[0])) { 586268240Sken scope = strtol(optarg, &endptr, 0); 587268240Sken if (*endptr != '\0') { 588268240Sken warnx("%s: invalid scope %s", 589268240Sken __func__, optarg); 590268240Sken error = 1; 591268240Sken goto bailout; 592268240Sken } 593268240Sken scope = (scope << SPR_SCOPE_SHIFT) & 594268240Sken SPR_SCOPE_MASK; 595268240Sken break; 596268240Sken } 597268240Sken 598268240Sken scope_size = sizeof(persist_scope_table) / 599268240Sken sizeof(persist_scope_table[0]); 600268240Sken scope_table = persist_scope_table; 601268240Sken status = scsi_get_nv(scope_table, scope_size, optarg, 602268240Sken &table_entry,SCSI_NV_FLAG_IG_CASE); 603268240Sken if (status == SCSI_NV_FOUND) 604268240Sken scope = scope_table[table_entry].value; 605268240Sken else { 606268240Sken warnx("%s: %s scope %s", __func__, 607268240Sken (status == SCSI_NV_AMBIGUOUS) ? 608268240Sken "ambiguous" : "invalid", optarg); 609268240Sken error = 1; 610268240Sken goto bailout; 611268240Sken } 612268240Sken break; 613268240Sken } 614268240Sken case 'S': 615268240Sken spec_i_pt = 1; 616268240Sken break; 617268240Sken case 'T': { 618268240Sken size_t res_type_size; 619268240Sken struct scsi_nv *rtype_table = NULL; 620268240Sken scsi_nv_status status; 621268240Sken char *endptr; 622268240Sken int table_entry = 0; 623268240Sken 624268240Sken /* 625268240Sken * First check to see if the user gave us a numeric 626268240Sken * argument. If so, we'll try using it. 627268240Sken */ 628268240Sken if (isdigit(optarg[0])) { 629268240Sken res_type = strtol(optarg, &endptr, 0); 630268240Sken if (*endptr != '\0') { 631268240Sken warnx("%s: invalid reservation type %s", 632268240Sken __func__, optarg); 633268240Sken error = 1; 634268240Sken goto bailout; 635268240Sken } 636268240Sken break; 637268240Sken } 638268240Sken 639268240Sken res_type_size = sizeof(persist_type_table) / 640268240Sken sizeof(persist_type_table[0]); 641268240Sken rtype_table = persist_type_table; 642268240Sken status = scsi_get_nv(rtype_table, res_type_size, 643268240Sken optarg, &table_entry, 644268240Sken SCSI_NV_FLAG_IG_CASE); 645268240Sken if (status == SCSI_NV_FOUND) 646268240Sken res_type = rtype_table[table_entry].value; 647268240Sken else { 648268240Sken warnx("%s: %s reservation type %s", __func__, 649268240Sken (status == SCSI_NV_AMBIGUOUS) ? 650268240Sken "ambiguous" : "invalid", optarg); 651268240Sken error = 1; 652268240Sken goto bailout; 653268240Sken } 654268240Sken break; 655268240Sken } 656268240Sken case 'U': 657268240Sken unreg = 1; 658268240Sken break; 659268240Sken default: 660268240Sken break; 661268240Sken } 662268240Sken } 663268240Sken 664268240Sken if ((in + out) != 1) { 665268240Sken warnx("%s: you must specify one of -i or -o", __func__); 666268240Sken error = 1; 667268240Sken goto bailout; 668268240Sken } 669268240Sken 670268240Sken /* 671268240Sken * Note that we don't really try to figure out whether the user 672268240Sken * needs to specify one or both keys. There are a number of 673268240Sken * scenarios, and sometimes 0 is a valid and desired value. 674268240Sken */ 675268240Sken if (in != 0) { 676268240Sken switch (action) { 677268240Sken case SPRI_RK: 678268240Sken case SPRI_RR: 679268240Sken case SPRI_RS: 680268240Sken /* 681268240Sken * Allocate the maximum length possible for these 682268240Sken * service actions. According to the spec, the 683268240Sken * target is supposed to return the available 684268240Sken * length in the header, regardless of the 685268240Sken * allocation length. In practice, though, with 686268240Sken * the READ FULL STATUS (SPRI_RS) service action, 687268240Sken * some Seagate drives (in particular a 688268240Sken * Constellation ES, <SEAGATE ST32000444SS 0006>) 689268240Sken * don't return the available length if you only 690268240Sken * allocate the length of the header. So just 691268240Sken * allocate the maximum here so we don't miss 692268240Sken * anything. 693268240Sken */ 694268240Sken res_len = SPRI_MAX_LEN; 695268240Sken break; 696268240Sken case SPRI_RC: 697268240Sken res_len = sizeof(struct scsi_per_res_cap); 698268240Sken break; 699268240Sken default: 700268240Sken /* In theory we should catch this above */ 701268240Sken warnx("%s: invalid action %d", __func__, action); 702268240Sken error = 1; 703268240Sken goto bailout; 704268240Sken break; 705268240Sken } 706268240Sken } else { 707268240Sken 708268240Sken /* 709268240Sken * XXX KDM need to add length for transport IDs for the 710268240Sken * register and move service action and the register 711268240Sken * service action with the SPEC_I_PT bit set. 712268240Sken */ 713268240Sken if (action == SPRO_REG_MOVE) { 714268240Sken if (num_ids != 1) { 715268240Sken warnx("%s: register and move requires a " 716268240Sken "single transport ID (-I)", __func__); 717268240Sken error = 1; 718268240Sken goto bailout; 719268240Sken } 720268240Sken if (rel_port_set == 0) { 721268240Sken warnx("%s: register and move requires a " 722268240Sken "relative target port (-R)", __func__); 723268240Sken error = 1; 724268240Sken goto bailout; 725268240Sken } 726268240Sken res_len = sizeof(struct scsi_per_res_reg_move) + id_len; 727268240Sken } else { 728268240Sken res_len = sizeof(struct scsi_per_res_out_parms); 729268240Sken if ((action == SPRO_REGISTER) 730268240Sken && (num_ids != 0)) { 731268240Sken /* 732268240Sken * If the user specifies any IDs with the 733268240Sken * register service action, turn on the 734268240Sken * spec_i_pt bit. 735268240Sken */ 736268240Sken spec_i_pt = 1; 737268240Sken res_len += id_len; 738268240Sken res_len += 739268240Sken sizeof(struct scsi_per_res_out_trans_ids); 740268240Sken } 741268240Sken } 742268240Sken } 743268240Skenretry: 744268240Sken if (res_buf != NULL) { 745268240Sken free(res_buf); 746268240Sken res_buf = NULL; 747268240Sken } 748268240Sken res_buf = malloc(res_len); 749268240Sken if (res_buf == NULL) { 750268240Sken warn("%s: error allocating %d bytes", __func__, res_len); 751268240Sken error = 1; 752268240Sken goto bailout; 753268240Sken } 754268240Sken bzero(res_buf, res_len); 755268240Sken 756268240Sken if (in != 0) { 757268240Sken scsi_persistent_reserve_in(&ccb->csio, 758268240Sken /*retries*/ retry_count, 759268240Sken /*cbfcnp*/ NULL, 760314220Sken /*tag_action*/ task_attr, 761268240Sken /*service_action*/ action, 762268240Sken /*data_ptr*/ res_buf, 763268240Sken /*dxfer_len*/ res_len, 764268240Sken /*sense_len*/ SSD_FULL_SIZE, 765268240Sken /*timeout*/ timeout ? timeout :5000); 766268240Sken 767268240Sken } else { 768268240Sken switch (action) { 769268240Sken case SPRO_REGISTER: 770268240Sken if (spec_i_pt != 0) { 771268240Sken struct scsi_per_res_out_trans_ids *id_hdr; 772268240Sken uint8_t *bufptr; 773268240Sken 774268240Sken bufptr = res_buf + 775268240Sken sizeof(struct scsi_per_res_out_parms) + 776268240Sken sizeof(struct scsi_per_res_out_trans_ids); 777268240Sken STAILQ_FOREACH(id, &transport_id_list, links) { 778268240Sken bcopy(id->hdr, bufptr, id->alloc_len); 779268240Sken bufptr += id->alloc_len; 780268240Sken } 781268240Sken id_hdr = (struct scsi_per_res_out_trans_ids *) 782268240Sken (res_buf + 783268240Sken sizeof(struct scsi_per_res_out_parms)); 784268240Sken scsi_ulto4b(id_len, id_hdr->additional_length); 785268240Sken } 786268240Sken case SPRO_REG_IGNO: 787268240Sken case SPRO_PREEMPT: 788268240Sken case SPRO_PRE_ABO: 789268240Sken case SPRO_RESERVE: 790268240Sken case SPRO_RELEASE: 791268240Sken case SPRO_CLEAR: 792268240Sken case SPRO_REPL_LOST_RES: { 793268240Sken struct scsi_per_res_out_parms *parms; 794268240Sken 795268240Sken parms = (struct scsi_per_res_out_parms *)res_buf; 796268240Sken 797268240Sken scsi_u64to8b(key, parms->res_key.key); 798268240Sken scsi_u64to8b(sa_key, parms->serv_act_res_key); 799268240Sken if (spec_i_pt != 0) 800268240Sken parms->flags |= SPR_SPEC_I_PT; 801268240Sken if (all_tg_pt != 0) 802268240Sken parms->flags |= SPR_ALL_TG_PT; 803268240Sken if (aptpl != 0) 804268240Sken parms->flags |= SPR_APTPL; 805268240Sken break; 806268240Sken } 807268240Sken case SPRO_REG_MOVE: { 808268240Sken struct scsi_per_res_reg_move *reg_move; 809268240Sken uint8_t *bufptr; 810268240Sken 811268240Sken reg_move = (struct scsi_per_res_reg_move *)res_buf; 812268240Sken 813268240Sken scsi_u64to8b(key, reg_move->res_key.key); 814268240Sken scsi_u64to8b(sa_key, reg_move->serv_act_res_key); 815268240Sken if (unreg != 0) 816268240Sken reg_move->flags |= SPR_REG_MOVE_UNREG; 817268240Sken if (aptpl != 0) 818268240Sken reg_move->flags |= SPR_REG_MOVE_APTPL; 819268240Sken scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id); 820268240Sken id = STAILQ_FIRST(&transport_id_list); 821268240Sken /* 822268240Sken * This shouldn't happen, since we already checked 823268240Sken * the number of IDs above. 824268240Sken */ 825268240Sken if (id == NULL) { 826268240Sken warnx("%s: No transport IDs found!", __func__); 827268240Sken error = 1; 828268240Sken goto bailout; 829268240Sken } 830268240Sken bufptr = (uint8_t *)®_move[1]; 831268240Sken bcopy(id->hdr, bufptr, id->alloc_len); 832268240Sken scsi_ulto4b(id->alloc_len, 833268240Sken reg_move->transport_id_length); 834268240Sken break; 835268240Sken } 836268240Sken default: 837268240Sken break; 838268240Sken } 839268240Sken scsi_persistent_reserve_out(&ccb->csio, 840268240Sken /*retries*/ retry_count, 841268240Sken /*cbfcnp*/ NULL, 842314220Sken /*tag_action*/ task_attr, 843268240Sken /*service_action*/ action, 844268240Sken /*scope*/ scope, 845268240Sken /*res_type*/ res_type, 846268240Sken /*data_ptr*/ res_buf, 847268240Sken /*dxfer_len*/ res_len, 848268240Sken /*sense_len*/ SSD_FULL_SIZE, 849268240Sken /*timeout*/ timeout ?timeout :5000); 850268240Sken } 851268240Sken 852268240Sken /* Disable freezing the device queue */ 853268240Sken ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 854268240Sken 855268240Sken if (err_recover != 0) 856268240Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 857268240Sken 858268240Sken if (cam_send_ccb(device, ccb) < 0) { 859268240Sken warn("error sending PERSISTENT RESERVE %s", (in != 0) ? 860268240Sken "IN" : "OUT"); 861268240Sken error = 1; 862268240Sken goto bailout; 863268240Sken } 864268240Sken 865268240Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 866268240Sken if (verbosemode != 0) { 867268240Sken cam_error_print(device, ccb, CAM_ESF_ALL, 868268240Sken CAM_EPF_ALL, stderr); 869268240Sken } 870268240Sken error = 1; 871268240Sken goto bailout; 872268240Sken } 873268240Sken 874268240Sken if (in == 0) 875268240Sken goto bailout; 876268240Sken 877268240Sken valid_len = res_len - ccb->csio.resid; 878268240Sken 879268240Sken switch (action) { 880268240Sken case SPRI_RK: 881268240Sken case SPRI_RR: 882268240Sken case SPRI_RS: { 883268240Sken struct scsi_per_res_in_header *hdr; 884268240Sken uint32_t hdr_len; 885268240Sken 886268240Sken if (valid_len < sizeof(*hdr)) { 887268240Sken warnx("%s: only got %d valid bytes, need %zd", 888268240Sken __func__, valid_len, sizeof(*hdr)); 889268240Sken error = 1; 890268240Sken goto bailout; 891268240Sken } 892268240Sken hdr = (struct scsi_per_res_in_header *)res_buf; 893268240Sken hdr_len = scsi_4btoul(hdr->length); 894268240Sken 895268240Sken if (hdr_len > (res_len - sizeof(*hdr))) { 896268240Sken res_len = hdr_len + sizeof(*hdr); 897268240Sken goto retry; 898268240Sken } 899268240Sken 900268240Sken if (action == SPRI_RK) { 901268240Sken persist_print_keys(hdr, valid_len); 902268240Sken } else if (action == SPRI_RR) { 903268240Sken persist_print_res(hdr, valid_len); 904268240Sken } else { 905268240Sken persist_print_full(hdr, valid_len); 906268240Sken } 907268240Sken break; 908268240Sken } 909268240Sken case SPRI_RC: { 910268240Sken struct scsi_per_res_cap *cap; 911268240Sken uint32_t cap_len; 912268240Sken 913268240Sken if (valid_len < sizeof(*cap)) { 914268240Sken warnx("%s: only got %u valid bytes, need %zd", 915268240Sken __func__, valid_len, sizeof(*cap)); 916268240Sken error = 1; 917268240Sken goto bailout; 918268240Sken } 919268240Sken cap = (struct scsi_per_res_cap *)res_buf; 920268240Sken cap_len = scsi_2btoul(cap->length); 921268240Sken if (cap_len != sizeof(*cap)) { 922268240Sken /* 923268240Sken * We should be able to deal with this, 924268240Sken * it's just more trouble. 925268240Sken */ 926268240Sken warnx("%s: reported size %u is different " 927268240Sken "than expected size %zd", __func__, 928268240Sken cap_len, sizeof(*cap)); 929268240Sken } 930268240Sken 931268240Sken /* 932268240Sken * If there is more data available, grab it all, 933268240Sken * even though we don't really know what to do with 934268240Sken * the extra data since it obviously wasn't in the 935268240Sken * spec when this code was written. 936268240Sken */ 937268240Sken if (cap_len > res_len) { 938268240Sken res_len = cap_len; 939268240Sken goto retry; 940268240Sken } 941268240Sken persist_print_cap(cap, valid_len); 942268240Sken break; 943268240Sken } 944268240Sken default: 945268240Sken break; 946268240Sken } 947268240Sken 948268240Skenbailout: 949268240Sken free(res_buf); 950268240Sken 951268240Sken if (ccb != NULL) 952268240Sken cam_freeccb(ccb); 953268240Sken 954268240Sken STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) { 955268240Sken STAILQ_REMOVE(&transport_id_list, id, persist_transport_id, 956268240Sken links); 957268240Sken free(id); 958268240Sken } 959268240Sken return (error); 960268240Sken} 961