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: releng/11.0/sbin/camcontrol/persist.c 300547 2016-05-24 00:57:11Z truckman $"); 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; 244268240Sken 245268240Sken length = scsi_2btoul(cap->length); 246268240Sken length = MIN(length, valid_len); 247268240Sken 248268240Sken if (length < __offsetof(struct scsi_per_res_cap, type_mask)) { 249268240Sken fprintf(stdout, "Insufficient data (%u bytes) to report " 250268240Sken "full capabilities\n", length); 251268240Sken return; 252268240Sken } 253268240Sken if (length >= __offsetof(struct scsi_per_res_cap, reserved)) 254268240Sken check_type_mask = 1; 255268240Sken 256268240Sken fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n", 257268240Sken (cap->flags1 & SPRI_RLR_C) ? 1 : 0); 258268240Sken fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n", 259268240Sken (cap->flags1 & SPRI_CRH) ? 1 : 0); 260268240Sken fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n", 261268240Sken (cap->flags1 & SPRI_SIP_C) ? 1 : 0); 262268240Sken fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n", 263268240Sken (cap->flags1 & SPRI_ATP_C) ? 1 : 0); 264268240Sken fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n", 265268240Sken (cap->flags1 & SPRI_PTPL_C) ? 1 : 0); 266268240Sken fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n", 267268240Sken (cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT); 268268240Sken /* 269268240Sken * These cases are cut-and-pasted from SPC4r36l. There is no 270268240Sken * succinct way to describe these otherwise, and even with the 271268240Sken * verbose description, the user will probably have to refer to 272268240Sken * the spec to fully understand what is going on. 273268240Sken */ 274268240Sken switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) { 275268240Sken case SPRI_ALLOW_1: 276268240Sken fprintf(stdout, 277268240Sken" The device server allows the TEST UNIT READY command through Write\n" 278268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 279268240Sken" and does not provide information about whether the following commands\n" 280268240Sken" are allowed through Write Exclusive type reservations:\n" 281268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 282268240Sken" command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n" 283268240Sken" RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n" 284268240Sken" and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n" 285268240Sken" b) the READ DEFECT DATA command (see SBC-3).\n"); 286268240Sken break; 287268240Sken case SPRI_ALLOW_2: 288268240Sken fprintf(stdout, 289268240Sken" The device server allows the TEST UNIT READY command through Write\n" 290268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 291268240Sken" and does not allow the following commands through Write Exclusive type\n" 292268240Sken" reservations:\n" 293268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 294268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 295268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 296268240Sken" FUNCTION command; and\n" 297268240Sken" b) the READ DEFECT DATA command.\n" 298268240Sken" The device server does not allow the RECEIVE COPY RESULTS command\n" 299268240Sken" through Write Exclusive type reservations or Exclusive Access type\n" 300268240Sken" reservations.\n"); 301268240Sken break; 302268240Sken case SPRI_ALLOW_3: 303268240Sken fprintf(stdout, 304268240Sken" The device server allows the TEST UNIT READY command through Write\n" 305268240Sken" Exclusive type reservations and Exclusive Access type reservations\n" 306268240Sken" and allows the following commands through Write Exclusive type\n" 307268240Sken" reservations:\n" 308268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 309268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 310268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 311268240Sken" FUNCTION command; and\n" 312268240Sken" b) the READ DEFECT DATA command.\n" 313268240Sken" The device server does not allow the RECEIVE COPY RESULTS command\n" 314268240Sken" through Write Exclusive type reservations or Exclusive Access type\n" 315268240Sken" reservations.\n"); 316268240Sken break; 317268240Sken case SPRI_ALLOW_4: 318268240Sken fprintf(stdout, 319268240Sken" The device server allows the TEST UNIT READY command and the RECEIVE\n" 320268240Sken" COPY RESULTS command through Write Exclusive type reservations and\n" 321268240Sken" Exclusive Access type reservations and allows the following commands\n" 322268240Sken" through Write Exclusive type reservations:\n" 323268240Sken" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n" 324268240Sken" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n" 325268240Sken" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n" 326268240Sken" FUNCTION command; and\n" 327268240Sken" b) the READ DEFECT DATA command.\n"); 328268240Sken break; 329268240Sken case SPRI_ALLOW_NA: 330268240Sken fprintf(stdout, 331268240Sken" No information is provided about whether certain commands are allowed\n" 332268240Sken" through certain types of persistent reservations.\n"); 333268240Sken break; 334268240Sken default: 335268240Sken fprintf(stdout, 336268240Sken" Unknown ALLOW COMMANDS value %#x\n", 337268240Sken (cap->flags2 & SPRI_ALLOW_CMD_MASK) >> 338268240Sken SPRI_ALLOW_CMD_SHIFT); 339268240Sken break; 340268240Sken } 341268240Sken fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n", 342268240Sken (cap->flags2 & SPRI_PTPL_A) ? 1 : 0); 343268240Sken if ((check_type_mask != 0) 344268240Sken && (cap->flags2 & SPRI_TMV)) { 345268240Sken fprintf(stdout, "Supported Persistent Reservation Types:\n"); 346268240Sken fprintf(stdout, " Write Exclusive - All Registrants " 347268240Sken "(WR_EX_AR): %d\n", 348268240Sken (cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0); 349268240Sken fprintf(stdout, " Exclusive Access - Registrants Only " 350268240Sken "(EX_AC_RO): %d\n", 351268240Sken (cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0); 352268240Sken fprintf(stdout, " Write Exclusive - Registrants Only " 353268240Sken "(WR_EX_RO): %d\n", 354268240Sken (cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0); 355268240Sken fprintf(stdout, " Exclusive Access (EX_AC): %d\n", 356268240Sken (cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0); 357268240Sken fprintf(stdout, " Write Exclusive (WR_EX): %d\n", 358268240Sken (cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0); 359268240Sken fprintf(stdout, " Exclusive Access - All Registrants " 360268240Sken "(EX_AC_AR): %d\n", 361268240Sken (cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0); 362268240Sken } else { 363268240Sken fprintf(stdout, "Persistent Reservation Type Mask is NOT " 364268240Sken "valid\n"); 365268240Sken } 366268240Sken 367268240Sken 368268240Sken} 369268240Sken 370268240Skenstatic void 371268240Skenpersist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len) 372268240Sken{ 373268240Sken uint32_t length, len_to_go = 0; 374268240Sken struct scsi_per_res_in_full_desc *desc; 375268240Sken uint8_t *cur_pos; 376268240Sken int i; 377268240Sken 378268240Sken length = scsi_4btoul(hdr->length); 379268240Sken length = MIN(length, valid_len); 380268240Sken 381268240Sken if (length < sizeof(*desc)) { 382268240Sken if (length == 0) 383268240Sken fprintf(stdout, "No reservations.\n"); 384268240Sken else 385268240Sken warnx("unable to print reservation, only got %u " 386268240Sken "valid bytes", length); 387268240Sken return; 388268240Sken } 389268240Sken 390268240Sken fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation)); 391268240Sken cur_pos = (uint8_t *)&hdr[1]; 392268240Sken for (len_to_go = length, i = 0, 393268240Sken desc = (struct scsi_per_res_in_full_desc *)cur_pos; 394268240Sken len_to_go >= sizeof(*desc); 395268240Sken desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) { 396268240Sken uint32_t additional_length, cur_length; 397268240Sken 398268240Sken 399268240Sken fprintf(stdout, "Reservation Key: %#jx\n", 400268240Sken (uintmax_t)scsi_8btou64(desc->res_key.key)); 401268240Sken fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n", 402268240Sken (desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0); 403268240Sken fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n", 404268240Sken (desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0); 405268240Sken 406268240Sken if (desc->flags & SPRI_FULL_R_HOLDER) 407268240Sken persist_print_scopetype(desc->scopetype); 408268240Sken 409268240Sken if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0) 410268240Sken fprintf(stdout, "Relative Target Port ID: %#x\n", 411268240Sken scsi_2btoul(desc->rel_trgt_port_id)); 412268240Sken 413268240Sken additional_length = scsi_4btoul(desc->additional_length); 414268240Sken 415268240Sken persist_print_transportid(desc->transport_id, 416268240Sken additional_length); 417268240Sken 418268240Sken cur_length = sizeof(*desc) + additional_length; 419268240Sken len_to_go -= cur_length; 420268240Sken cur_pos += cur_length; 421268240Sken } 422268240Sken} 423268240Sken 424268240Skenint 425268240Skenscsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt, 426268240Sken int retry_count, int timeout, int verbosemode, int err_recover) 427268240Sken{ 428268240Sken union ccb *ccb = NULL; 429268240Sken int c, in = 0, out = 0; 430268240Sken int action = -1, num_ids = 0; 431268240Sken int error = 0; 432268240Sken uint32_t res_len = 0; 433268240Sken unsigned long rel_tgt_port = 0; 434268240Sken uint8_t *res_buf = NULL; 435283907Saraujo int scope = SPR_LU_SCOPE, res_type = 0; 436268240Sken struct persist_transport_id *id, *id2; 437268240Sken STAILQ_HEAD(, persist_transport_id) transport_id_list; 438268240Sken uint64_t key = 0, sa_key = 0; 439268240Sken struct scsi_nv *table = NULL; 440268240Sken size_t table_size = 0, id_len = 0; 441268240Sken uint32_t valid_len = 0; 442268240Sken int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0; 443268240Sken 444268240Sken STAILQ_INIT(&transport_id_list); 445268240Sken 446268240Sken ccb = cam_getccb(device); 447268240Sken if (ccb == NULL) { 448268240Sken warnx("%s: error allocating CCB", __func__); 449268240Sken error = 1; 450268240Sken goto bailout; 451268240Sken } 452268240Sken 453300547Struckman CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); 454268240Sken 455268240Sken while ((c = getopt(argc, argv, combinedopt)) != -1) { 456268240Sken switch (c) { 457268240Sken case 'a': 458268240Sken all_tg_pt = 1; 459268240Sken break; 460268240Sken case 'I': { 461268240Sken int error_str_len = 128; 462268240Sken char error_str[error_str_len]; 463268240Sken char *id_str; 464268240Sken 465268240Sken id = malloc(sizeof(*id)); 466268240Sken if (id == NULL) { 467268240Sken warnx("%s: error allocating %zu bytes", 468268240Sken __func__, sizeof(*id)); 469268240Sken error = 1; 470268240Sken goto bailout; 471268240Sken } 472268240Sken bzero(id, sizeof(*id)); 473268240Sken 474268240Sken id_str = strdup(optarg); 475268240Sken if (id_str == NULL) { 476268240Sken warnx("%s: error duplicating string %s", 477268240Sken __func__, optarg); 478268240Sken free(id); 479268240Sken error = 1; 480268240Sken goto bailout; 481268240Sken } 482268240Sken error = scsi_parse_transportid(id_str, &id->hdr, 483268240Sken &id->alloc_len, error_str, error_str_len); 484268240Sken if (error != 0) { 485268240Sken warnx("%s", error_str); 486268240Sken error = 1; 487268240Sken free(id); 488268240Sken free(id_str); 489268240Sken goto bailout; 490268240Sken } 491268240Sken free(id_str); 492268240Sken 493268240Sken STAILQ_INSERT_TAIL(&transport_id_list, id, links); 494268240Sken num_ids++; 495268240Sken id_len += id->alloc_len; 496268240Sken break; 497268240Sken } 498268240Sken case 'k': 499268240Sken case 'K': { 500268240Sken char *endptr; 501268240Sken uint64_t tmpval; 502268240Sken 503268240Sken tmpval = strtoumax(optarg, &endptr, 0); 504268240Sken if (*endptr != '\0') { 505268240Sken warnx("%s: invalid key argument %s", __func__, 506268240Sken optarg); 507268240Sken error = 1; 508268240Sken goto bailout; 509268240Sken } 510268240Sken if (c == 'k') { 511268240Sken key = tmpval; 512268240Sken } else { 513268240Sken sa_key = tmpval; 514268240Sken } 515268240Sken break; 516268240Sken } 517268240Sken case 'i': 518268240Sken case 'o': { 519268240Sken scsi_nv_status status; 520268240Sken int table_entry = 0; 521268240Sken 522268240Sken if (c == 'i') { 523268240Sken in = 1; 524268240Sken table = persist_in_actions; 525268240Sken table_size = sizeof(persist_in_actions) / 526268240Sken sizeof(persist_in_actions[0]); 527268240Sken } else { 528268240Sken out = 1; 529268240Sken table = persist_out_actions; 530268240Sken table_size = sizeof(persist_out_actions) / 531268240Sken sizeof(persist_out_actions[0]); 532268240Sken } 533268240Sken 534268240Sken if ((in + out) > 1) { 535268240Sken warnx("%s: only one in (-i) or out (-o) " 536268240Sken "action is allowed", __func__); 537268240Sken error = 1; 538268240Sken goto bailout; 539268240Sken } 540268240Sken 541268240Sken status = scsi_get_nv(table, table_size, optarg, 542268240Sken &table_entry,SCSI_NV_FLAG_IG_CASE); 543268240Sken if (status == SCSI_NV_FOUND) 544268240Sken action = table[table_entry].value; 545268240Sken else { 546268240Sken warnx("%s: %s %s option %s", __func__, 547268240Sken (status == SCSI_NV_AMBIGUOUS) ? 548268240Sken "ambiguous" : "invalid", in ? "in" : 549268240Sken "out", optarg); 550268240Sken error = 1; 551268240Sken goto bailout; 552268240Sken } 553268240Sken break; 554268240Sken } 555268240Sken case 'p': 556268240Sken aptpl = 1; 557268240Sken break; 558268240Sken case 'R': { 559268240Sken char *endptr; 560268240Sken 561268240Sken rel_tgt_port = strtoul(optarg, &endptr, 0); 562268240Sken if (*endptr != '\0') { 563268240Sken warnx("%s: invalid relative target port %s", 564268240Sken __func__, optarg); 565268240Sken error = 1; 566268240Sken goto bailout; 567268240Sken } 568268240Sken rel_port_set = 1; 569268240Sken break; 570268240Sken } 571268240Sken case 's': { 572268240Sken size_t scope_size; 573268240Sken struct scsi_nv *scope_table = NULL; 574268240Sken scsi_nv_status status; 575268240Sken int table_entry = 0; 576268240Sken char *endptr; 577268240Sken 578268240Sken /* 579268240Sken * First check to see if the user gave us a numeric 580268240Sken * argument. If so, we'll try using it. 581268240Sken */ 582268240Sken if (isdigit(optarg[0])) { 583268240Sken scope = strtol(optarg, &endptr, 0); 584268240Sken if (*endptr != '\0') { 585268240Sken warnx("%s: invalid scope %s", 586268240Sken __func__, optarg); 587268240Sken error = 1; 588268240Sken goto bailout; 589268240Sken } 590268240Sken scope = (scope << SPR_SCOPE_SHIFT) & 591268240Sken SPR_SCOPE_MASK; 592268240Sken break; 593268240Sken } 594268240Sken 595268240Sken scope_size = sizeof(persist_scope_table) / 596268240Sken sizeof(persist_scope_table[0]); 597268240Sken scope_table = persist_scope_table; 598268240Sken status = scsi_get_nv(scope_table, scope_size, optarg, 599268240Sken &table_entry,SCSI_NV_FLAG_IG_CASE); 600268240Sken if (status == SCSI_NV_FOUND) 601268240Sken scope = scope_table[table_entry].value; 602268240Sken else { 603268240Sken warnx("%s: %s scope %s", __func__, 604268240Sken (status == SCSI_NV_AMBIGUOUS) ? 605268240Sken "ambiguous" : "invalid", optarg); 606268240Sken error = 1; 607268240Sken goto bailout; 608268240Sken } 609268240Sken break; 610268240Sken } 611268240Sken case 'S': 612268240Sken spec_i_pt = 1; 613268240Sken break; 614268240Sken case 'T': { 615268240Sken size_t res_type_size; 616268240Sken struct scsi_nv *rtype_table = NULL; 617268240Sken scsi_nv_status status; 618268240Sken char *endptr; 619268240Sken int table_entry = 0; 620268240Sken 621268240Sken /* 622268240Sken * First check to see if the user gave us a numeric 623268240Sken * argument. If so, we'll try using it. 624268240Sken */ 625268240Sken if (isdigit(optarg[0])) { 626268240Sken res_type = strtol(optarg, &endptr, 0); 627268240Sken if (*endptr != '\0') { 628268240Sken warnx("%s: invalid reservation type %s", 629268240Sken __func__, optarg); 630268240Sken error = 1; 631268240Sken goto bailout; 632268240Sken } 633268240Sken break; 634268240Sken } 635268240Sken 636268240Sken res_type_size = sizeof(persist_type_table) / 637268240Sken sizeof(persist_type_table[0]); 638268240Sken rtype_table = persist_type_table; 639268240Sken status = scsi_get_nv(rtype_table, res_type_size, 640268240Sken optarg, &table_entry, 641268240Sken SCSI_NV_FLAG_IG_CASE); 642268240Sken if (status == SCSI_NV_FOUND) 643268240Sken res_type = rtype_table[table_entry].value; 644268240Sken else { 645268240Sken warnx("%s: %s reservation type %s", __func__, 646268240Sken (status == SCSI_NV_AMBIGUOUS) ? 647268240Sken "ambiguous" : "invalid", optarg); 648268240Sken error = 1; 649268240Sken goto bailout; 650268240Sken } 651268240Sken break; 652268240Sken } 653268240Sken case 'U': 654268240Sken unreg = 1; 655268240Sken break; 656268240Sken default: 657268240Sken break; 658268240Sken } 659268240Sken } 660268240Sken 661268240Sken if ((in + out) != 1) { 662268240Sken warnx("%s: you must specify one of -i or -o", __func__); 663268240Sken error = 1; 664268240Sken goto bailout; 665268240Sken } 666268240Sken 667268240Sken /* 668268240Sken * Note that we don't really try to figure out whether the user 669268240Sken * needs to specify one or both keys. There are a number of 670268240Sken * scenarios, and sometimes 0 is a valid and desired value. 671268240Sken */ 672268240Sken if (in != 0) { 673268240Sken switch (action) { 674268240Sken case SPRI_RK: 675268240Sken case SPRI_RR: 676268240Sken case SPRI_RS: 677268240Sken /* 678268240Sken * Allocate the maximum length possible for these 679268240Sken * service actions. According to the spec, the 680268240Sken * target is supposed to return the available 681268240Sken * length in the header, regardless of the 682268240Sken * allocation length. In practice, though, with 683268240Sken * the READ FULL STATUS (SPRI_RS) service action, 684268240Sken * some Seagate drives (in particular a 685268240Sken * Constellation ES, <SEAGATE ST32000444SS 0006>) 686268240Sken * don't return the available length if you only 687268240Sken * allocate the length of the header. So just 688268240Sken * allocate the maximum here so we don't miss 689268240Sken * anything. 690268240Sken */ 691268240Sken res_len = SPRI_MAX_LEN; 692268240Sken break; 693268240Sken case SPRI_RC: 694268240Sken res_len = sizeof(struct scsi_per_res_cap); 695268240Sken break; 696268240Sken default: 697268240Sken /* In theory we should catch this above */ 698268240Sken warnx("%s: invalid action %d", __func__, action); 699268240Sken error = 1; 700268240Sken goto bailout; 701268240Sken break; 702268240Sken } 703268240Sken } else { 704268240Sken 705268240Sken /* 706268240Sken * XXX KDM need to add length for transport IDs for the 707268240Sken * register and move service action and the register 708268240Sken * service action with the SPEC_I_PT bit set. 709268240Sken */ 710268240Sken if (action == SPRO_REG_MOVE) { 711268240Sken if (num_ids != 1) { 712268240Sken warnx("%s: register and move requires a " 713268240Sken "single transport ID (-I)", __func__); 714268240Sken error = 1; 715268240Sken goto bailout; 716268240Sken } 717268240Sken if (rel_port_set == 0) { 718268240Sken warnx("%s: register and move requires a " 719268240Sken "relative target port (-R)", __func__); 720268240Sken error = 1; 721268240Sken goto bailout; 722268240Sken } 723268240Sken res_len = sizeof(struct scsi_per_res_reg_move) + id_len; 724268240Sken } else { 725268240Sken res_len = sizeof(struct scsi_per_res_out_parms); 726268240Sken if ((action == SPRO_REGISTER) 727268240Sken && (num_ids != 0)) { 728268240Sken /* 729268240Sken * If the user specifies any IDs with the 730268240Sken * register service action, turn on the 731268240Sken * spec_i_pt bit. 732268240Sken */ 733268240Sken spec_i_pt = 1; 734268240Sken res_len += id_len; 735268240Sken res_len += 736268240Sken sizeof(struct scsi_per_res_out_trans_ids); 737268240Sken } 738268240Sken } 739268240Sken } 740268240Skenretry: 741268240Sken if (res_buf != NULL) { 742268240Sken free(res_buf); 743268240Sken res_buf = NULL; 744268240Sken } 745268240Sken res_buf = malloc(res_len); 746268240Sken if (res_buf == NULL) { 747268240Sken warn("%s: error allocating %d bytes", __func__, res_len); 748268240Sken error = 1; 749268240Sken goto bailout; 750268240Sken } 751268240Sken bzero(res_buf, res_len); 752268240Sken 753268240Sken if (in != 0) { 754268240Sken scsi_persistent_reserve_in(&ccb->csio, 755268240Sken /*retries*/ retry_count, 756268240Sken /*cbfcnp*/ NULL, 757268240Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 758268240Sken /*service_action*/ action, 759268240Sken /*data_ptr*/ res_buf, 760268240Sken /*dxfer_len*/ res_len, 761268240Sken /*sense_len*/ SSD_FULL_SIZE, 762268240Sken /*timeout*/ timeout ? timeout :5000); 763268240Sken 764268240Sken } else { 765268240Sken switch (action) { 766268240Sken case SPRO_REGISTER: 767268240Sken if (spec_i_pt != 0) { 768268240Sken struct scsi_per_res_out_trans_ids *id_hdr; 769268240Sken uint8_t *bufptr; 770268240Sken 771268240Sken bufptr = res_buf + 772268240Sken sizeof(struct scsi_per_res_out_parms) + 773268240Sken sizeof(struct scsi_per_res_out_trans_ids); 774268240Sken STAILQ_FOREACH(id, &transport_id_list, links) { 775268240Sken bcopy(id->hdr, bufptr, id->alloc_len); 776268240Sken bufptr += id->alloc_len; 777268240Sken } 778268240Sken id_hdr = (struct scsi_per_res_out_trans_ids *) 779268240Sken (res_buf + 780268240Sken sizeof(struct scsi_per_res_out_parms)); 781268240Sken scsi_ulto4b(id_len, id_hdr->additional_length); 782268240Sken } 783268240Sken case SPRO_REG_IGNO: 784268240Sken case SPRO_PREEMPT: 785268240Sken case SPRO_PRE_ABO: 786268240Sken case SPRO_RESERVE: 787268240Sken case SPRO_RELEASE: 788268240Sken case SPRO_CLEAR: 789268240Sken case SPRO_REPL_LOST_RES: { 790268240Sken struct scsi_per_res_out_parms *parms; 791268240Sken 792268240Sken parms = (struct scsi_per_res_out_parms *)res_buf; 793268240Sken 794268240Sken scsi_u64to8b(key, parms->res_key.key); 795268240Sken scsi_u64to8b(sa_key, parms->serv_act_res_key); 796268240Sken if (spec_i_pt != 0) 797268240Sken parms->flags |= SPR_SPEC_I_PT; 798268240Sken if (all_tg_pt != 0) 799268240Sken parms->flags |= SPR_ALL_TG_PT; 800268240Sken if (aptpl != 0) 801268240Sken parms->flags |= SPR_APTPL; 802268240Sken break; 803268240Sken } 804268240Sken case SPRO_REG_MOVE: { 805268240Sken struct scsi_per_res_reg_move *reg_move; 806268240Sken uint8_t *bufptr; 807268240Sken 808268240Sken reg_move = (struct scsi_per_res_reg_move *)res_buf; 809268240Sken 810268240Sken scsi_u64to8b(key, reg_move->res_key.key); 811268240Sken scsi_u64to8b(sa_key, reg_move->serv_act_res_key); 812268240Sken if (unreg != 0) 813268240Sken reg_move->flags |= SPR_REG_MOVE_UNREG; 814268240Sken if (aptpl != 0) 815268240Sken reg_move->flags |= SPR_REG_MOVE_APTPL; 816268240Sken scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id); 817268240Sken id = STAILQ_FIRST(&transport_id_list); 818268240Sken /* 819268240Sken * This shouldn't happen, since we already checked 820268240Sken * the number of IDs above. 821268240Sken */ 822268240Sken if (id == NULL) { 823268240Sken warnx("%s: No transport IDs found!", __func__); 824268240Sken error = 1; 825268240Sken goto bailout; 826268240Sken } 827268240Sken bufptr = (uint8_t *)®_move[1]; 828268240Sken bcopy(id->hdr, bufptr, id->alloc_len); 829268240Sken scsi_ulto4b(id->alloc_len, 830268240Sken reg_move->transport_id_length); 831268240Sken break; 832268240Sken } 833268240Sken default: 834268240Sken break; 835268240Sken } 836268240Sken scsi_persistent_reserve_out(&ccb->csio, 837268240Sken /*retries*/ retry_count, 838268240Sken /*cbfcnp*/ NULL, 839268240Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 840268240Sken /*service_action*/ action, 841268240Sken /*scope*/ scope, 842268240Sken /*res_type*/ res_type, 843268240Sken /*data_ptr*/ res_buf, 844268240Sken /*dxfer_len*/ res_len, 845268240Sken /*sense_len*/ SSD_FULL_SIZE, 846268240Sken /*timeout*/ timeout ?timeout :5000); 847268240Sken } 848268240Sken 849268240Sken /* Disable freezing the device queue */ 850268240Sken ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 851268240Sken 852268240Sken if (err_recover != 0) 853268240Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 854268240Sken 855268240Sken if (cam_send_ccb(device, ccb) < 0) { 856268240Sken warn("error sending PERSISTENT RESERVE %s", (in != 0) ? 857268240Sken "IN" : "OUT"); 858268240Sken 859268240Sken if (verbosemode != 0) { 860268240Sken cam_error_print(device, ccb, CAM_ESF_ALL, 861268240Sken CAM_EPF_ALL, stderr); 862268240Sken } 863268240Sken 864268240Sken error = 1; 865268240Sken goto bailout; 866268240Sken } 867268240Sken 868268240Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 869268240Sken if (verbosemode != 0) { 870268240Sken cam_error_print(device, ccb, CAM_ESF_ALL, 871268240Sken CAM_EPF_ALL, stderr); 872268240Sken } 873268240Sken error = 1; 874268240Sken goto bailout; 875268240Sken } 876268240Sken 877268240Sken if (in == 0) 878268240Sken goto bailout; 879268240Sken 880268240Sken valid_len = res_len - ccb->csio.resid; 881268240Sken 882268240Sken switch (action) { 883268240Sken case SPRI_RK: 884268240Sken case SPRI_RR: 885268240Sken case SPRI_RS: { 886268240Sken struct scsi_per_res_in_header *hdr; 887268240Sken uint32_t hdr_len; 888268240Sken 889268240Sken if (valid_len < sizeof(*hdr)) { 890268240Sken warnx("%s: only got %d valid bytes, need %zd", 891268240Sken __func__, valid_len, sizeof(*hdr)); 892268240Sken error = 1; 893268240Sken goto bailout; 894268240Sken } 895268240Sken hdr = (struct scsi_per_res_in_header *)res_buf; 896268240Sken hdr_len = scsi_4btoul(hdr->length); 897268240Sken 898268240Sken if (hdr_len > (res_len - sizeof(*hdr))) { 899268240Sken res_len = hdr_len + sizeof(*hdr); 900268240Sken goto retry; 901268240Sken } 902268240Sken 903268240Sken if (action == SPRI_RK) { 904268240Sken persist_print_keys(hdr, valid_len); 905268240Sken } else if (action == SPRI_RR) { 906268240Sken persist_print_res(hdr, valid_len); 907268240Sken } else { 908268240Sken persist_print_full(hdr, valid_len); 909268240Sken } 910268240Sken break; 911268240Sken } 912268240Sken case SPRI_RC: { 913268240Sken struct scsi_per_res_cap *cap; 914268240Sken uint32_t cap_len; 915268240Sken 916268240Sken if (valid_len < sizeof(*cap)) { 917268240Sken warnx("%s: only got %u valid bytes, need %zd", 918268240Sken __func__, valid_len, sizeof(*cap)); 919268240Sken error = 1; 920268240Sken goto bailout; 921268240Sken } 922268240Sken cap = (struct scsi_per_res_cap *)res_buf; 923268240Sken cap_len = scsi_2btoul(cap->length); 924268240Sken if (cap_len != sizeof(*cap)) { 925268240Sken /* 926268240Sken * We should be able to deal with this, 927268240Sken * it's just more trouble. 928268240Sken */ 929268240Sken warnx("%s: reported size %u is different " 930268240Sken "than expected size %zd", __func__, 931268240Sken cap_len, sizeof(*cap)); 932268240Sken } 933268240Sken 934268240Sken /* 935268240Sken * If there is more data available, grab it all, 936268240Sken * even though we don't really know what to do with 937268240Sken * the extra data since it obviously wasn't in the 938268240Sken * spec when this code was written. 939268240Sken */ 940268240Sken if (cap_len > res_len) { 941268240Sken res_len = cap_len; 942268240Sken goto retry; 943268240Sken } 944268240Sken persist_print_cap(cap, valid_len); 945268240Sken break; 946268240Sken } 947268240Sken default: 948268240Sken break; 949268240Sken } 950268240Sken 951268240Skenbailout: 952268240Sken free(res_buf); 953268240Sken 954268240Sken if (ccb != NULL) 955268240Sken cam_freeccb(ccb); 956268240Sken 957268240Sken STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) { 958268240Sken STAILQ_REMOVE(&transport_id_list, id, persist_transport_id, 959268240Sken links); 960268240Sken free(id); 961268240Sken } 962268240Sken return (error); 963268240Sken} 964