1 /*-
2 * Copyright (c) 2013 Spectra Logic Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * substantially similar to the "NO WARRANTY" disclaimer below
13 * ("Disclaimer") and any redistribution must be conditioned upon
14 * including a substantially similar Disclaimer requirement for further
15 * binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
29 *
30 * Authors: Ken Merry (Spectra Logic Corporation)
31 */
32 /*
33 * SCSI Persistent Reservation support for camcontrol(8).
34 */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include <sys/ioctl.h>
40 #include <sys/stdint.h>
41 #include <sys/types.h>
42 #include <sys/endian.h>
43 #include <sys/sbuf.h>
44 #include <sys/queue.h>
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <inttypes.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <strings.h>
52 #include <fcntl.h>
53 #include <ctype.h>
54 #include <limits.h>
55 #include <err.h>
56
57 #include <cam/cam.h>
58 #include <cam/cam_debug.h>
59 #include <cam/cam_ccb.h>
60 #include <cam/scsi/scsi_all.h>
61 #include <cam/scsi/scsi_pass.h>
62 #include <cam/scsi/scsi_message.h>
63 #include <camlib.h>
64 #include "camcontrol.h"
65
66 struct persist_transport_id {
67 struct scsi_transportid_header *hdr;
68 unsigned int alloc_len;
69 STAILQ_ENTRY(persist_transport_id) links;
70 };
71
72 /*
73 * Service Actions for PERSISTENT RESERVE IN.
74 */
75 static struct scsi_nv persist_in_actions[] = {
76 { "read_keys", SPRI_RK },
77 { "read_reservation", SPRI_RR },
78 { "report_capabilities", SPRI_RC },
79 { "read_full_status", SPRI_RS }
80 };
81
82 /*
83 * Service Actions for PERSISTENT RESERVE OUT.
84 */
85 static struct scsi_nv persist_out_actions[] = {
86 { "register", SPRO_REGISTER },
87 { "reserve", SPRO_RESERVE },
88 { "release" , SPRO_RELEASE },
89 { "clear", SPRO_CLEAR },
90 { "preempt", SPRO_PREEMPT },
91 { "preempt_abort", SPRO_PRE_ABO },
92 { "register_ignore", SPRO_REG_IGNO },
93 { "register_move", SPRO_REG_MOVE },
94 { "replace_lost", SPRO_REPL_LOST_RES }
95 };
96
97 /*
98 * Known reservation scopes. As of SPC-4, only LU_SCOPE is used in the
99 * spec. The others are obsolete.
100 */
101 static struct scsi_nv persist_scope_table[] = {
102 { "lun", SPR_LU_SCOPE },
103 { "extent", SPR_EXTENT_SCOPE },
104 { "element", SPR_ELEMENT_SCOPE }
105 };
106
107 /*
108 * Reservation types. The longer name for a given reservation type is
109 * listed first, so that it makes more sense when we print out the
110 * reservation type. We step through the table linearly when looking for
111 * the text name for a particular numeric reservation type value.
112 */
113 static struct scsi_nv persist_type_table[] = {
114 { "read_shared", SPR_TYPE_RD_SHARED },
115 { "write_exclusive", SPR_TYPE_WR_EX },
116 { "wr_ex", SPR_TYPE_WR_EX },
117 { "read_exclusive", SPR_TYPE_RD_EX },
118 { "rd_ex", SPR_TYPE_RD_EX },
119 { "exclusive_access", SPR_TYPE_EX_AC },
120 { "ex_ac", SPR_TYPE_EX_AC },
121 { "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
122 { "wr_ex_ro", SPR_TYPE_WR_EX_RO },
123 { "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
124 { "ex_ac_ro", SPR_TYPE_EX_AC_RO },
125 { "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
126 { "wr_ex_ar", SPR_TYPE_WR_EX_AR },
127 { "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
128 { "ex_ac_ar", SPR_TYPE_EX_AC_AR }
129 };
130
131 /*
132 * Print out the standard scope/type field.
133 */
134 static void
persist_print_scopetype(uint8_t scopetype)135 persist_print_scopetype(uint8_t scopetype)
136 {
137 const char *tmpstr;
138 int num_entries;
139
140 num_entries = sizeof(persist_scope_table) /
141 sizeof(persist_scope_table[0]);
142 tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
143 scopetype & SPR_SCOPE_MASK);
144 fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
145 "Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
146
147 num_entries = sizeof(persist_type_table) /
148 sizeof(persist_type_table[0]);
149 tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
150 scopetype & SPR_TYPE_MASK);
151 fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
152 "Unknown", scopetype & SPR_TYPE_MASK);
153 }
154
155 static void
persist_print_transportid(uint8_t * buf,uint32_t len)156 persist_print_transportid(uint8_t *buf, uint32_t len)
157 {
158 struct sbuf *sb;
159
160 sb = sbuf_new_auto();
161 if (sb == NULL)
162 fprintf(stderr, "Unable to allocate sbuf\n");
163
164 scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
165
166 sbuf_finish(sb);
167
168 fprintf(stdout, "%s\n", sbuf_data(sb));
169
170 sbuf_delete(sb);
171 }
172
173 /*
174 * Print out a persistent reservation. This is used with the READ
175 * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
176 */
177 static void
persist_print_res(struct scsi_per_res_in_header * hdr,uint32_t valid_len)178 persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
179 {
180 uint32_t length;
181 struct scsi_per_res_in_rsrv *res;
182
183 length = scsi_4btoul(hdr->length);
184 length = MIN(length, valid_len);
185
186 res = (struct scsi_per_res_in_rsrv *)hdr;
187
188 if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
189 if (length == 0)
190 fprintf(stdout, "No reservations.\n");
191 else
192 warnx("unable to print reservation, only got %u "
193 "valid bytes", length);
194 return;
195 }
196 fprintf(stdout, "PRgeneration: %#x\n",
197 scsi_4btoul(res->header.generation));
198 fprintf(stdout, "Reservation Key: %#jx\n",
199 (uintmax_t)scsi_8btou64(res->data.reservation));
200 fprintf(stdout, "Scope address: %#x\n",
201 scsi_4btoul(res->data.scope_addr));
202
203 persist_print_scopetype(res->data.scopetype);
204
205 fprintf(stdout, "Extent length: %u\n",
206 scsi_2btoul(res->data.extent_length));
207 }
208
209 /*
210 * Print out persistent reservation keys. This is used with the READ KEYS
211 * service action of the PERSISTENT RESERVE IN command.
212 */
213 static void
persist_print_keys(struct scsi_per_res_in_header * hdr,uint32_t valid_len)214 persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
215 {
216 uint32_t length, num_keys, i;
217 struct scsi_per_res_key *key;
218
219 length = scsi_4btoul(hdr->length);
220 length = MIN(length, valid_len);
221
222 num_keys = length / sizeof(*key);
223
224 fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
225 fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
226 (num_keys == 0) ? "." : ":");
227
228 for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
229 i++, key++) {
230 fprintf(stdout, "%u: %#jx\n", i,
231 (uintmax_t)scsi_8btou64(key->key));
232 }
233 }
234
235 /*
236 * Print out persistent reservation capabilities. This is used with the
237 * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
238 */
239 static void
persist_print_cap(struct scsi_per_res_cap * cap,uint32_t valid_len)240 persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
241 {
242 uint32_t length;
243 int check_type_mask = 0;
244 uint32_t type_mask;
245
246 length = scsi_2btoul(cap->length);
247 length = MIN(length, valid_len);
248 type_mask = scsi_2btoul(cap->type_mask);
249
250 if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
251 fprintf(stdout, "Insufficient data (%u bytes) to report "
252 "full capabilities\n", length);
253 return;
254 }
255 if (length >= __offsetof(struct scsi_per_res_cap, reserved))
256 check_type_mask = 1;
257
258 fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
259 (cap->flags1 & SPRI_RLR_C) ? 1 : 0);
260 fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
261 (cap->flags1 & SPRI_CRH) ? 1 : 0);
262 fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
263 (cap->flags1 & SPRI_SIP_C) ? 1 : 0);
264 fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
265 (cap->flags1 & SPRI_ATP_C) ? 1 : 0);
266 fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
267 (cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
268 fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
269 (cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
270 /*
271 * These cases are cut-and-pasted from SPC4r36l. There is no
272 * succinct way to describe these otherwise, and even with the
273 * verbose description, the user will probably have to refer to
274 * the spec to fully understand what is going on.
275 */
276 switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
277 case SPRI_ALLOW_1:
278 fprintf(stdout,
279 " The device server allows the TEST UNIT READY command through Write\n"
280 " Exclusive type reservations and Exclusive Access type reservations\n"
281 " and does not provide information about whether the following commands\n"
282 " are allowed through Write Exclusive type reservations:\n"
283 " a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
284 " command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
285 " RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
286 " and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
287 " b) the READ DEFECT DATA command (see SBC-3).\n");
288 break;
289 case SPRI_ALLOW_2:
290 fprintf(stdout,
291 " The device server allows the TEST UNIT READY command through Write\n"
292 " Exclusive type reservations and Exclusive Access type reservations\n"
293 " and does not allow the following commands through Write Exclusive type\n"
294 " reservations:\n"
295 " a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
296 " command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
297 " OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
298 " FUNCTION command; and\n"
299 " b) the READ DEFECT DATA command.\n"
300 " The device server does not allow the RECEIVE COPY RESULTS command\n"
301 " through Write Exclusive type reservations or Exclusive Access type\n"
302 " reservations.\n");
303 break;
304 case SPRI_ALLOW_3:
305 fprintf(stdout,
306 " The device server allows the TEST UNIT READY command through Write\n"
307 " Exclusive type reservations and Exclusive Access type reservations\n"
308 " and allows the following commands through Write Exclusive type\n"
309 " reservations:\n"
310 " a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
311 " command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
312 " OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
313 " FUNCTION command; and\n"
314 " b) the READ DEFECT DATA command.\n"
315 " The device server does not allow the RECEIVE COPY RESULTS command\n"
316 " through Write Exclusive type reservations or Exclusive Access type\n"
317 " reservations.\n");
318 break;
319 case SPRI_ALLOW_4:
320 fprintf(stdout,
321 " The device server allows the TEST UNIT READY command and the RECEIVE\n"
322 " COPY RESULTS command through Write Exclusive type reservations and\n"
323 " Exclusive Access type reservations and allows the following commands\n"
324 " through Write Exclusive type reservations:\n"
325 " a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
326 " command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
327 " OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
328 " FUNCTION command; and\n"
329 " b) the READ DEFECT DATA command.\n");
330 break;
331 case SPRI_ALLOW_NA:
332 fprintf(stdout,
333 " No information is provided about whether certain commands are allowed\n"
334 " through certain types of persistent reservations.\n");
335 break;
336 default:
337 fprintf(stdout,
338 " Unknown ALLOW COMMANDS value %#x\n",
339 (cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
340 SPRI_ALLOW_CMD_SHIFT);
341 break;
342 }
343 fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
344 (cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
345 if ((check_type_mask != 0)
346 && (cap->flags2 & SPRI_TMV)) {
347 fprintf(stdout, "Supported Persistent Reservation Types:\n");
348 fprintf(stdout, " Write Exclusive - All Registrants "
349 "(WR_EX_AR): %d\n",
350 (type_mask & SPRI_TM_WR_EX_AR)? 1 : 0);
351 fprintf(stdout, " Exclusive Access - Registrants Only "
352 "(EX_AC_RO): %d\n",
353 (type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0);
354 fprintf(stdout, " Write Exclusive - Registrants Only "
355 "(WR_EX_RO): %d\n",
356 (type_mask & SPRI_TM_WR_EX_RO)? 1 : 0);
357 fprintf(stdout, " Exclusive Access (EX_AC): %d\n",
358 (type_mask & SPRI_TM_EX_AC) ? 1 : 0);
359 fprintf(stdout, " Write Exclusive (WR_EX): %d\n",
360 (type_mask & SPRI_TM_WR_EX) ? 1 : 0);
361 fprintf(stdout, " Exclusive Access - All Registrants "
362 "(EX_AC_AR): %d\n",
363 (type_mask & SPRI_TM_EX_AC_AR) ? 1 : 0);
364 } else {
365 fprintf(stdout, "Persistent Reservation Type Mask is NOT "
366 "valid\n");
367 }
368
369
370 }
371
372 static void
persist_print_full(struct scsi_per_res_in_header * hdr,uint32_t valid_len)373 persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
374 {
375 uint32_t length, len_to_go = 0;
376 struct scsi_per_res_in_full_desc *desc;
377 uint8_t *cur_pos;
378 int i;
379
380 length = scsi_4btoul(hdr->length);
381 length = MIN(length, valid_len);
382
383 if (length < sizeof(*desc)) {
384 if (length == 0)
385 fprintf(stdout, "No reservations.\n");
386 else
387 warnx("unable to print reservation, only got %u "
388 "valid bytes", length);
389 return;
390 }
391
392 fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
393 cur_pos = (uint8_t *)&hdr[1];
394 for (len_to_go = length, i = 0,
395 desc = (struct scsi_per_res_in_full_desc *)cur_pos;
396 len_to_go >= sizeof(*desc);
397 desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
398 uint32_t additional_length, cur_length;
399
400
401 fprintf(stdout, "Reservation Key: %#jx\n",
402 (uintmax_t)scsi_8btou64(desc->res_key.key));
403 fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
404 (desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
405 fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
406 (desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
407
408 if (desc->flags & SPRI_FULL_R_HOLDER)
409 persist_print_scopetype(desc->scopetype);
410
411 if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
412 fprintf(stdout, "Relative Target Port ID: %#x\n",
413 scsi_2btoul(desc->rel_trgt_port_id));
414
415 additional_length = scsi_4btoul(desc->additional_length);
416
417 persist_print_transportid(desc->transport_id,
418 additional_length);
419
420 cur_length = sizeof(*desc) + additional_length;
421 len_to_go -= cur_length;
422 cur_pos += cur_length;
423 }
424 }
425
426 int
scsipersist(struct cam_device * device,int argc,char ** argv,char * combinedopt,int task_attr,int retry_count,int timeout,int verbosemode,int err_recover)427 scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
428 int task_attr, int retry_count, int timeout, int verbosemode,
429 int err_recover)
430 {
431 union ccb *ccb = NULL;
432 int c, in = 0, out = 0;
433 int action = -1, num_ids = 0;
434 int error = 0;
435 uint32_t res_len = 0;
436 unsigned long rel_tgt_port = 0;
437 uint8_t *res_buf = NULL;
438 int scope = SPR_LU_SCOPE, res_type = 0;
439 struct persist_transport_id *id, *id2;
440 STAILQ_HEAD(, persist_transport_id) transport_id_list;
441 uint64_t key = 0, sa_key = 0;
442 struct scsi_nv *table = NULL;
443 size_t table_size = 0, id_len = 0;
444 uint32_t valid_len = 0;
445 int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
446
447 STAILQ_INIT(&transport_id_list);
448
449 ccb = cam_getccb(device);
450 if (ccb == NULL) {
451 warnx("%s: error allocating CCB", __func__);
452 error = 1;
453 goto bailout;
454 }
455
456 CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
457
458 while ((c = getopt(argc, argv, combinedopt)) != -1) {
459 switch (c) {
460 case 'a':
461 all_tg_pt = 1;
462 break;
463 case 'I': {
464 int error_str_len = 128;
465 char error_str[error_str_len];
466 char *id_str;
467
468 id = malloc(sizeof(*id));
469 if (id == NULL) {
470 warnx("%s: error allocating %zu bytes",
471 __func__, sizeof(*id));
472 error = 1;
473 goto bailout;
474 }
475 bzero(id, sizeof(*id));
476
477 id_str = strdup(optarg);
478 if (id_str == NULL) {
479 warnx("%s: error duplicating string %s",
480 __func__, optarg);
481 free(id);
482 error = 1;
483 goto bailout;
484 }
485 error = scsi_parse_transportid(id_str, &id->hdr,
486 &id->alloc_len, error_str, error_str_len);
487 if (error != 0) {
488 warnx("%s", error_str);
489 error = 1;
490 free(id);
491 free(id_str);
492 goto bailout;
493 }
494 free(id_str);
495
496 STAILQ_INSERT_TAIL(&transport_id_list, id, links);
497 num_ids++;
498 id_len += id->alloc_len;
499 break;
500 }
501 case 'k':
502 case 'K': {
503 char *endptr;
504 uint64_t tmpval;
505
506 tmpval = strtoumax(optarg, &endptr, 0);
507 if (*endptr != '\0') {
508 warnx("%s: invalid key argument %s", __func__,
509 optarg);
510 error = 1;
511 goto bailout;
512 }
513 if (c == 'k') {
514 key = tmpval;
515 } else {
516 sa_key = tmpval;
517 }
518 break;
519 }
520 case 'i':
521 case 'o': {
522 scsi_nv_status status;
523 int table_entry = 0;
524
525 if (c == 'i') {
526 in = 1;
527 table = persist_in_actions;
528 table_size = sizeof(persist_in_actions) /
529 sizeof(persist_in_actions[0]);
530 } else {
531 out = 1;
532 table = persist_out_actions;
533 table_size = sizeof(persist_out_actions) /
534 sizeof(persist_out_actions[0]);
535 }
536
537 if ((in + out) > 1) {
538 warnx("%s: only one in (-i) or out (-o) "
539 "action is allowed", __func__);
540 error = 1;
541 goto bailout;
542 }
543
544 status = scsi_get_nv(table, table_size, optarg,
545 &table_entry,SCSI_NV_FLAG_IG_CASE);
546 if (status == SCSI_NV_FOUND)
547 action = table[table_entry].value;
548 else {
549 warnx("%s: %s %s option %s", __func__,
550 (status == SCSI_NV_AMBIGUOUS) ?
551 "ambiguous" : "invalid", in ? "in" :
552 "out", optarg);
553 error = 1;
554 goto bailout;
555 }
556 break;
557 }
558 case 'p':
559 aptpl = 1;
560 break;
561 case 'R': {
562 char *endptr;
563
564 rel_tgt_port = strtoul(optarg, &endptr, 0);
565 if (*endptr != '\0') {
566 warnx("%s: invalid relative target port %s",
567 __func__, optarg);
568 error = 1;
569 goto bailout;
570 }
571 rel_port_set = 1;
572 break;
573 }
574 case 's': {
575 size_t scope_size;
576 struct scsi_nv *scope_table = NULL;
577 scsi_nv_status status;
578 int table_entry = 0;
579 char *endptr;
580
581 /*
582 * First check to see if the user gave us a numeric
583 * argument. If so, we'll try using it.
584 */
585 if (isdigit(optarg[0])) {
586 scope = strtol(optarg, &endptr, 0);
587 if (*endptr != '\0') {
588 warnx("%s: invalid scope %s",
589 __func__, optarg);
590 error = 1;
591 goto bailout;
592 }
593 scope = (scope << SPR_SCOPE_SHIFT) &
594 SPR_SCOPE_MASK;
595 break;
596 }
597
598 scope_size = sizeof(persist_scope_table) /
599 sizeof(persist_scope_table[0]);
600 scope_table = persist_scope_table;
601 status = scsi_get_nv(scope_table, scope_size, optarg,
602 &table_entry,SCSI_NV_FLAG_IG_CASE);
603 if (status == SCSI_NV_FOUND)
604 scope = scope_table[table_entry].value;
605 else {
606 warnx("%s: %s scope %s", __func__,
607 (status == SCSI_NV_AMBIGUOUS) ?
608 "ambiguous" : "invalid", optarg);
609 error = 1;
610 goto bailout;
611 }
612 break;
613 }
614 case 'S':
615 spec_i_pt = 1;
616 break;
617 case 'T': {
618 size_t res_type_size;
619 struct scsi_nv *rtype_table = NULL;
620 scsi_nv_status status;
621 char *endptr;
622 int table_entry = 0;
623
624 /*
625 * First check to see if the user gave us a numeric
626 * argument. If so, we'll try using it.
627 */
628 if (isdigit(optarg[0])) {
629 res_type = strtol(optarg, &endptr, 0);
630 if (*endptr != '\0') {
631 warnx("%s: invalid reservation type %s",
632 __func__, optarg);
633 error = 1;
634 goto bailout;
635 }
636 break;
637 }
638
639 res_type_size = sizeof(persist_type_table) /
640 sizeof(persist_type_table[0]);
641 rtype_table = persist_type_table;
642 status = scsi_get_nv(rtype_table, res_type_size,
643 optarg, &table_entry,
644 SCSI_NV_FLAG_IG_CASE);
645 if (status == SCSI_NV_FOUND)
646 res_type = rtype_table[table_entry].value;
647 else {
648 warnx("%s: %s reservation type %s", __func__,
649 (status == SCSI_NV_AMBIGUOUS) ?
650 "ambiguous" : "invalid", optarg);
651 error = 1;
652 goto bailout;
653 }
654 break;
655 }
656 case 'U':
657 unreg = 1;
658 break;
659 default:
660 break;
661 }
662 }
663
664 if ((in + out) != 1) {
665 warnx("%s: you must specify one of -i or -o", __func__);
666 error = 1;
667 goto bailout;
668 }
669
670 /*
671 * Note that we don't really try to figure out whether the user
672 * needs to specify one or both keys. There are a number of
673 * scenarios, and sometimes 0 is a valid and desired value.
674 */
675 if (in != 0) {
676 switch (action) {
677 case SPRI_RK:
678 case SPRI_RR:
679 case SPRI_RS:
680 /*
681 * Allocate the maximum length possible for these
682 * service actions. According to the spec, the
683 * target is supposed to return the available
684 * length in the header, regardless of the
685 * allocation length. In practice, though, with
686 * the READ FULL STATUS (SPRI_RS) service action,
687 * some Seagate drives (in particular a
688 * Constellation ES, <SEAGATE ST32000444SS 0006>)
689 * don't return the available length if you only
690 * allocate the length of the header. So just
691 * allocate the maximum here so we don't miss
692 * anything.
693 */
694 res_len = SPRI_MAX_LEN;
695 break;
696 case SPRI_RC:
697 res_len = sizeof(struct scsi_per_res_cap);
698 break;
699 default:
700 /* In theory we should catch this above */
701 warnx("%s: invalid action %d", __func__, action);
702 error = 1;
703 goto bailout;
704 break;
705 }
706 } else {
707
708 /*
709 * XXX KDM need to add length for transport IDs for the
710 * register and move service action and the register
711 * service action with the SPEC_I_PT bit set.
712 */
713 if (action == SPRO_REG_MOVE) {
714 if (num_ids != 1) {
715 warnx("%s: register and move requires a "
716 "single transport ID (-I)", __func__);
717 error = 1;
718 goto bailout;
719 }
720 if (rel_port_set == 0) {
721 warnx("%s: register and move requires a "
722 "relative target port (-R)", __func__);
723 error = 1;
724 goto bailout;
725 }
726 res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
727 } else {
728 res_len = sizeof(struct scsi_per_res_out_parms);
729 if ((action == SPRO_REGISTER)
730 && (num_ids != 0)) {
731 /*
732 * If the user specifies any IDs with the
733 * register service action, turn on the
734 * spec_i_pt bit.
735 */
736 spec_i_pt = 1;
737 res_len += id_len;
738 res_len +=
739 sizeof(struct scsi_per_res_out_trans_ids);
740 }
741 }
742 }
743 retry:
744 if (res_buf != NULL) {
745 free(res_buf);
746 res_buf = NULL;
747 }
748 res_buf = malloc(res_len);
749 if (res_buf == NULL) {
750 warn("%s: error allocating %d bytes", __func__, res_len);
751 error = 1;
752 goto bailout;
753 }
754 bzero(res_buf, res_len);
755
756 if (in != 0) {
757 scsi_persistent_reserve_in(&ccb->csio,
758 /*retries*/ retry_count,
759 /*cbfcnp*/ NULL,
760 /*tag_action*/ task_attr,
761 /*service_action*/ action,
762 /*data_ptr*/ res_buf,
763 /*dxfer_len*/ res_len,
764 /*sense_len*/ SSD_FULL_SIZE,
765 /*timeout*/ timeout ? timeout :5000);
766
767 } else {
768 switch (action) {
769 case SPRO_REGISTER:
770 if (spec_i_pt != 0) {
771 struct scsi_per_res_out_trans_ids *id_hdr;
772 uint8_t *bufptr;
773
774 bufptr = res_buf +
775 sizeof(struct scsi_per_res_out_parms) +
776 sizeof(struct scsi_per_res_out_trans_ids);
777 STAILQ_FOREACH(id, &transport_id_list, links) {
778 bcopy(id->hdr, bufptr, id->alloc_len);
779 bufptr += id->alloc_len;
780 }
781 id_hdr = (struct scsi_per_res_out_trans_ids *)
782 (res_buf +
783 sizeof(struct scsi_per_res_out_parms));
784 scsi_ulto4b(id_len, id_hdr->additional_length);
785 }
786 case SPRO_REG_IGNO:
787 case SPRO_PREEMPT:
788 case SPRO_PRE_ABO:
789 case SPRO_RESERVE:
790 case SPRO_RELEASE:
791 case SPRO_CLEAR:
792 case SPRO_REPL_LOST_RES: {
793 struct scsi_per_res_out_parms *parms;
794
795 parms = (struct scsi_per_res_out_parms *)res_buf;
796
797 scsi_u64to8b(key, parms->res_key.key);
798 scsi_u64to8b(sa_key, parms->serv_act_res_key);
799 if (spec_i_pt != 0)
800 parms->flags |= SPR_SPEC_I_PT;
801 if (all_tg_pt != 0)
802 parms->flags |= SPR_ALL_TG_PT;
803 if (aptpl != 0)
804 parms->flags |= SPR_APTPL;
805 break;
806 }
807 case SPRO_REG_MOVE: {
808 struct scsi_per_res_reg_move *reg_move;
809 uint8_t *bufptr;
810
811 reg_move = (struct scsi_per_res_reg_move *)res_buf;
812
813 scsi_u64to8b(key, reg_move->res_key.key);
814 scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
815 if (unreg != 0)
816 reg_move->flags |= SPR_REG_MOVE_UNREG;
817 if (aptpl != 0)
818 reg_move->flags |= SPR_REG_MOVE_APTPL;
819 scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
820 id = STAILQ_FIRST(&transport_id_list);
821 /*
822 * This shouldn't happen, since we already checked
823 * the number of IDs above.
824 */
825 if (id == NULL) {
826 warnx("%s: No transport IDs found!", __func__);
827 error = 1;
828 goto bailout;
829 }
830 bufptr = (uint8_t *)®_move[1];
831 bcopy(id->hdr, bufptr, id->alloc_len);
832 scsi_ulto4b(id->alloc_len,
833 reg_move->transport_id_length);
834 break;
835 }
836 default:
837 break;
838 }
839 scsi_persistent_reserve_out(&ccb->csio,
840 /*retries*/ retry_count,
841 /*cbfcnp*/ NULL,
842 /*tag_action*/ task_attr,
843 /*service_action*/ action,
844 /*scope*/ scope,
845 /*res_type*/ res_type,
846 /*data_ptr*/ res_buf,
847 /*dxfer_len*/ res_len,
848 /*sense_len*/ SSD_FULL_SIZE,
849 /*timeout*/ timeout ?timeout :5000);
850 }
851
852 /* Disable freezing the device queue */
853 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
854
855 if (err_recover != 0)
856 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
857
858 if (cam_send_ccb(device, ccb) < 0) {
859 warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
860 "IN" : "OUT");
861 error = 1;
862 goto bailout;
863 }
864
865 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
866 if (verbosemode != 0) {
867 cam_error_print(device, ccb, CAM_ESF_ALL,
868 CAM_EPF_ALL, stderr);
869 }
870 error = 1;
871 goto bailout;
872 }
873
874 if (in == 0)
875 goto bailout;
876
877 valid_len = res_len - ccb->csio.resid;
878
879 switch (action) {
880 case SPRI_RK:
881 case SPRI_RR:
882 case SPRI_RS: {
883 struct scsi_per_res_in_header *hdr;
884 uint32_t hdr_len;
885
886 if (valid_len < sizeof(*hdr)) {
887 warnx("%s: only got %d valid bytes, need %zd",
888 __func__, valid_len, sizeof(*hdr));
889 error = 1;
890 goto bailout;
891 }
892 hdr = (struct scsi_per_res_in_header *)res_buf;
893 hdr_len = scsi_4btoul(hdr->length);
894
895 if (hdr_len > (res_len - sizeof(*hdr))) {
896 res_len = hdr_len + sizeof(*hdr);
897 goto retry;
898 }
899
900 if (action == SPRI_RK) {
901 persist_print_keys(hdr, valid_len);
902 } else if (action == SPRI_RR) {
903 persist_print_res(hdr, valid_len);
904 } else {
905 persist_print_full(hdr, valid_len);
906 }
907 break;
908 }
909 case SPRI_RC: {
910 struct scsi_per_res_cap *cap;
911 uint32_t cap_len;
912
913 if (valid_len < sizeof(*cap)) {
914 warnx("%s: only got %u valid bytes, need %zd",
915 __func__, valid_len, sizeof(*cap));
916 error = 1;
917 goto bailout;
918 }
919 cap = (struct scsi_per_res_cap *)res_buf;
920 cap_len = scsi_2btoul(cap->length);
921 if (cap_len != sizeof(*cap)) {
922 /*
923 * We should be able to deal with this,
924 * it's just more trouble.
925 */
926 warnx("%s: reported size %u is different "
927 "than expected size %zd", __func__,
928 cap_len, sizeof(*cap));
929 }
930
931 /*
932 * If there is more data available, grab it all,
933 * even though we don't really know what to do with
934 * the extra data since it obviously wasn't in the
935 * spec when this code was written.
936 */
937 if (cap_len > res_len) {
938 res_len = cap_len;
939 goto retry;
940 }
941 persist_print_cap(cap, valid_len);
942 break;
943 }
944 default:
945 break;
946 }
947
948 bailout:
949 free(res_buf);
950
951 if (ccb != NULL)
952 cam_freeccb(ccb);
953
954 STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
955 STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
956 links);
957 free(id);
958 }
959 return (error);
960 }
961