1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2003 Poul-Henning Kamp
5 * Copyright (c) 2015 Spectra Logic Corporation
6 * Copyright (c) 2017 Alexander Motin <mav@FreeBSD.org>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The names of the authors may not be used to endorse or promote
18 *    products derived from this software without specific prior written
19 *    permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: stable/11/usr.sbin/diskinfo/diskinfo.c 330449 2018-03-05 07:26:05Z eadler $
34 */
35
36#include <stdbool.h>
37#include <stdio.h>
38#include <stdint.h>
39#include <stdlib.h>
40#include <string.h>
41#include <strings.h>
42#include <unistd.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <libutil.h>
46#include <paths.h>
47#include <err.h>
48#include <geom/geom_disk.h>
49#include <sysexits.h>
50#include <sys/aio.h>
51#include <sys/disk.h>
52#include <sys/param.h>
53#include <sys/stat.h>
54#include <sys/time.h>
55
56#define	NAIO	128
57#define	MAXTX	(8*1024*1024)
58#define	MEGATX	(1024*1024)
59
60static void
61usage(void)
62{
63	fprintf(stderr, "usage: diskinfo [-cipsStvw] disk ...\n");
64	exit (1);
65}
66
67static int opt_c, opt_i, opt_p, opt_s, opt_S, opt_t, opt_v, opt_w;
68
69static bool candelete(int fd);
70static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
71static void commandtime(int fd, off_t mediasize, u_int sectorsize);
72static void iopsbench(int fd, off_t mediasize, u_int sectorsize);
73static void rotationrate(int fd, char *buf, size_t buflen);
74static void slogbench(int fd, int isreg, off_t mediasize, u_int sectorsize);
75static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str,
76		     size_t zone_str_len);
77
78static uint8_t *buf;
79
80int
81main(int argc, char **argv)
82{
83	struct stat sb;
84	int i, ch, fd, error, exitval = 0;
85	char tstr[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
86	char zone_desc[64];
87	char rrate[64];
88	struct diocgattr_arg arg;
89	off_t	mediasize, stripesize, stripeoffset;
90	u_int	sectorsize, fwsectors, fwheads, zoned = 0, isreg;
91	uint32_t zone_mode;
92
93	while ((ch = getopt(argc, argv, "cipsStvw")) != -1) {
94		switch (ch) {
95		case 'c':
96			opt_c = 1;
97			opt_v = 1;
98			break;
99		case 'i':
100			opt_i = 1;
101			opt_v = 1;
102			break;
103		case 'p':
104			opt_p = 1;
105			break;
106		case 's':
107			opt_s = 1;
108			break;
109		case 'S':
110			opt_S = 1;
111			opt_v = 1;
112			break;
113		case 't':
114			opt_t = 1;
115			opt_v = 1;
116			break;
117		case 'v':
118			opt_v = 1;
119			break;
120		case 'w':
121			opt_w = 1;
122			break;
123		default:
124			usage();
125		}
126	}
127	argc -= optind;
128	argv += optind;
129
130	if (argc < 1)
131		usage();
132
133	if ((opt_p && opt_s) || ((opt_p || opt_s) && (opt_c || opt_i || opt_t || opt_v))) {
134		warnx("-p or -s cannot be used with other options");
135		usage();
136	}
137
138	if (opt_S && !opt_w) {
139		warnx("-S require also -w");
140		usage();
141	}
142
143	if (posix_memalign((void **)&buf, PAGE_SIZE, MAXTX))
144		errx(1, "Can't allocate memory buffer");
145	for (i = 0; i < argc; i++) {
146		fd = open(argv[i], (opt_w ? O_RDWR : O_RDONLY) | O_DIRECT);
147		if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
148			snprintf(tstr, sizeof(tstr), "%s%s", _PATH_DEV, argv[i]);
149			fd = open(tstr, O_RDONLY);
150		}
151		if (fd < 0) {
152			warn("%s", argv[i]);
153			exit(1);
154		}
155		error = fstat(fd, &sb);
156		if (error != 0) {
157			warn("cannot stat %s", argv[i]);
158			exitval = 1;
159			goto out;
160		}
161		isreg = S_ISREG(sb.st_mode);
162		if (isreg) {
163			mediasize = sb.st_size;
164			sectorsize = S_BLKSIZE;
165			fwsectors = 0;
166			fwheads = 0;
167			stripesize = sb.st_blksize;
168			stripeoffset = 0;
169			if (opt_p || opt_s) {
170				warnx("-p and -s only operate on physical devices: %s", argv[i]);
171				goto out;
172			}
173		} else {
174			if (opt_p) {
175				if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0) {
176					printf("%s\n", physpath);
177				} else {
178					warnx("Failed to determine physpath for: %s", argv[i]);
179				}
180				goto out;
181			}
182			if (opt_s) {
183				if (ioctl(fd, DIOCGIDENT, ident) == 0) {
184					printf("%s\n", ident);
185				} else {
186					warnx("Failed to determine serial number for: %s", argv[i]);
187				}
188				goto out;
189			}
190			error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
191			if (error) {
192				warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
193				exitval = 1;
194				goto out;
195			}
196			error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
197			if (error) {
198				warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
199				exitval = 1;
200				goto out;
201			}
202			error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
203			if (error)
204				fwsectors = 0;
205			error = ioctl(fd, DIOCGFWHEADS, &fwheads);
206			if (error)
207				fwheads = 0;
208			error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
209			if (error)
210				stripesize = 0;
211			error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
212			if (error)
213				stripeoffset = 0;
214			error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc));
215			if (error == 0)
216				zoned = 1;
217		}
218		if (!opt_v) {
219			printf("%s", argv[i]);
220			printf("\t%u", sectorsize);
221			printf("\t%jd", (intmax_t)mediasize);
222			printf("\t%jd", (intmax_t)mediasize/sectorsize);
223			printf("\t%jd", (intmax_t)stripesize);
224			printf("\t%jd", (intmax_t)stripeoffset);
225			if (fwsectors != 0 && fwheads != 0) {
226				printf("\t%jd", (intmax_t)mediasize /
227				    (fwsectors * fwheads * sectorsize));
228				printf("\t%u", fwheads);
229				printf("\t%u", fwsectors);
230			}
231		} else {
232			humanize_number(tstr, 5, (int64_t)mediasize, "",
233			    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
234			printf("%s\n", argv[i]);
235			printf("\t%-12u\t# sectorsize\n", sectorsize);
236			printf("\t%-12jd\t# mediasize in bytes (%s)\n",
237			    (intmax_t)mediasize, tstr);
238			printf("\t%-12jd\t# mediasize in sectors\n",
239			    (intmax_t)mediasize/sectorsize);
240			printf("\t%-12jd\t# stripesize\n", stripesize);
241			printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
242			if (fwsectors != 0 && fwheads != 0) {
243				printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
244				    (fwsectors * fwheads * sectorsize));
245				printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
246				printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
247			}
248			strlcpy(arg.name, "GEOM::descr", sizeof(arg.name));
249			arg.len = sizeof(arg.value.str);
250			if (ioctl(fd, DIOCGATTR, &arg) == 0)
251				printf("\t%-12s\t# Disk descr.\n", arg.value.str);
252			if (ioctl(fd, DIOCGIDENT, ident) == 0)
253				printf("\t%-12s\t# Disk ident.\n", ident);
254			if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
255				printf("\t%-12s\t# Physical path\n", physpath);
256			printf("\t%-12s\t# TRIM/UNMAP support\n",
257			    candelete(fd) ? "Yes" : "No");
258			rotationrate(fd, rrate, sizeof(rrate));
259			printf("\t%-12s\t# Rotation rate in RPM\n", rrate);
260			if (zoned != 0)
261				printf("\t%-12s\t# Zone Mode\n", zone_desc);
262		}
263		printf("\n");
264		if (opt_c)
265			commandtime(fd, mediasize, sectorsize);
266		if (opt_t)
267			speeddisk(fd, mediasize, sectorsize);
268		if (opt_i)
269			iopsbench(fd, mediasize, sectorsize);
270		if (opt_S)
271			slogbench(fd, isreg, mediasize, sectorsize);
272out:
273		close(fd);
274	}
275	free(buf);
276	exit (exitval);
277}
278
279static bool
280candelete(int fd)
281{
282	struct diocgattr_arg arg;
283
284	strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name));
285	arg.len = sizeof(arg.value.i);
286	if (ioctl(fd, DIOCGATTR, &arg) == 0)
287		return (arg.value.i != 0);
288	else
289		return (false);
290}
291
292static void
293rotationrate(int fd, char *rate, size_t buflen)
294{
295	struct diocgattr_arg arg;
296	int ret;
297
298	strlcpy(arg.name, "GEOM::rotation_rate", sizeof(arg.name));
299	arg.len = sizeof(arg.value.u16);
300
301	ret = ioctl(fd, DIOCGATTR, &arg);
302	if (ret < 0 || arg.value.u16 == DISK_RR_UNKNOWN)
303		snprintf(rate, buflen, "Unknown");
304	else if (arg.value.u16 == DISK_RR_NON_ROTATING)
305		snprintf(rate, buflen, "%d", 0);
306	else if (arg.value.u16 >= DISK_RR_MIN && arg.value.u16 <= DISK_RR_MAX)
307		snprintf(rate, buflen, "%d", arg.value.u16);
308	else
309		snprintf(rate, buflen, "Invalid");
310}
311
312static void
313rdsect(int fd, off_t blockno, u_int sectorsize)
314{
315	int error;
316
317	if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1)
318		err(1, "lseek");
319	error = read(fd, buf, sectorsize);
320	if (error == -1)
321		err(1, "read");
322	if (error != (int)sectorsize)
323		errx(1, "disk too small for test.");
324}
325
326static void
327rdmega(int fd)
328{
329	int error;
330
331	error = read(fd, buf, MEGATX);
332	if (error == -1)
333		err(1, "read");
334	if (error != MEGATX)
335		errx(1, "disk too small for test.");
336}
337
338static struct timeval tv1, tv2;
339
340static void
341T0(void)
342{
343
344	fflush(stdout);
345	sync();
346	sleep(1);
347	sync();
348	sync();
349	gettimeofday(&tv1, NULL);
350}
351
352static double
353delta_t(void)
354{
355	double dt;
356
357	gettimeofday(&tv2, NULL);
358	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
359	dt += (tv2.tv_sec - tv1.tv_sec);
360
361	return (dt);
362}
363
364static void
365TN(int count)
366{
367	double dt;
368
369	dt = delta_t();
370	printf("%5d iter in %10.6f sec = %8.3f msec\n",
371		count, dt, dt * 1000.0 / count);
372}
373
374static void
375TR(double count)
376{
377	double dt;
378
379	dt = delta_t();
380	printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
381		count, dt, count / dt);
382}
383
384static void
385TI(double count)
386{
387	double dt;
388
389	dt = delta_t();
390	printf("%8.0f ops in  %10.6f sec = %8.0f IOPS\n",
391		count, dt, count / dt);
392}
393
394static void
395TS(u_int size, int count)
396{
397	double dt;
398
399	dt = delta_t();
400	printf("%8.1f usec/IO = %8.1f Mbytes/s\n",
401	    dt * 1000000.0 / count, (double)size * count / dt / (1024 * 1024));
402}
403
404static void
405speeddisk(int fd, off_t mediasize, u_int sectorsize)
406{
407	int bulk, i;
408	off_t b0, b1, sectorcount, step;
409
410	sectorcount = mediasize / sectorsize;
411	if (sectorcount <= 0)
412		return;		/* Can't test devices with no sectors */
413
414	step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
415	if (step > 16384)
416		step = 16384;
417	bulk = mediasize / (1024 * 1024);
418	if (bulk > 100)
419		bulk = 100;
420
421	printf("Seek times:\n");
422	printf("\tFull stroke:\t");
423	b0 = 0;
424	b1 = sectorcount - step;
425	T0();
426	for (i = 0; i < 125; i++) {
427		rdsect(fd, b0, sectorsize);
428		b0 += step;
429		rdsect(fd, b1, sectorsize);
430		b1 -= step;
431	}
432	TN(250);
433
434	printf("\tHalf stroke:\t");
435	b0 = sectorcount / 4;
436	b1 = b0 + sectorcount / 2;
437	T0();
438	for (i = 0; i < 125; i++) {
439		rdsect(fd, b0, sectorsize);
440		b0 += step;
441		rdsect(fd, b1, sectorsize);
442		b1 += step;
443	}
444	TN(250);
445	printf("\tQuarter stroke:\t");
446	b0 = sectorcount / 4;
447	b1 = b0 + sectorcount / 4;
448	T0();
449	for (i = 0; i < 250; i++) {
450		rdsect(fd, b0, sectorsize);
451		b0 += step;
452		rdsect(fd, b1, sectorsize);
453		b1 += step;
454	}
455	TN(500);
456
457	printf("\tShort forward:\t");
458	b0 = sectorcount / 2;
459	T0();
460	for (i = 0; i < 400; i++) {
461		rdsect(fd, b0, sectorsize);
462		b0 += step;
463	}
464	TN(400);
465
466	printf("\tShort backward:\t");
467	b0 = sectorcount / 2;
468	T0();
469	for (i = 0; i < 400; i++) {
470		rdsect(fd, b0, sectorsize);
471		b0 -= step;
472	}
473	TN(400);
474
475	printf("\tSeq outer:\t");
476	b0 = 0;
477	T0();
478	for (i = 0; i < 2048; i++) {
479		rdsect(fd, b0, sectorsize);
480		b0++;
481	}
482	TN(2048);
483
484	printf("\tSeq inner:\t");
485	b0 = sectorcount - 2048;
486	T0();
487	for (i = 0; i < 2048; i++) {
488		rdsect(fd, b0, sectorsize);
489		b0++;
490	}
491	TN(2048);
492
493	printf("\nTransfer rates:\n");
494	printf("\toutside:     ");
495	rdsect(fd, 0, sectorsize);
496	T0();
497	for (i = 0; i < bulk; i++) {
498		rdmega(fd);
499	}
500	TR(bulk * 1024);
501
502	printf("\tmiddle:      ");
503	b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
504	rdsect(fd, b0, sectorsize);
505	T0();
506	for (i = 0; i < bulk; i++) {
507		rdmega(fd);
508	}
509	TR(bulk * 1024);
510
511	printf("\tinside:      ");
512	b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
513	rdsect(fd, b0, sectorsize);
514	T0();
515	for (i = 0; i < bulk; i++) {
516		rdmega(fd);
517	}
518	TR(bulk * 1024);
519
520	printf("\n");
521	return;
522}
523
524static void
525commandtime(int fd, off_t mediasize, u_int sectorsize)
526{
527	double dtmega, dtsector;
528	int i;
529
530	printf("I/O command overhead:\n");
531	i = mediasize;
532	rdsect(fd, 0, sectorsize);
533	T0();
534	for (i = 0; i < 10; i++)
535		rdmega(fd);
536	dtmega = delta_t();
537
538	printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
539		dtmega, dtmega*100/2048);
540
541	rdsect(fd, 0, sectorsize);
542	T0();
543	for (i = 0; i < 20480; i++)
544		rdsect(fd, 0, sectorsize);
545	dtsector = delta_t();
546
547	printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
548		dtsector, dtsector*100/2048);
549	printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
550		(dtsector - dtmega)*100/2048);
551
552	printf("\n");
553	return;
554}
555
556static void
557iops(int fd, off_t mediasize, u_int sectorsize)
558{
559	struct aiocb aios[NAIO], *aiop;
560	ssize_t ret;
561	off_t sectorcount;
562	int error, i, queued, completed;
563
564	sectorcount = mediasize / sectorsize;
565
566	for (i = 0; i < NAIO; i++) {
567		aiop = &(aios[i]);
568		bzero(aiop, sizeof(*aiop));
569		aiop->aio_buf = malloc(sectorsize);
570		if (aiop->aio_buf == NULL)
571			err(1, "malloc");
572	}
573
574	T0();
575	for (i = 0; i < NAIO; i++) {
576		aiop = &(aios[i]);
577
578		aiop->aio_fildes = fd;
579		aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
580		aiop->aio_nbytes = sectorsize;
581
582		error = aio_read(aiop);
583		if (error != 0)
584			err(1, "aio_read");
585	}
586
587	queued = i;
588	completed = 0;
589
590	for (;;) {
591		ret = aio_waitcomplete(&aiop, NULL);
592		if (ret < 0)
593			err(1, "aio_waitcomplete");
594		if (ret != (ssize_t)sectorsize)
595			errx(1, "short read");
596
597		completed++;
598
599		if (delta_t() < 3.0) {
600			aiop->aio_fildes = fd;
601			aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
602			aiop->aio_nbytes = sectorsize;
603
604			error = aio_read(aiop);
605			if (error != 0)
606				err(1, "aio_read");
607
608			queued++;
609		} else if (completed == queued) {
610			break;
611		}
612	}
613
614	TI(completed);
615
616	return;
617}
618
619static void
620iopsbench(int fd, off_t mediasize, u_int sectorsize)
621{
622	printf("Asynchronous random reads:\n");
623
624	printf("\tsectorsize:  ");
625	iops(fd, mediasize, sectorsize);
626
627	if (sectorsize != 4096) {
628		printf("\t4 kbytes:    ");
629		iops(fd, mediasize, 4096);
630	}
631
632	printf("\t32 kbytes:   ");
633	iops(fd, mediasize, 32 * 1024);
634
635	printf("\t128 kbytes:  ");
636	iops(fd, mediasize, 128 * 1024);
637
638	printf("\n");
639}
640
641#define MAXIO (128*1024)
642#define MAXIOS (MAXTX / MAXIO)
643
644static void
645parwrite(int fd, size_t size, off_t off)
646{
647	struct aiocb aios[MAXIOS];
648	off_t o;
649	int n, error;
650	struct aiocb *aiop;
651
652	// if size > MAXIO, use AIO to write n - 1 pieces in parallel
653	for (n = 0, o = 0; size > MAXIO; n++, size -= MAXIO, o += MAXIO) {
654		aiop = &aios[n];
655		bzero(aiop, sizeof(*aiop));
656		aiop->aio_buf = &buf[o];
657		aiop->aio_fildes = fd;
658		aiop->aio_offset = off + o;
659		aiop->aio_nbytes = MAXIO;
660		error = aio_write(aiop);
661		if (error != 0)
662			err(EX_IOERR, "AIO write submit error");
663	}
664	// Use synchronous writes for the runt of size <= MAXIO
665	error = pwrite(fd, &buf[o], size, off + o);
666	if (error < 0)
667		err(EX_IOERR, "Sync write error");
668	for (; n > 0; n--) {
669		error = aio_waitcomplete(&aiop, NULL);
670		if (error < 0)
671			err(EX_IOERR, "AIO write wait error");
672	}
673}
674
675static void
676slogbench(int fd, int isreg, off_t mediasize, u_int sectorsize)
677{
678	off_t off;
679	u_int size;
680	int error, n, N, nowritecache = 0;
681
682	printf("Synchronous random writes:\n");
683	for (size = sectorsize; size <= MAXTX; size *= 2) {
684		printf("\t%4.4g kbytes: ", (double)size / 1024);
685		N = 0;
686		T0();
687		do {
688			for (n = 0; n < 250; n++) {
689				off = random() % (mediasize / size);
690				parwrite(fd, size, off * size);
691				if (nowritecache)
692					continue;
693				if (isreg)
694					error = fsync(fd);
695				else
696					error = ioctl(fd, DIOCGFLUSH);
697				if (error < 0) {
698					if (errno == ENOTSUP)
699						nowritecache = 1;
700					else
701						err(EX_IOERR, "Flush error");
702				}
703			}
704			N += 250;
705		} while (delta_t() < 1.0);
706		TS(size, N);
707	}
708}
709
710static int
711zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
712{
713	struct disk_zone_args zone_args;
714	int error;
715
716	bzero(&zone_args, sizeof(zone_args));
717
718	zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
719	error = ioctl(fd, DIOCZONECMD, &zone_args);
720
721	if (error == 0) {
722		*zone_mode = zone_args.zone_params.disk_params.zone_mode;
723
724		switch (*zone_mode) {
725		case DISK_ZONE_MODE_NONE:
726			snprintf(zone_str, zone_str_len, "Not_Zoned");
727			break;
728		case DISK_ZONE_MODE_HOST_AWARE:
729			snprintf(zone_str, zone_str_len, "Host_Aware");
730			break;
731		case DISK_ZONE_MODE_DRIVE_MANAGED:
732			snprintf(zone_str, zone_str_len, "Drive_Managed");
733			break;
734		case DISK_ZONE_MODE_HOST_MANAGED:
735			snprintf(zone_str, zone_str_len, "Host_Managed");
736			break;
737		default:
738			snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",
739			    *zone_mode);
740			break;
741		}
742	}
743	return (error);
744}
745