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$
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			strlcpy(arg.name, "GEOM::attachment", sizeof(arg.name));
255			arg.len = sizeof(arg.value.str);
256			if (ioctl(fd, DIOCGATTR, &arg) == 0)
257				printf("\t%-12s\t# Attachment\n", arg.value.str);
258			if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
259				printf("\t%-12s\t# Physical path\n", physpath);
260			printf("\t%-12s\t# TRIM/UNMAP support\n",
261			    candelete(fd) ? "Yes" : "No");
262			rotationrate(fd, rrate, sizeof(rrate));
263			printf("\t%-12s\t# Rotation rate in RPM\n", rrate);
264			if (zoned != 0)
265				printf("\t%-12s\t# Zone Mode\n", zone_desc);
266		}
267		printf("\n");
268		if (opt_c)
269			commandtime(fd, mediasize, sectorsize);
270		if (opt_t)
271			speeddisk(fd, mediasize, sectorsize);
272		if (opt_i)
273			iopsbench(fd, mediasize, sectorsize);
274		if (opt_S)
275			slogbench(fd, isreg, mediasize, sectorsize);
276out:
277		close(fd);
278	}
279	free(buf);
280	exit (exitval);
281}
282
283static bool
284candelete(int fd)
285{
286	struct diocgattr_arg arg;
287
288	strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name));
289	arg.len = sizeof(arg.value.i);
290	if (ioctl(fd, DIOCGATTR, &arg) == 0)
291		return (arg.value.i != 0);
292	else
293		return (false);
294}
295
296static void
297rotationrate(int fd, char *rate, size_t buflen)
298{
299	struct diocgattr_arg arg;
300	int ret;
301
302	strlcpy(arg.name, "GEOM::rotation_rate", sizeof(arg.name));
303	arg.len = sizeof(arg.value.u16);
304
305	ret = ioctl(fd, DIOCGATTR, &arg);
306	if (ret < 0 || arg.value.u16 == DISK_RR_UNKNOWN)
307		snprintf(rate, buflen, "Unknown");
308	else if (arg.value.u16 == DISK_RR_NON_ROTATING)
309		snprintf(rate, buflen, "%d", 0);
310	else if (arg.value.u16 >= DISK_RR_MIN && arg.value.u16 <= DISK_RR_MAX)
311		snprintf(rate, buflen, "%d", arg.value.u16);
312	else
313		snprintf(rate, buflen, "Invalid");
314}
315
316static void
317rdsect(int fd, off_t blockno, u_int sectorsize)
318{
319	int error;
320
321	if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1)
322		err(1, "lseek");
323	error = read(fd, buf, sectorsize);
324	if (error == -1)
325		err(1, "read");
326	if (error != (int)sectorsize)
327		errx(1, "disk too small for test.");
328}
329
330static void
331rdmega(int fd)
332{
333	int error;
334
335	error = read(fd, buf, MEGATX);
336	if (error == -1)
337		err(1, "read");
338	if (error != MEGATX)
339		errx(1, "disk too small for test.");
340}
341
342static struct timeval tv1, tv2;
343
344static void
345T0(void)
346{
347
348	fflush(stdout);
349	sync();
350	sleep(1);
351	sync();
352	sync();
353	gettimeofday(&tv1, NULL);
354}
355
356static double
357delta_t(void)
358{
359	double dt;
360
361	gettimeofday(&tv2, NULL);
362	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
363	dt += (tv2.tv_sec - tv1.tv_sec);
364
365	return (dt);
366}
367
368static void
369TN(int count)
370{
371	double dt;
372
373	dt = delta_t();
374	printf("%5d iter in %10.6f sec = %8.3f msec\n",
375		count, dt, dt * 1000.0 / count);
376}
377
378static void
379TR(double count)
380{
381	double dt;
382
383	dt = delta_t();
384	printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
385		count, dt, count / dt);
386}
387
388static void
389TI(double count)
390{
391	double dt;
392
393	dt = delta_t();
394	printf("%8.0f ops in  %10.6f sec = %8.0f IOPS\n",
395		count, dt, count / dt);
396}
397
398static void
399TS(u_int size, int count)
400{
401	double dt;
402
403	dt = delta_t();
404	printf("%8.1f usec/IO = %8.1f Mbytes/s\n",
405	    dt * 1000000.0 / count, (double)size * count / dt / (1024 * 1024));
406}
407
408static void
409speeddisk(int fd, off_t mediasize, u_int sectorsize)
410{
411	int bulk, i;
412	off_t b0, b1, sectorcount, step;
413
414	/*
415	 * Drives smaller than 1MB produce negative sector numbers,
416	 * as do 2048 or fewer sectors.
417	 */
418	sectorcount = mediasize / sectorsize;
419	if (mediasize < 1024 * 1024 || sectorcount < 2048)
420		return;
421
422
423	step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
424	if (step > 16384)
425		step = 16384;
426	bulk = mediasize / (1024 * 1024);
427	if (bulk > 100)
428		bulk = 100;
429
430	printf("Seek times:\n");
431	printf("\tFull stroke:\t");
432	b0 = 0;
433	b1 = sectorcount - step;
434	T0();
435	for (i = 0; i < 125; i++) {
436		rdsect(fd, b0, sectorsize);
437		b0 += step;
438		rdsect(fd, b1, sectorsize);
439		b1 -= step;
440	}
441	TN(250);
442
443	printf("\tHalf stroke:\t");
444	b0 = sectorcount / 4;
445	b1 = b0 + sectorcount / 2;
446	T0();
447	for (i = 0; i < 125; i++) {
448		rdsect(fd, b0, sectorsize);
449		b0 += step;
450		rdsect(fd, b1, sectorsize);
451		b1 += step;
452	}
453	TN(250);
454	printf("\tQuarter stroke:\t");
455	b0 = sectorcount / 4;
456	b1 = b0 + sectorcount / 4;
457	T0();
458	for (i = 0; i < 250; i++) {
459		rdsect(fd, b0, sectorsize);
460		b0 += step;
461		rdsect(fd, b1, sectorsize);
462		b1 += step;
463	}
464	TN(500);
465
466	printf("\tShort forward:\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("\tShort backward:\t");
476	b0 = sectorcount / 2;
477	T0();
478	for (i = 0; i < 400; i++) {
479		rdsect(fd, b0, sectorsize);
480		b0 -= step;
481	}
482	TN(400);
483
484	printf("\tSeq outer:\t");
485	b0 = 0;
486	T0();
487	for (i = 0; i < 2048; i++) {
488		rdsect(fd, b0, sectorsize);
489		b0++;
490	}
491	TN(2048);
492
493	printf("\tSeq inner:\t");
494	b0 = sectorcount - 2048;
495	T0();
496	for (i = 0; i < 2048; i++) {
497		rdsect(fd, b0, sectorsize);
498		b0++;
499	}
500	TN(2048);
501
502	printf("\nTransfer rates:\n");
503	printf("\toutside:     ");
504	rdsect(fd, 0, sectorsize);
505	T0();
506	for (i = 0; i < bulk; i++) {
507		rdmega(fd);
508	}
509	TR(bulk * 1024);
510
511	printf("\tmiddle:      ");
512	b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 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("\tinside:      ");
521	b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
522	rdsect(fd, b0, sectorsize);
523	T0();
524	for (i = 0; i < bulk; i++) {
525		rdmega(fd);
526	}
527	TR(bulk * 1024);
528
529	printf("\n");
530	return;
531}
532
533static void
534commandtime(int fd, off_t mediasize, u_int sectorsize)
535{
536	double dtmega, dtsector;
537	int i;
538
539	printf("I/O command overhead:\n");
540	i = mediasize;
541	rdsect(fd, 0, sectorsize);
542	T0();
543	for (i = 0; i < 10; i++)
544		rdmega(fd);
545	dtmega = delta_t();
546
547	printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
548		dtmega, dtmega*100/2048);
549
550	rdsect(fd, 0, sectorsize);
551	T0();
552	for (i = 0; i < 20480; i++)
553		rdsect(fd, 0, sectorsize);
554	dtsector = delta_t();
555
556	printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
557		dtsector, dtsector*100/2048);
558	printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
559		(dtsector - dtmega)*100/2048);
560
561	printf("\n");
562	return;
563}
564
565static void
566iops(int fd, off_t mediasize, u_int sectorsize)
567{
568	struct aiocb aios[NAIO], *aiop;
569	ssize_t ret;
570	off_t sectorcount;
571	int error, i, queued, completed;
572
573	sectorcount = mediasize / sectorsize;
574
575	for (i = 0; i < NAIO; i++) {
576		aiop = &(aios[i]);
577		bzero(aiop, sizeof(*aiop));
578		aiop->aio_buf = malloc(sectorsize);
579		if (aiop->aio_buf == NULL)
580			err(1, "malloc");
581	}
582
583	T0();
584	for (i = 0; i < NAIO; i++) {
585		aiop = &(aios[i]);
586
587		aiop->aio_fildes = fd;
588		aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
589		aiop->aio_nbytes = sectorsize;
590
591		error = aio_read(aiop);
592		if (error != 0)
593			err(1, "aio_read");
594	}
595
596	queued = i;
597	completed = 0;
598
599	for (;;) {
600		ret = aio_waitcomplete(&aiop, NULL);
601		if (ret < 0)
602			err(1, "aio_waitcomplete");
603		if (ret != (ssize_t)sectorsize)
604			errx(1, "short read");
605
606		completed++;
607
608		if (delta_t() < 3.0) {
609			aiop->aio_fildes = fd;
610			aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
611			aiop->aio_nbytes = sectorsize;
612
613			error = aio_read(aiop);
614			if (error != 0)
615				err(1, "aio_read");
616
617			queued++;
618		} else if (completed == queued) {
619			break;
620		}
621	}
622
623	TI(completed);
624
625	return;
626}
627
628static void
629iopsbench(int fd, off_t mediasize, u_int sectorsize)
630{
631	printf("Asynchronous random reads:\n");
632
633	printf("\tsectorsize:  ");
634	iops(fd, mediasize, sectorsize);
635
636	if (sectorsize != 4096) {
637		printf("\t4 kbytes:    ");
638		iops(fd, mediasize, 4096);
639	}
640
641	printf("\t32 kbytes:   ");
642	iops(fd, mediasize, 32 * 1024);
643
644	printf("\t128 kbytes:  ");
645	iops(fd, mediasize, 128 * 1024);
646
647	printf("\t1024 kbytes: ");
648	iops(fd, mediasize, 1024 * 1024);
649
650	printf("\n");
651}
652
653#define MAXIO (128*1024)
654#define MAXIOS (MAXTX / MAXIO)
655
656static void
657parwrite(int fd, size_t size, off_t off)
658{
659	struct aiocb aios[MAXIOS];
660	off_t o;
661	int n, error;
662	struct aiocb *aiop;
663
664	// if size > MAXIO, use AIO to write n - 1 pieces in parallel
665	for (n = 0, o = 0; size > MAXIO; n++, size -= MAXIO, o += MAXIO) {
666		aiop = &aios[n];
667		bzero(aiop, sizeof(*aiop));
668		aiop->aio_buf = &buf[o];
669		aiop->aio_fildes = fd;
670		aiop->aio_offset = off + o;
671		aiop->aio_nbytes = MAXIO;
672		error = aio_write(aiop);
673		if (error != 0)
674			err(EX_IOERR, "AIO write submit error");
675	}
676	// Use synchronous writes for the runt of size <= MAXIO
677	error = pwrite(fd, &buf[o], size, off + o);
678	if (error < 0)
679		err(EX_IOERR, "Sync write error");
680	for (; n > 0; n--) {
681		error = aio_waitcomplete(&aiop, NULL);
682		if (error < 0)
683			err(EX_IOERR, "AIO write wait error");
684	}
685}
686
687static void
688slogbench(int fd, int isreg, off_t mediasize, u_int sectorsize)
689{
690	off_t off;
691	u_int size;
692	int error, n, N, nowritecache = 0;
693
694	printf("Synchronous random writes:\n");
695	for (size = sectorsize; size <= MAXTX; size *= 2) {
696		printf("\t%4.4g kbytes: ", (double)size / 1024);
697		N = 0;
698		T0();
699		do {
700			for (n = 0; n < 250; n++) {
701				off = random() % (mediasize / size);
702				parwrite(fd, size, off * size);
703				if (nowritecache)
704					continue;
705				if (isreg)
706					error = fsync(fd);
707				else
708					error = ioctl(fd, DIOCGFLUSH);
709				if (error < 0) {
710					if (errno == ENOTSUP)
711						nowritecache = 1;
712					else
713						err(EX_IOERR, "Flush error");
714				}
715			}
716			N += 250;
717		} while (delta_t() < 1.0);
718		TS(size, N);
719	}
720}
721
722static int
723zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
724{
725	struct disk_zone_args zone_args;
726	int error;
727
728	bzero(&zone_args, sizeof(zone_args));
729
730	zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
731	error = ioctl(fd, DIOCZONECMD, &zone_args);
732
733	if (error == 0) {
734		*zone_mode = zone_args.zone_params.disk_params.zone_mode;
735
736		switch (*zone_mode) {
737		case DISK_ZONE_MODE_NONE:
738			snprintf(zone_str, zone_str_len, "Not_Zoned");
739			break;
740		case DISK_ZONE_MODE_HOST_AWARE:
741			snprintf(zone_str, zone_str_len, "Host_Aware");
742			break;
743		case DISK_ZONE_MODE_DRIVE_MANAGED:
744			snprintf(zone_str, zone_str_len, "Drive_Managed");
745			break;
746		case DISK_ZONE_MODE_HOST_MANAGED:
747			snprintf(zone_str, zone_str_len, "Host_Managed");
748			break;
749		default:
750			snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",
751			    *zone_mode);
752			break;
753		}
754	}
755	return (error);
756}
757