aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/zonectl
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/zonectl')
-rw-r--r--usr.sbin/zonectl/Makefile8
-rw-r--r--usr.sbin/zonectl/Makefile.depend18
-rw-r--r--usr.sbin/zonectl/zonectl.8230
-rw-r--r--usr.sbin/zonectl/zonectl.c586
4 files changed, 842 insertions, 0 deletions
diff --git a/usr.sbin/zonectl/Makefile b/usr.sbin/zonectl/Makefile
new file mode 100644
index 000000000000..69915ec4916c
--- /dev/null
+++ b/usr.sbin/zonectl/Makefile
@@ -0,0 +1,8 @@
+PROG= zonectl
+SRCS= zonectl.c
+SDIR= ${SRCTOP}/sys
+LIBADD= cam sbuf util
+MAN= zonectl.8
+CFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zonectl/Makefile.depend b/usr.sbin/zonectl/Makefile.depend
new file mode 100644
index 000000000000..0ef6390836dd
--- /dev/null
+++ b/usr.sbin/zonectl/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcam \
+ lib/libcompiler_rt \
+ lib/libsbuf \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/zonectl/zonectl.8 b/usr.sbin/zonectl/zonectl.8
new file mode 100644
index 000000000000..72cebfaf1ff0
--- /dev/null
+++ b/usr.sbin/zonectl/zonectl.8
@@ -0,0 +1,230 @@
+.\"
+.\" Copyright (c) 2015 Spectra Logic Corporation
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions, and the following disclaimer,
+.\" without modification.
+.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer
+.\" substantially similar to the "NO WARRANTY" disclaimer below
+.\" ("Disclaimer") and any redistribution must be conditioned upon
+.\" including a substantially similar Disclaimer requirement for further
+.\" binary redistribution.
+.\"
+.\" NO WARRANTY
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGES.
+.\"
+.\" Authors: Ken Merry (Spectra Logic Corporation)
+.\"
+.Dd May 18, 2016
+.Dt ZONECTL 8
+.Os
+.Sh NAME
+.Nm zonectl
+.Nd Shingled Magnetic Recording Zone Control utility
+.Sh SYNOPSIS
+.Nm
+.Aq Fl d Ar dev
+.Aq Fl c Ar cmd
+.Op Fl a
+.Op Fl l Ar LBA
+.Op Fl o Ar rep_opts
+.Op Fl P Ar print_opts
+.Sh DESCRIPTION
+Manage
+.Tn SCSI
+and
+.Tn ATA
+Zoned Block devices.
+This allows managing devices that conform to the
+.Tn SCSI
+Zoned Block Commands (ZBC) and
+.Tn ATA
+Zoned ATA Command Set (ZAC)
+specifications.
+Devices using these command sets are usually hard drives using Shingled
+Magnetic Recording (SMR).
+There are three types of SMR drives:
+.Bl -tag -width 13n
+.It Drive Managed
+Drive Managed drives look and act just like a standard random access block
+device, but underneath, the drive reads and writes the bulk of its capacity
+using SMR zones.
+Sequential writes will yield better performance, but writing sequentially
+is not required.
+.It Host Aware
+Host Aware drives expose the underlying zone layout via
+.Tn SCSI
+or
+.Tn ATA
+commands and allow the host to manage the zone conditions.
+The host is not required to manage the zones on the drive, though.
+Sequential writes will yield better performance in Sequential Write
+Preferred zones, but the host can write randomly in those zones.
+.It Host Managed
+Host Managed drives expose the underlying zone layout via
+.Tn SCSI
+or
+.Tn ATA
+commands.
+The host is required to access the zones according to the rules described
+by the zone layout.
+Any commands that violate the rules will be returned with an error.
+.El
+.Pp
+SMR drives are divided into zones (typically in the range of 256MB each)
+that fall into three general categories:
+.Bl -tag -width 20n
+.It Conventional
+These are also known as Non Write Pointer zones.
+These zones can be randomly written without an unexpected performance penalty.
+.It Sequential Preferred
+These zones should be written sequentially starting at the write pointer
+for the zone.
+They may be written randomly.
+Writes that do not conform to the zone layout may be significantly slower
+than expected.
+.It Sequential Required
+These zones must be written sequentially.
+If they are not written sequentially, starting at the write pointer, the
+command will fail.
+.El
+.Bl -tag -width 12n
+.It Fl c Ar cmd
+Specify the zone subcommand:
+.Bl -tag -width 6n
+.It params
+Display device parameters, including the type of device (Drive Managed,
+Host Aware, Host Managed, Not Zoned), the zone commands supported, and
+how many open zones it supports.
+.It rz
+Issue the Report Zones command.
+All zones are returned by default.
+Specify report options with
+.Fl o
+and printing options with
+.Fl P .
+Specify the starting LBA with
+.Fl l .
+Note that
+.Dq reportzones
+is also accepted as a command argument.
+.It open
+Explicitly open the zone specified by the starting LBA.
+.It close
+Close the zone specified by starting LBA.
+.It finish
+Finish the zone specified by the starting LBA.
+.It rwp
+Reset the write pointer for the zone specified by the starting LBA.
+.El
+.It Fl a
+For the Open, Close, Finish, and Reset Write Pointer operations, apply the
+operation to all zones on the drive.
+.It Fl l Ar lba
+Specify the starting LBA.
+For the Report Zones command, this tells the drive to report starting with
+the zone that starts at the given LBA.
+For the other commands, this allows the user to identify the zone requested
+by its starting LBA.
+The LBA may be specified in decimal, hexadecimal or octal notation.
+.It Fl o Ar rep_opt
+For the Report Zones command, specify a subset of zones to report.
+.Bl -tag -width 8n
+.It all
+Report all zones.
+This is the default.
+.It empty
+Report only empty zones.
+.It imp_open
+Report zones that are implicitly open.
+This means that the host has sent a write to the zone without explicitly
+opening the zone.
+.It exp_open
+Report zones that are explicitly open.
+.It closed
+Report zones that have been closed by the host.
+.It full
+Report zones that are full.
+.It ro
+Report zones that are in the read only state.
+Note that
+.Dq readonly
+is also accepted as an argument.
+.It offline
+Report zones that are in the offline state.
+.It reset
+Report zones that the device recommends should have their write pointers reset.
+.It nonseq
+Report zones that have the Non Sequential Resources Active flag set.
+These are zones that are Sequential Write Preferred, but have been written
+non-sequentially.
+.It nonwp
+Report Non Write Pointer zones, also known as Conventional zones.
+.El
+.It Fl P Ar print_opt
+Specify a printing option for Report Zones:
+.Bl -tag -width 7n
+.It normal
+Normal Report Zones output.
+This is the default.
+The summary and column headings are printed, fields are separated by spaces
+and the fields themselves may contain spaces.
+.It summary
+Just print the summary: the number of zones, the maximum LBA (LBA of the
+last logical block on the drive), and the value of the
+.Dq same
+field.
+The
+.Dq same
+field describes whether the zones on the drive are all identical, all
+different, or whether they are the same except for the last zone, etc.
+.It script
+Print the zones in a script friendly format.
+The summary and column headings are omitted, the fields are separated by
+commas, and the fields do not contain spaces.
+The fields contain underscores where spaces would normally be used.
+.El
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+zonectl -d /dev/da5 -c params
+.Ed
+.Pp
+Display basic zoning information for disk da5.
+.Bd -literal -offset indent
+zonectl -d /dev/da5 -c rz
+.Ed
+.Pp
+Issue the Report Zones command to disk da5, and print out all
+zones on the drive in the default format.
+.Bd -literal -offset indent
+zonectl -d /dev/da5 -c rz -o reset -P script
+.Ed
+.Pp
+Issue the Report Zones command to disk da5, and print out all
+of the zones that have the Reset Write Pointer Recommended bit set to true.
+Print the zones in a script friendly form.
+.Bd -literal -offset indent
+zonectl -d /dev/da5 -c rwp -l 0x2c80000
+.Ed
+.Pp
+Issue the Reset Write Pointer command to disk da5 for the zone
+that starts at LBA 0x2c80000.
+.Sh SEE ALSO
+.Xr camcontrol 8
+.Sh AUTHORS
+.An Kenneth Merry Aq ken@FreeBSD.org
diff --git a/usr.sbin/zonectl/zonectl.c b/usr.sbin/zonectl/zonectl.c
new file mode 100644
index 000000000000..53e5e6599597
--- /dev/null
+++ b/usr.sbin/zonectl/zonectl.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2015, 2016 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Ken Merry (Spectra Logic Corporation)
+ */
+
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stdint.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#include <sys/disk_zone.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+#include <locale.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+
+static struct scsi_nv zone_cmd_map[] = {
+ { "rz", DISK_ZONE_REPORT_ZONES },
+ { "reportzones", DISK_ZONE_REPORT_ZONES },
+ { "close", DISK_ZONE_CLOSE },
+ { "finish", DISK_ZONE_FINISH },
+ { "open", DISK_ZONE_OPEN },
+ { "rwp", DISK_ZONE_RWP },
+ { "params", DISK_ZONE_GET_PARAMS }
+};
+
+static struct scsi_nv zone_rep_opts[] = {
+ { "all", DISK_ZONE_REP_ALL },
+ { "empty", DISK_ZONE_REP_EMPTY },
+ { "imp_open", DISK_ZONE_REP_IMP_OPEN },
+ { "exp_open", DISK_ZONE_REP_EXP_OPEN },
+ { "closed", DISK_ZONE_REP_CLOSED },
+ { "full", DISK_ZONE_REP_FULL },
+ { "readonly", DISK_ZONE_REP_READONLY },
+ { "ro", DISK_ZONE_REP_READONLY },
+ { "offline", DISK_ZONE_REP_OFFLINE },
+ { "reset", DISK_ZONE_REP_RWP },
+ { "rwp", DISK_ZONE_REP_RWP },
+ { "nonseq", DISK_ZONE_REP_NON_SEQ },
+ { "nonwp", DISK_ZONE_REP_NON_WP }
+};
+
+
+typedef enum {
+ ZONE_OF_NORMAL = 0x00,
+ ZONE_OF_SUMMARY = 0x01,
+ ZONE_OF_SCRIPT = 0x02
+} zone_output_flags;
+
+static struct scsi_nv zone_print_opts[] = {
+ { "normal", ZONE_OF_NORMAL },
+ { "summary", ZONE_OF_SUMMARY },
+ { "script", ZONE_OF_SCRIPT }
+};
+
+static struct scsi_nv zone_cmd_desc_table[] = {
+ {"Report Zones", DISK_ZONE_RZ_SUP },
+ {"Open", DISK_ZONE_OPEN_SUP },
+ {"Close", DISK_ZONE_CLOSE_SUP },
+ {"Finish", DISK_ZONE_FINISH_SUP },
+ {"Reset Write Pointer", DISK_ZONE_RWP_SUP }
+};
+
+typedef enum {
+ ZONE_PRINT_OK,
+ ZONE_PRINT_MORE_DATA,
+ ZONE_PRINT_ERROR
+} zone_print_status;
+
+typedef enum {
+ ZONE_FW_START,
+ ZONE_FW_LEN,
+ ZONE_FW_WP,
+ ZONE_FW_TYPE,
+ ZONE_FW_COND,
+ ZONE_FW_SEQ,
+ ZONE_FW_RESET,
+ ZONE_NUM_FIELDS
+} zone_field_widths;
+
+
+static void usage(int error);
+static void zonectl_print_params(struct disk_zone_disk_params *params);
+zone_print_status zonectl_print_rz(struct disk_zone_report *report,
+ zone_output_flags out_flags, int first_pass);
+
+static void
+usage(int error)
+{
+ fprintf(error ? stderr : stdout,
+"usage: zonectl <-d dev> <-c cmd> [-a][-o rep_opts] [-l lba][-P print_opts]\n"
+ );
+}
+
+static void
+zonectl_print_params(struct disk_zone_disk_params *params)
+{
+ unsigned int i;
+ int first;
+
+ printf("Zone Mode: ");
+ switch (params->zone_mode) {
+ case DISK_ZONE_MODE_NONE:
+ printf("None");
+ break;
+ case DISK_ZONE_MODE_HOST_AWARE:
+ printf("Host Aware");
+ break;
+ case DISK_ZONE_MODE_DRIVE_MANAGED:
+ printf("Drive Managed");
+ break;
+ case DISK_ZONE_MODE_HOST_MANAGED:
+ printf("Host Managed");
+ break;
+ default:
+ printf("Unknown mode %#x", params->zone_mode);
+ break;
+ }
+ printf("\n");
+
+ first = 1;
+ printf("Command support: ");
+ for (i = 0; i < sizeof(zone_cmd_desc_table) /
+ sizeof(zone_cmd_desc_table[0]); i++) {
+ if (params->flags & zone_cmd_desc_table[i].value) {
+ if (first == 0)
+ printf(", ");
+ else
+ first = 0;
+ printf("%s", zone_cmd_desc_table[i].name);
+ }
+ }
+ if (first == 1)
+ printf("None");
+ printf("\n");
+
+ printf("Unrestricted Read in Sequential Write Required Zone "
+ "(URSWRZ): %s\n", (params->flags & DISK_ZONE_DISK_URSWRZ) ?
+ "Yes" : "No");
+
+ printf("Optimal Number of Open Sequential Write Preferred Zones: ");
+ if (params->flags & DISK_ZONE_OPT_SEQ_SET)
+ if (params->optimal_seq_zones == SVPD_ZBDC_OPT_SEQ_NR)
+ printf("Not Reported");
+ else
+ printf("%ju", (uintmax_t)params->optimal_seq_zones);
+ else
+ printf("Not Set");
+ printf("\n");
+
+
+ printf("Optimal Number of Non-Sequentially Written Sequential Write "
+ "Preferred Zones: ");
+ if (params->flags & DISK_ZONE_OPT_NONSEQ_SET)
+ if (params->optimal_nonseq_zones == SVPD_ZBDC_OPT_NONSEQ_NR)
+ printf("Not Reported");
+ else
+ printf("%ju",(uintmax_t)params->optimal_nonseq_zones);
+ else
+ printf("Not Set");
+ printf("\n");
+
+ printf("Maximum Number of Open Sequential Write Required Zones: ");
+ if (params->flags & DISK_ZONE_MAX_SEQ_SET)
+ if (params->max_seq_zones == SVPD_ZBDC_MAX_SEQ_UNLIMITED)
+ printf("Unlimited");
+ else
+ printf("%ju", (uintmax_t)params->max_seq_zones);
+ else
+ printf("Not Set");
+ printf("\n");
+}
+
+zone_print_status
+zonectl_print_rz(struct disk_zone_report *report, zone_output_flags out_flags,
+ int first_pass)
+{
+ zone_print_status status = ZONE_PRINT_OK;
+ struct disk_zone_rep_header *header = &report->header;
+ int field_widths[ZONE_NUM_FIELDS];
+ struct disk_zone_rep_entry *entry;
+ uint64_t next_lba = 0;
+ char tmpstr[80];
+ char word_sep;
+ uint32_t i;
+
+ field_widths[ZONE_FW_START] = 11;
+ field_widths[ZONE_FW_LEN] = 6;
+ field_widths[ZONE_FW_WP] = 11;
+ field_widths[ZONE_FW_TYPE] = 13;
+ field_widths[ZONE_FW_COND] = 13;
+ field_widths[ZONE_FW_SEQ] = 14;
+ field_widths[ZONE_FW_RESET] = 16;
+
+ if ((report->entries_available - report->entries_filled) > 0)
+ status = ZONE_PRINT_MORE_DATA;
+
+ if (out_flags == ZONE_OF_SCRIPT)
+ word_sep = '_';
+ else
+ word_sep = ' ';
+
+ if ((out_flags != ZONE_OF_SCRIPT)
+ && (first_pass != 0)) {
+ printf("%u zones, Maximum LBA %#jx (%ju)\n",
+ report->entries_available,
+ (uintmax_t)header->maximum_lba,
+ (uintmax_t)header->maximum_lba);
+
+ switch (header->same) {
+ case DISK_ZONE_SAME_ALL_DIFFERENT:
+ printf("Zone lengths and types may vary\n");
+ break;
+ case DISK_ZONE_SAME_ALL_SAME:
+ printf("Zone lengths and types are all the same\n");
+ break;
+ case DISK_ZONE_SAME_LAST_DIFFERENT:
+ printf("Zone types are the same, last zone length "
+ "differs\n");
+ break;
+ case DISK_ZONE_SAME_TYPES_DIFFERENT:
+ printf("Zone lengths are the same, types vary\n");
+ break;
+ default:
+ printf("Unknown SAME field value %#x\n",header->same);
+ break;
+ }
+ }
+ if (out_flags == ZONE_OF_SUMMARY) {
+ status = ZONE_PRINT_OK;
+ goto bailout;
+ }
+
+ if ((out_flags == ZONE_OF_NORMAL)
+ && (first_pass != 0)) {
+ printf("%*s %*s %*s %*s %*s %*s %*s\n",
+ field_widths[ZONE_FW_START], "Start LBA",
+ field_widths[ZONE_FW_LEN], "Length",
+ field_widths[ZONE_FW_WP], "WP LBA",
+ field_widths[ZONE_FW_TYPE], "Zone Type",
+ field_widths[ZONE_FW_COND], "Condition",
+ field_widths[ZONE_FW_SEQ], "Sequential",
+ field_widths[ZONE_FW_RESET], "Reset");
+ }
+
+ for (i = 0; i < report->entries_filled; i++) {
+ entry = &report->entries[i];
+
+ printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
+ (uintmax_t)entry->zone_start_lba,
+ field_widths[ZONE_FW_LEN],
+ (uintmax_t)entry->zone_length, field_widths[ZONE_FW_WP],
+ (uintmax_t)entry->write_pointer_lba);
+
+ switch (entry->zone_type) {
+ case DISK_ZONE_TYPE_CONVENTIONAL:
+ snprintf(tmpstr, sizeof(tmpstr), "Conventional");
+ break;
+ case DISK_ZONE_TYPE_SEQ_PREFERRED:
+ case DISK_ZONE_TYPE_SEQ_REQUIRED:
+ snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
+ word_sep, (entry->zone_type ==
+ DISK_ZONE_TYPE_SEQ_PREFERRED) ? "Preferred" :
+ "Required");
+ break;
+ default:
+ snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
+ word_sep, word_sep, entry->zone_type);
+ break;
+ }
+ printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
+
+ switch (entry->zone_condition) {
+ case DISK_ZONE_COND_NOT_WP:
+ snprintf(tmpstr, sizeof(tmpstr), "NWP");
+ break;
+ case DISK_ZONE_COND_EMPTY:
+ snprintf(tmpstr, sizeof(tmpstr), "Empty");
+ break;
+ case DISK_ZONE_COND_IMPLICIT_OPEN:
+ snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
+ word_sep);
+ break;
+ case DISK_ZONE_COND_EXPLICIT_OPEN:
+ snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
+ word_sep);
+ break;
+ case DISK_ZONE_COND_CLOSED:
+ snprintf(tmpstr, sizeof(tmpstr), "Closed");
+ break;
+ case DISK_ZONE_COND_READONLY:
+ snprintf(tmpstr, sizeof(tmpstr), "Readonly");
+ break;
+ case DISK_ZONE_COND_FULL:
+ snprintf(tmpstr, sizeof(tmpstr), "Full");
+ break;
+ case DISK_ZONE_COND_OFFLINE:
+ snprintf(tmpstr, sizeof(tmpstr), "Offline");
+ break;
+ default:
+ snprintf(tmpstr, sizeof(tmpstr), "%#x",
+ entry->zone_condition);
+ break;
+ }
+
+ printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
+
+ if (entry->zone_flags & DISK_ZONE_FLAG_NON_SEQ)
+ snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
+ word_sep);
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "Sequential");
+
+ printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
+
+ if (entry->zone_flags & DISK_ZONE_FLAG_RESET)
+ snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
+ word_sep);
+ else
+ snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
+ word_sep, word_sep);
+
+ printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
+
+ next_lba = entry->zone_start_lba + entry->zone_length;
+ }
+bailout:
+ report->starting_id = next_lba;
+
+ return (status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int all_zones = 0;
+ int error = 0;
+ int action = -1, rep_option = -1;
+ int fd = -1;
+ uint64_t lba = 0;
+ zone_output_flags out_flags = ZONE_OF_NORMAL;
+ char *filename = NULL;
+ struct disk_zone_args zone_args;
+ struct disk_zone_rep_entry *entries = NULL;
+ uint32_t num_entries = 16384;
+ zone_print_status zp_status;
+ int first_pass = 1;
+ size_t entry_alloc_size;
+ int open_flags = O_RDONLY;
+
+ while ((c = getopt(argc, argv, "ac:d:hl:o:P:?")) != -1) {
+ switch (c) {
+ case 'a':
+ all_zones = 1;
+ break;
+ case 'c': {
+ scsi_nv_status status;
+ int entry_num;
+
+ status = scsi_get_nv(zone_cmd_map,
+ nitems(zone_cmd_map),
+ optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ action = zone_cmd_map[entry_num].value;
+ else {
+ warnx("%s: %s: %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "zone command",
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 'd':
+ filename = strdup(optarg);
+ if (filename == NULL)
+ err(1, "Unable to allocate memory for "
+ "filename");
+ break;
+ case 'l': {
+ char *endptr;
+
+ lba = strtoull(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid lba argument %s", __func__,
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 'o': {
+ scsi_nv_status status;
+ int entry_num;
+
+ status = scsi_get_nv(zone_rep_opts,
+ (sizeof(zone_rep_opts) /
+ sizeof(zone_rep_opts[0])),
+ optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ rep_option = zone_rep_opts[entry_num].value;
+ else {
+ warnx("%s: %s: %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "report zones",
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 'P': {
+ scsi_nv_status status;
+ int entry_num;
+
+ status = scsi_get_nv(zone_print_opts,
+ (sizeof(zone_print_opts) /
+ sizeof(zone_print_opts[0])), optarg, &entry_num,
+ SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ out_flags = zone_print_opts[entry_num].value;
+ else {
+ warnx("%s: %s: %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "print",
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ default:
+ error = 1;
+ case 'h': /*FALLTHROUGH*/
+ usage(error);
+ goto bailout;
+ break; /*NOTREACHED*/
+ }
+ }
+
+ if (filename == NULL) {
+ warnx("You must specify a device with -d");
+ error = 1;
+ }
+ if (action == -1) {
+ warnx("You must specify an action with -c");
+ error = 1;
+ }
+
+ if (error != 0) {
+ usage(error);
+ goto bailout;
+ }
+
+ bzero(&zone_args, sizeof(zone_args));
+
+ zone_args.zone_cmd = action;
+
+ switch (action) {
+ case DISK_ZONE_OPEN:
+ case DISK_ZONE_CLOSE:
+ case DISK_ZONE_FINISH:
+ case DISK_ZONE_RWP:
+ open_flags = O_RDWR;
+ zone_args.zone_params.rwp.id = lba;
+ if (all_zones != 0)
+ zone_args.zone_params.rwp.flags |=
+ DISK_ZONE_RWP_FLAG_ALL;
+ break;
+ case DISK_ZONE_REPORT_ZONES: {
+ entry_alloc_size = num_entries *
+ sizeof(struct disk_zone_rep_entry);
+ entries = malloc(entry_alloc_size);
+ if (entries == NULL) {
+ warn("Could not allocate %zu bytes",
+ entry_alloc_size);
+ error = 1;
+ goto bailout;
+ }
+ zone_args.zone_params.report.entries_allocated = num_entries;
+ zone_args.zone_params.report.entries = entries;
+ zone_args.zone_params.report.starting_id = lba;
+ if (rep_option != -1)
+ zone_args.zone_params.report.rep_options = rep_option;
+ break;
+ }
+ case DISK_ZONE_GET_PARAMS:
+ break;
+ default:
+ warnx("Unknown action %d", action);
+ error = 1;
+ goto bailout;
+ break; /*NOTREACHED*/
+ }
+
+ fd = open(filename, open_flags);
+ if (fd == -1) {
+ warn("Unable to open device %s", filename);
+ error = 1;
+ goto bailout;
+ }
+next_chunk:
+ error = ioctl(fd, DIOCZONECMD, &zone_args);
+ if (error == -1) {
+ warn("DIOCZONECMD ioctl failed");
+ error = 1;
+ goto bailout;
+ }
+
+ switch (action) {
+ case DISK_ZONE_OPEN:
+ case DISK_ZONE_CLOSE:
+ case DISK_ZONE_FINISH:
+ case DISK_ZONE_RWP:
+ break;
+ case DISK_ZONE_REPORT_ZONES:
+ zp_status = zonectl_print_rz(&zone_args.zone_params.report,
+ out_flags, first_pass);
+ if (zp_status == ZONE_PRINT_MORE_DATA) {
+ first_pass = 0;
+ bzero(entries, entry_alloc_size);
+ zone_args.zone_params.report.entries_filled = 0;
+ goto next_chunk;
+ } else if (zp_status == ZONE_PRINT_ERROR)
+ error = 1;
+ break;
+ case DISK_ZONE_GET_PARAMS:
+ zonectl_print_params(&zone_args.zone_params.disk_params);
+ break;
+ default:
+ warnx("Unknown action %d", action);
+ error = 1;
+ goto bailout;
+ break; /*NOTREACHED*/
+ }
+bailout:
+ free(entries);
+
+ if (fd != -1)
+ close(fd);
+ exit (error);
+}