2 * Copyright (c) 2015, 2016 Spectra Logic Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 * Authors: Ken Merry (Spectra Logic Corporation)
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/ioctl.h>
37 #include <sys/stdint.h>
38 #include <sys/types.h>
39 #include <sys/endian.h>
41 #include <sys/queue.h>
43 #include <sys/disk_zone.h>
57 #include <cam/cam_debug.h>
58 #include <cam/cam_ccb.h>
59 #include <cam/scsi/scsi_all.h>
61 static struct scsi_nv zone_cmd_map
[] = {
62 { "rz", DISK_ZONE_REPORT_ZONES
},
63 { "reportzones", DISK_ZONE_REPORT_ZONES
},
64 { "close", DISK_ZONE_CLOSE
},
65 { "finish", DISK_ZONE_FINISH
},
66 { "open", DISK_ZONE_OPEN
},
67 { "rwp", DISK_ZONE_RWP
},
68 { "params", DISK_ZONE_GET_PARAMS
}
71 static struct scsi_nv zone_rep_opts
[] = {
72 { "all", DISK_ZONE_REP_ALL
},
73 { "empty", DISK_ZONE_REP_EMPTY
},
74 { "imp_open", DISK_ZONE_REP_IMP_OPEN
},
75 { "exp_open", DISK_ZONE_REP_EXP_OPEN
},
76 { "closed", DISK_ZONE_REP_CLOSED
},
77 { "full", DISK_ZONE_REP_FULL
},
78 { "readonly", DISK_ZONE_REP_READONLY
},
79 { "ro", DISK_ZONE_REP_READONLY
},
80 { "offline", DISK_ZONE_REP_OFFLINE
},
81 { "reset", DISK_ZONE_REP_RWP
},
82 { "rwp", DISK_ZONE_REP_RWP
},
83 { "nonseq", DISK_ZONE_REP_NON_SEQ
},
84 { "nonwp", DISK_ZONE_REP_NON_WP
}
89 ZONE_OF_NORMAL
= 0x00,
90 ZONE_OF_SUMMARY
= 0x01,
94 static struct scsi_nv zone_print_opts
[] = {
95 { "normal", ZONE_OF_NORMAL
},
96 { "summary", ZONE_OF_SUMMARY
},
97 { "script", ZONE_OF_SCRIPT
}
100 static struct scsi_nv zone_cmd_desc_table
[] = {
101 {"Report Zones", DISK_ZONE_RZ_SUP
},
102 {"Open", DISK_ZONE_OPEN_SUP
},
103 {"Close", DISK_ZONE_CLOSE_SUP
},
104 {"Finish", DISK_ZONE_FINISH_SUP
},
105 {"Reset Write Pointer", DISK_ZONE_RWP_SUP
}
110 ZONE_PRINT_MORE_DATA
,
126 static void usage(int error
);
127 static void zonectl_print_params(struct disk_zone_disk_params
*params
);
128 zone_print_status
zonectl_print_rz(struct disk_zone_report
*report
,
129 zone_output_flags out_flags
, int first_pass
);
134 fprintf(error
? stderr
: stdout
,
135 "usage: zonectl <-d dev> <-c cmd> [-a][-o rep_opts] [-l lba][-P print_opts]\n"
140 zonectl_print_params(struct disk_zone_disk_params
*params
)
145 printf("Zone Mode: ");
146 switch (params
->zone_mode
) {
147 case DISK_ZONE_MODE_NONE
:
150 case DISK_ZONE_MODE_HOST_AWARE
:
151 printf("Host Aware");
153 case DISK_ZONE_MODE_DRIVE_MANAGED
:
154 printf("Drive Managed");
156 case DISK_ZONE_MODE_HOST_MANAGED
:
157 printf("Host Managed");
160 printf("Unknown mode %#x", params
->zone_mode
);
166 printf("Command support: ");
167 for (i
= 0; i
< sizeof(zone_cmd_desc_table
) /
168 sizeof(zone_cmd_desc_table
[0]); i
++) {
169 if (params
->flags
& zone_cmd_desc_table
[i
].value
) {
174 printf("%s", zone_cmd_desc_table
[i
].name
);
181 printf("Unrestricted Read in Sequential Write Required Zone "
182 "(URSWRZ): %s\n", (params
->flags
& DISK_ZONE_DISK_URSWRZ
) ?
185 printf("Optimal Number of Open Sequential Write Preferred Zones: ");
186 if (params
->flags
& DISK_ZONE_OPT_SEQ_SET
)
187 if (params
->optimal_seq_zones
== SVPD_ZBDC_OPT_SEQ_NR
)
188 printf("Not Reported");
190 printf("%ju", (uintmax_t)params
->optimal_seq_zones
);
196 printf("Optimal Number of Non-Sequentially Written Sequential Write "
197 "Preferred Zones: ");
198 if (params
->flags
& DISK_ZONE_OPT_NONSEQ_SET
)
199 if (params
->optimal_nonseq_zones
== SVPD_ZBDC_OPT_NONSEQ_NR
)
200 printf("Not Reported");
202 printf("%ju",(uintmax_t)params
->optimal_nonseq_zones
);
207 printf("Maximum Number of Open Sequential Write Required Zones: ");
208 if (params
->flags
& DISK_ZONE_MAX_SEQ_SET
)
209 if (params
->max_seq_zones
== SVPD_ZBDC_MAX_SEQ_UNLIMITED
)
212 printf("%ju", (uintmax_t)params
->max_seq_zones
);
219 zonectl_print_rz(struct disk_zone_report
*report
, zone_output_flags out_flags
,
222 zone_print_status status
= ZONE_PRINT_OK
;
223 struct disk_zone_rep_header
*header
= &report
->header
;
224 int field_widths
[ZONE_NUM_FIELDS
];
225 struct disk_zone_rep_entry
*entry
;
226 uint64_t next_lba
= 0;
232 field_widths
[ZONE_FW_START
] = 11;
233 field_widths
[ZONE_FW_LEN
] = 6;
234 field_widths
[ZONE_FW_WP
] = 11;
235 field_widths
[ZONE_FW_TYPE
] = 13;
236 field_widths
[ZONE_FW_COND
] = 13;
237 field_widths
[ZONE_FW_SEQ
] = 14;
238 field_widths
[ZONE_FW_RESET
] = 16;
240 if ((report
->entries_available
- report
->entries_filled
) > 0) {
242 status
= ZONE_PRINT_MORE_DATA
;
245 if (out_flags
== ZONE_OF_SCRIPT
)
250 if ((out_flags
!= ZONE_OF_SCRIPT
)
251 && (first_pass
!= 0)) {
252 printf("%u zones, Maximum LBA %#jx (%ju)\n",
253 report
->entries_available
,
254 (uintmax_t)header
->maximum_lba
,
255 (uintmax_t)header
->maximum_lba
);
257 switch (header
->same
) {
258 case DISK_ZONE_SAME_ALL_DIFFERENT
:
259 printf("Zone lengths and types may vary\n");
261 case DISK_ZONE_SAME_ALL_SAME
:
262 printf("Zone lengths and types are all the same\n");
264 case DISK_ZONE_SAME_LAST_DIFFERENT
:
265 printf("Zone types are the same, last zone length "
268 case DISK_ZONE_SAME_TYPES_DIFFERENT
:
269 printf("Zone lengths are the same, types vary\n");
272 printf("Unknown SAME field value %#x\n",header
->same
);
276 if (out_flags
== ZONE_OF_SUMMARY
) {
277 status
= ZONE_PRINT_OK
;
281 if ((out_flags
== ZONE_OF_NORMAL
)
282 && (first_pass
!= 0)) {
283 printf("%*s %*s %*s %*s %*s %*s %*s\n",
284 field_widths
[ZONE_FW_START
], "Start LBA",
285 field_widths
[ZONE_FW_LEN
], "Length",
286 field_widths
[ZONE_FW_WP
], "WP LBA",
287 field_widths
[ZONE_FW_TYPE
], "Zone Type",
288 field_widths
[ZONE_FW_COND
], "Condition",
289 field_widths
[ZONE_FW_SEQ
], "Sequential",
290 field_widths
[ZONE_FW_RESET
], "Reset");
293 for (i
= 0; i
< report
->entries_filled
; i
++) {
294 entry
= &report
->entries
[i
];
296 printf("%#*jx, %*ju, %#*jx, ", field_widths
[ZONE_FW_START
],
297 (uintmax_t)entry
->zone_start_lba
,
298 field_widths
[ZONE_FW_LEN
],
299 (uintmax_t)entry
->zone_length
, field_widths
[ZONE_FW_WP
],
300 (uintmax_t)entry
->write_pointer_lba
);
302 switch (entry
->zone_type
) {
303 case DISK_ZONE_TYPE_CONVENTIONAL
:
304 snprintf(tmpstr
, sizeof(tmpstr
), "Conventional");
306 case DISK_ZONE_TYPE_SEQ_PREFERRED
:
307 case DISK_ZONE_TYPE_SEQ_REQUIRED
:
308 snprintf(tmpstr
, sizeof(tmpstr
), "Seq%c%s",
309 word_sep
, (entry
->zone_type
==
310 DISK_ZONE_TYPE_SEQ_PREFERRED
) ? "Preferred" :
314 snprintf(tmpstr
, sizeof(tmpstr
), "Zone%ctype%c%#x",
315 word_sep
, word_sep
, entry
->zone_type
);
318 printf("%*s, ", field_widths
[ZONE_FW_TYPE
], tmpstr
);
320 switch (entry
->zone_condition
) {
321 case DISK_ZONE_COND_NOT_WP
:
322 snprintf(tmpstr
, sizeof(tmpstr
), "NWP");
324 case DISK_ZONE_COND_EMPTY
:
325 snprintf(tmpstr
, sizeof(tmpstr
), "Empty");
327 case DISK_ZONE_COND_IMPLICIT_OPEN
:
328 snprintf(tmpstr
, sizeof(tmpstr
), "Implicit%cOpen",
331 case DISK_ZONE_COND_EXPLICIT_OPEN
:
332 snprintf(tmpstr
, sizeof(tmpstr
), "Explicit%cOpen",
335 case DISK_ZONE_COND_CLOSED
:
336 snprintf(tmpstr
, sizeof(tmpstr
), "Closed");
338 case DISK_ZONE_COND_READONLY
:
339 snprintf(tmpstr
, sizeof(tmpstr
), "Readonly");
341 case DISK_ZONE_COND_FULL
:
342 snprintf(tmpstr
, sizeof(tmpstr
), "Full");
344 case DISK_ZONE_COND_OFFLINE
:
345 snprintf(tmpstr
, sizeof(tmpstr
), "Offline");
348 snprintf(tmpstr
, sizeof(tmpstr
), "%#x",
349 entry
->zone_condition
);
353 printf("%*s, ", field_widths
[ZONE_FW_COND
], tmpstr
);
355 if (entry
->zone_flags
& DISK_ZONE_FLAG_NON_SEQ
)
356 snprintf(tmpstr
, sizeof(tmpstr
), "Non%cSequential",
359 snprintf(tmpstr
, sizeof(tmpstr
), "Sequential");
361 printf("%*s, ", field_widths
[ZONE_FW_SEQ
], tmpstr
);
363 if (entry
->zone_flags
& DISK_ZONE_FLAG_RESET
)
364 snprintf(tmpstr
, sizeof(tmpstr
), "Reset%cNeeded",
367 snprintf(tmpstr
, sizeof(tmpstr
), "No%cReset%cNeeded",
370 printf("%*s\n", field_widths
[ZONE_FW_RESET
], tmpstr
);
372 next_lba
= entry
->zone_start_lba
+ entry
->zone_length
;
375 report
->starting_id
= next_lba
;
381 main(int argc
, char **argv
)
386 int action
= -1, rep_option
= -1;
389 zone_output_flags out_flags
= ZONE_OF_NORMAL
;
390 char *filename
= NULL
;
391 struct disk_zone_args zone_args
;
392 struct disk_zone_rep_entry
*entries
= NULL
;
393 uint32_t num_entries
= 16384;
394 zone_print_status zp_status
;
396 size_t entry_alloc_size
;
397 int open_flags
= O_RDONLY
;
399 while ((c
= getopt(argc
, argv
, "ac:d:hl:o:P:?")) != -1) {
405 scsi_nv_status status
;
408 status
= scsi_get_nv(zone_cmd_map
,
409 (sizeof(zone_cmd_map
) / sizeof(zone_cmd_map
[0])),
410 optarg
, &entry_num
, SCSI_NV_FLAG_IG_CASE
);
411 if (status
== SCSI_NV_FOUND
)
412 action
= zone_cmd_map
[entry_num
].value
;
414 warnx("%s: %s: %s option %s", __func__
,
415 (status
== SCSI_NV_AMBIGUOUS
) ?
416 "ambiguous" : "invalid", "zone command",
424 filename
= strdup(optarg
);
425 if (filename
== NULL
)
426 err(1, "Unable to allocate memory for "
432 lba
= strtoull(optarg
, &endptr
, 0);
433 if (*endptr
!= '\0') {
434 warnx("%s: invalid lba argument %s", __func__
,
442 scsi_nv_status status
;
445 status
= scsi_get_nv(zone_rep_opts
,
446 (sizeof(zone_rep_opts
) /
447 sizeof(zone_rep_opts
[0])),
448 optarg
, &entry_num
, SCSI_NV_FLAG_IG_CASE
);
449 if (status
== SCSI_NV_FOUND
)
450 rep_option
= zone_rep_opts
[entry_num
].value
;
452 warnx("%s: %s: %s option %s", __func__
,
453 (status
== SCSI_NV_AMBIGUOUS
) ?
454 "ambiguous" : "invalid", "report zones",
462 scsi_nv_status status
;
465 status
= scsi_get_nv(zone_print_opts
,
466 (sizeof(zone_print_opts
) /
467 sizeof(zone_print_opts
[0])), optarg
, &entry_num
,
468 SCSI_NV_FLAG_IG_CASE
);
469 if (status
== SCSI_NV_FOUND
)
470 out_flags
= zone_print_opts
[entry_num
].value
;
472 warnx("%s: %s: %s option %s", __func__
,
473 (status
== SCSI_NV_AMBIGUOUS
) ?
474 "ambiguous" : "invalid", "print",
483 case 'h': /*FALLTHROUGH*/
486 break; /*NOTREACHED*/
490 if (filename
== NULL
) {
491 warnx("You must specify a device with -d");
495 warnx("You must specify an action with -c");
504 bzero(&zone_args
, sizeof(zone_args
));
506 zone_args
.zone_cmd
= action
;
510 case DISK_ZONE_CLOSE
:
511 case DISK_ZONE_FINISH
:
514 zone_args
.zone_params
.rwp
.id
= lba
;
516 zone_args
.zone_params
.rwp
.flags
|=
517 DISK_ZONE_RWP_FLAG_ALL
;
519 case DISK_ZONE_REPORT_ZONES
: {
520 entry_alloc_size
= num_entries
*
521 sizeof(struct disk_zone_rep_entry
);
522 entries
= malloc(entry_alloc_size
);
523 if (entries
== NULL
) {
524 warn("Could not allocate %zu bytes",
529 zone_args
.zone_params
.report
.entries_allocated
= num_entries
;
530 zone_args
.zone_params
.report
.entries
= entries
;
531 zone_args
.zone_params
.report
.starting_id
= lba
;
532 if (rep_option
!= -1)
533 zone_args
.zone_params
.report
.rep_options
= rep_option
;
536 case DISK_ZONE_GET_PARAMS
:
539 warnx("Unknown action %d", action
);
542 break; /*NOTREACHED*/
545 fd
= open(filename
, open_flags
);
547 warn("Unable to open device %s", filename
);
552 error
= ioctl(fd
, DIOCZONECMD
, &zone_args
);
554 warn("DIOCZONECMD ioctl failed");
561 case DISK_ZONE_CLOSE
:
562 case DISK_ZONE_FINISH
:
565 case DISK_ZONE_REPORT_ZONES
:
566 zp_status
= zonectl_print_rz(&zone_args
.zone_params
.report
,
567 out_flags
, first_pass
);
568 if (zp_status
== ZONE_PRINT_MORE_DATA
) {
570 bzero(entries
, entry_alloc_size
);
571 zone_args
.zone_params
.report
.entries_filled
= 0;
573 } else if (zp_status
== ZONE_PRINT_ERROR
)
576 case DISK_ZONE_GET_PARAMS
:
577 zonectl_print_params(&zone_args
.zone_params
.disk_params
);
580 warnx("Unknown action %d", action
);
583 break; /*NOTREACHED*/