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$"); 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; 435268240Sken int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 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 453268240Sken bzero(&(&ccb->ccb_h)[1], 454268240Sken sizeof(union ccb) - sizeof(struct ccb_hdr)); 455268240Sken 456268240Sken while ((c = getopt(argc, argv, combinedopt)) != -1) { 457268240Sken switch (c) { 458268240Sken case 'a': 459268240Sken all_tg_pt = 1; 460268240Sken break; 461268240Sken case 'I': { 462268240Sken int error_str_len = 128; 463268240Sken char error_str[error_str_len]; 464268240Sken char *id_str; 465268240Sken 466268240Sken id = malloc(sizeof(*id)); 467268240Sken if (id == NULL) { 468268240Sken warnx("%s: error allocating %zu bytes", 469268240Sken __func__, sizeof(*id)); 470268240Sken error = 1; 471268240Sken goto bailout; 472268240Sken } 473268240Sken bzero(id, sizeof(*id)); 474268240Sken 475268240Sken id_str = strdup(optarg); 476268240Sken if (id_str == NULL) { 477268240Sken warnx("%s: error duplicating string %s", 478268240Sken __func__, optarg); 479268240Sken free(id); 480268240Sken error = 1; 481268240Sken goto bailout; 482268240Sken } 483268240Sken error = scsi_parse_transportid(id_str, &id->hdr, 484268240Sken &id->alloc_len, error_str, error_str_len); 485268240Sken if (error != 0) { 486268240Sken warnx("%s", error_str); 487268240Sken error = 1; 488268240Sken free(id); 489268240Sken free(id_str); 490268240Sken goto bailout; 491268240Sken } 492268240Sken free(id_str); 493268240Sken 494268240Sken STAILQ_INSERT_TAIL(&transport_id_list, id, links); 495268240Sken num_ids++; 496268240Sken id_len += id->alloc_len; 497268240Sken break; 498268240Sken } 499268240Sken case 'k': 500268240Sken case 'K': { 501268240Sken char *endptr; 502268240Sken uint64_t tmpval; 503268240Sken 504268240Sken tmpval = strtoumax(optarg, &endptr, 0); 505268240Sken if (*endptr != '\0') { 506268240Sken warnx("%s: invalid key argument %s", __func__, 507268240Sken optarg); 508268240Sken error = 1; 509268240Sken goto bailout; 510268240Sken } 511268240Sken if (c == 'k') { 512268240Sken key = tmpval; 513268240Sken key_set = 1; 514268240Sken } else { 515268240Sken sa_key = tmpval; 516268240Sken sa_key_set = 1; 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, 760268240Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 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, 842268240Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 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 862268240Sken if (verbosemode != 0) { 863268240Sken cam_error_print(device, ccb, CAM_ESF_ALL, 864268240Sken CAM_EPF_ALL, stderr); 865268240Sken } 866268240Sken 867268240Sken error = 1; 868268240Sken goto bailout; 869268240Sken } 870268240Sken 871268240Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 872268240Sken if (verbosemode != 0) { 873268240Sken cam_error_print(device, ccb, CAM_ESF_ALL, 874268240Sken CAM_EPF_ALL, stderr); 875268240Sken } 876268240Sken error = 1; 877268240Sken goto bailout; 878268240Sken } 879268240Sken 880268240Sken if (in == 0) 881268240Sken goto bailout; 882268240Sken 883268240Sken valid_len = res_len - ccb->csio.resid; 884268240Sken 885268240Sken switch (action) { 886268240Sken case SPRI_RK: 887268240Sken case SPRI_RR: 888268240Sken case SPRI_RS: { 889268240Sken struct scsi_per_res_in_header *hdr; 890268240Sken uint32_t hdr_len; 891268240Sken 892268240Sken if (valid_len < sizeof(*hdr)) { 893268240Sken warnx("%s: only got %d valid bytes, need %zd", 894268240Sken __func__, valid_len, sizeof(*hdr)); 895268240Sken error = 1; 896268240Sken goto bailout; 897268240Sken } 898268240Sken hdr = (struct scsi_per_res_in_header *)res_buf; 899268240Sken hdr_len = scsi_4btoul(hdr->length); 900268240Sken 901268240Sken if (hdr_len > (res_len - sizeof(*hdr))) { 902268240Sken res_len = hdr_len + sizeof(*hdr); 903268240Sken goto retry; 904268240Sken } 905268240Sken 906268240Sken if (action == SPRI_RK) { 907268240Sken persist_print_keys(hdr, valid_len); 908268240Sken } else if (action == SPRI_RR) { 909268240Sken persist_print_res(hdr, valid_len); 910268240Sken } else { 911268240Sken persist_print_full(hdr, valid_len); 912268240Sken } 913268240Sken break; 914268240Sken } 915268240Sken case SPRI_RC: { 916268240Sken struct scsi_per_res_cap *cap; 917268240Sken uint32_t cap_len; 918268240Sken 919268240Sken if (valid_len < sizeof(*cap)) { 920268240Sken warnx("%s: only got %u valid bytes, need %zd", 921268240Sken __func__, valid_len, sizeof(*cap)); 922268240Sken error = 1; 923268240Sken goto bailout; 924268240Sken } 925268240Sken cap = (struct scsi_per_res_cap *)res_buf; 926268240Sken cap_len = scsi_2btoul(cap->length); 927268240Sken if (cap_len != sizeof(*cap)) { 928268240Sken /* 929268240Sken * We should be able to deal with this, 930268240Sken * it's just more trouble. 931268240Sken */ 932268240Sken warnx("%s: reported size %u is different " 933268240Sken "than expected size %zd", __func__, 934268240Sken cap_len, sizeof(*cap)); 935268240Sken } 936268240Sken 937268240Sken /* 938268240Sken * If there is more data available, grab it all, 939268240Sken * even though we don't really know what to do with 940268240Sken * the extra data since it obviously wasn't in the 941268240Sken * spec when this code was written. 942268240Sken */ 943268240Sken if (cap_len > res_len) { 944268240Sken res_len = cap_len; 945268240Sken goto retry; 946268240Sken } 947268240Sken persist_print_cap(cap, valid_len); 948268240Sken break; 949268240Sken } 950268240Sken default: 951268240Sken break; 952268240Sken } 953268240Sken 954268240Skenbailout: 955268240Sken free(res_buf); 956268240Sken 957268240Sken if (ccb != NULL) 958268240Sken cam_freeccb(ccb); 959268240Sken 960268240Sken STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) { 961268240Sken STAILQ_REMOVE(&transport_id_list, id, persist_transport_id, 962268240Sken links); 963268240Sken free(id); 964268240Sken } 965268240Sken return (error); 966268240Sken} 967