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