diff options
Diffstat (limited to 'usr.sbin/zonectl')
| -rw-r--r-- | usr.sbin/zonectl/Makefile | 8 | ||||
| -rw-r--r-- | usr.sbin/zonectl/Makefile.depend | 18 | ||||
| -rw-r--r-- | usr.sbin/zonectl/zonectl.8 | 230 | ||||
| -rw-r--r-- | usr.sbin/zonectl/zonectl.c | 586 | 
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); +} | 
