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