]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/diskinfo/diskinfo.c
MFV r323107: 8414 Implemented zpool scrub pause/resume
[FreeBSD/FreeBSD.git] / usr.sbin / diskinfo / diskinfo.c
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$
32  */
33
34 #include <stdio.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <libutil.h>
43 #include <paths.h>
44 #include <err.h>
45 #include <sysexits.h>
46 #include <sys/aio.h>
47 #include <sys/disk.h>
48 #include <sys/param.h>
49 #include <sys/stat.h>
50 #include <sys/time.h>
51
52 #define NAIO    128
53
54 static void
55 usage(void)
56 {
57         fprintf(stderr, "usage: diskinfo [-cipsStvw] disk ...\n");
58         exit (1);
59 }
60
61 static int opt_c, opt_i, opt_p, opt_s, opt_S, opt_t, opt_v, opt_w;
62
63 static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
64 static void commandtime(int fd, off_t mediasize, u_int sectorsize);
65 static void iopsbench(int fd, off_t mediasize, u_int sectorsize);
66 static void slogbench(int fd, int isreg, off_t mediasize, u_int sectorsize);
67 static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str,
68                      size_t zone_str_len);
69
70 int
71 main(int argc, char **argv)
72 {
73         struct stat sb;
74         int i, ch, fd, error, exitval = 0;
75         char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
76         char zone_desc[64];
77         struct diocgattr_arg arg;
78         off_t   mediasize, stripesize, stripeoffset;
79         u_int   sectorsize, fwsectors, fwheads, zoned = 0, isreg;
80         uint32_t zone_mode;
81
82         while ((ch = getopt(argc, argv, "cipsStvw")) != -1) {
83                 switch (ch) {
84                 case 'c':
85                         opt_c = 1;
86                         opt_v = 1;
87                         break;
88                 case 'i':
89                         opt_i = 1;
90                         opt_v = 1;
91                         break;
92                 case 'p':
93                         opt_p = 1;
94                         break;
95                 case 's':
96                         opt_s = 1;
97                         break;
98                 case 'S':
99                         opt_S = 1;
100                         opt_v = 1;
101                         break;
102                 case 't':
103                         opt_t = 1;
104                         opt_v = 1;
105                         break;
106                 case 'v':
107                         opt_v = 1;
108                         break;
109                 case 'w':
110                         opt_w = 1;
111                         break;
112                 default:
113                         usage();
114                 }
115         }
116         argc -= optind;
117         argv += optind;
118
119         if (argc < 1)
120                 usage();
121
122         if ((opt_p && opt_s) || ((opt_p || opt_s) && (opt_c || opt_i || opt_t || opt_v))) {
123                 warnx("-p or -s cannot be used with other options");
124                 usage();
125         }
126
127         if (opt_S && !opt_w) {
128                 warnx("-S require also -w");
129                 usage();
130         }
131
132         for (i = 0; i < argc; i++) {
133                 fd = open(argv[i], (opt_w ? O_RDWR : O_RDONLY) | O_DIRECT);
134                 if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
135                         snprintf(buf, BUFSIZ, "%s%s", _PATH_DEV, argv[i]);
136                         fd = open(buf, O_RDONLY);
137                 }
138                 if (fd < 0) {
139                         warn("%s", argv[i]);
140                         exit(1);
141                 }
142                 error = fstat(fd, &sb);
143                 if (error != 0) {
144                         warn("cannot stat %s", argv[i]);
145                         exitval = 1;
146                         goto out;
147                 }
148                 isreg = S_ISREG(sb.st_mode);
149                 if (isreg) {
150                         mediasize = sb.st_size;
151                         sectorsize = S_BLKSIZE;
152                         fwsectors = 0;
153                         fwheads = 0;
154                         stripesize = sb.st_blksize;
155                         stripeoffset = 0;
156                         if (opt_p || opt_s) {
157                                 warnx("-p and -s only operate on physical devices: %s", argv[i]);
158                                 goto out;
159                         }
160                 } else {
161                         if (opt_p) {
162                                 if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0) {
163                                         printf("%s\n", physpath);
164                                 } else {
165                                         warnx("Failed to determine physpath for: %s", argv[i]);
166                                 }
167                                 goto out;
168                         }
169                         if (opt_s) {
170                                 if (ioctl(fd, DIOCGIDENT, ident) == 0) {
171                                         printf("%s\n", ident);
172                                 } else {
173                                         warnx("Failed to determine serial number for: %s", argv[i]);
174                                 }
175                                 goto out;
176                         }
177                         error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
178                         if (error) {
179                                 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
180                                 exitval = 1;
181                                 goto out;
182                         }
183                         error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
184                         if (error) {
185                                 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
186                                 exitval = 1;
187                                 goto out;
188                         }
189                         error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
190                         if (error)
191                                 fwsectors = 0;
192                         error = ioctl(fd, DIOCGFWHEADS, &fwheads);
193                         if (error)
194                                 fwheads = 0;
195                         error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
196                         if (error)
197                                 stripesize = 0;
198                         error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
199                         if (error)
200                                 stripeoffset = 0;
201                         error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc));
202                         if (error == 0)
203                                 zoned = 1;
204                 }
205                 if (!opt_v) {
206                         printf("%s", argv[i]);
207                         printf("\t%u", sectorsize);
208                         printf("\t%jd", (intmax_t)mediasize);
209                         printf("\t%jd", (intmax_t)mediasize/sectorsize);
210                         printf("\t%jd", (intmax_t)stripesize);
211                         printf("\t%jd", (intmax_t)stripeoffset);
212                         if (fwsectors != 0 && fwheads != 0) {
213                                 printf("\t%jd", (intmax_t)mediasize /
214                                     (fwsectors * fwheads * sectorsize));
215                                 printf("\t%u", fwheads);
216                                 printf("\t%u", fwsectors);
217                         } 
218                 } else {
219                         humanize_number(buf, 5, (int64_t)mediasize, "",
220                             HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
221                         printf("%s\n", argv[i]);
222                         printf("\t%-12u\t# sectorsize\n", sectorsize);
223                         printf("\t%-12jd\t# mediasize in bytes (%s)\n",
224                             (intmax_t)mediasize, buf);
225                         printf("\t%-12jd\t# mediasize in sectors\n",
226                             (intmax_t)mediasize/sectorsize);
227                         printf("\t%-12jd\t# stripesize\n", stripesize);
228                         printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
229                         if (fwsectors != 0 && fwheads != 0) {
230                                 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
231                                     (fwsectors * fwheads * sectorsize));
232                                 printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
233                                 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
234                         } 
235                         strlcpy(arg.name, "GEOM::descr", sizeof(arg.name));
236                         arg.len = sizeof(arg.value.str);
237                         if (ioctl(fd, DIOCGATTR, &arg) == 0)
238                                 printf("\t%-12s\t# Disk descr.\n", arg.value.str);
239                         if (ioctl(fd, DIOCGIDENT, ident) == 0)
240                                 printf("\t%-12s\t# Disk ident.\n", ident);
241                         if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
242                                 printf("\t%-12s\t# Physical path\n", physpath);
243                         if (zoned != 0)
244                                 printf("\t%-12s\t# Zone Mode\n", zone_desc);
245                 }
246                 printf("\n");
247                 if (opt_c)
248                         commandtime(fd, mediasize, sectorsize);
249                 if (opt_t)
250                         speeddisk(fd, mediasize, sectorsize);
251                 if (opt_i)
252                         iopsbench(fd, mediasize, sectorsize);
253                 if (opt_S)
254                         slogbench(fd, isreg, mediasize, sectorsize);
255 out:
256                 close(fd);
257         }
258         exit (exitval);
259 }
260
261 #define MAXTX (8*1024*1024)
262 #define MEGATX (1024*1024)
263 static uint8_t buf[MAXTX];
264
265 static void
266 rdsect(int fd, off_t blockno, u_int sectorsize)
267 {
268         int error;
269
270         if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1)
271                 err(1, "lseek");
272         error = read(fd, buf, sectorsize);
273         if (error == -1)
274                 err(1, "read");
275         if (error != (int)sectorsize)
276                 errx(1, "disk too small for test.");
277 }
278
279 static void
280 rdmega(int fd)
281 {
282         int error;
283
284         error = read(fd, buf, MEGATX);
285         if (error == -1)
286                 err(1, "read");
287         if (error != MEGATX)
288                 errx(1, "disk too small for test.");
289 }
290
291 static struct timeval tv1, tv2;
292
293 static void
294 T0(void)
295 {
296
297         fflush(stdout);
298         sync();
299         sleep(1);
300         sync();
301         sync();
302         gettimeofday(&tv1, NULL);
303 }
304
305 static double
306 delta_t(void)
307 {
308         double dt;
309
310         gettimeofday(&tv2, NULL);
311         dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
312         dt += (tv2.tv_sec - tv1.tv_sec);
313
314         return (dt);
315 }
316
317 static void
318 TN(int count)
319 {
320         double dt;
321
322         dt = delta_t();
323         printf("%5d iter in %10.6f sec = %8.3f msec\n",
324                 count, dt, dt * 1000.0 / count);
325 }
326
327 static void
328 TR(double count)
329 {
330         double dt;
331
332         dt = delta_t();
333         printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
334                 count, dt, count / dt);
335 }
336
337 static void
338 TI(double count)
339 {
340         double dt;
341
342         dt = delta_t();
343         printf("%8.0f ops in  %10.6f sec = %8.0f IOPS\n",
344                 count, dt, count / dt);
345 }
346
347 static void
348 TS(u_int size, int count)
349 {
350         double dt;
351
352         dt = delta_t();
353         printf("%8.1f usec/IO = %8.1f Mbytes/s\n",
354             dt * 1000000.0 / count, size * count / dt / (1024 * 1024));
355 }
356
357 static void
358 speeddisk(int fd, off_t mediasize, u_int sectorsize)
359 {
360         int bulk, i;
361         off_t b0, b1, sectorcount, step;
362
363         sectorcount = mediasize / sectorsize;
364         if (sectorcount <= 0)
365                 return;         /* Can't test devices with no sectors */
366
367         step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
368         if (step > 16384)
369                 step = 16384;
370         bulk = mediasize / (1024 * 1024);
371         if (bulk > 100)
372                 bulk = 100;
373
374         printf("Seek times:\n");
375         printf("\tFull stroke:\t");
376         b0 = 0;
377         b1 = sectorcount - step;
378         T0();
379         for (i = 0; i < 125; i++) {
380                 rdsect(fd, b0, sectorsize);
381                 b0 += step;
382                 rdsect(fd, b1, sectorsize);
383                 b1 -= step;
384         }
385         TN(250);
386
387         printf("\tHalf stroke:\t");
388         b0 = sectorcount / 4;
389         b1 = b0 + sectorcount / 2;
390         T0();
391         for (i = 0; i < 125; i++) {
392                 rdsect(fd, b0, sectorsize);
393                 b0 += step;
394                 rdsect(fd, b1, sectorsize);
395                 b1 += step;
396         }
397         TN(250);
398         printf("\tQuarter stroke:\t");
399         b0 = sectorcount / 4;
400         b1 = b0 + sectorcount / 4;
401         T0();
402         for (i = 0; i < 250; i++) {
403                 rdsect(fd, b0, sectorsize);
404                 b0 += step;
405                 rdsect(fd, b1, sectorsize);
406                 b1 += step;
407         }
408         TN(500);
409
410         printf("\tShort forward:\t");
411         b0 = sectorcount / 2;
412         T0();
413         for (i = 0; i < 400; i++) {
414                 rdsect(fd, b0, sectorsize);
415                 b0 += step;
416         }
417         TN(400);
418
419         printf("\tShort backward:\t");
420         b0 = sectorcount / 2;
421         T0();
422         for (i = 0; i < 400; i++) {
423                 rdsect(fd, b0, sectorsize);
424                 b0 -= step;
425         }
426         TN(400);
427
428         printf("\tSeq outer:\t");
429         b0 = 0;
430         T0();
431         for (i = 0; i < 2048; i++) {
432                 rdsect(fd, b0, sectorsize);
433                 b0++;
434         }
435         TN(2048);
436
437         printf("\tSeq inner:\t");
438         b0 = sectorcount - 2048;
439         T0();
440         for (i = 0; i < 2048; i++) {
441                 rdsect(fd, b0, sectorsize);
442                 b0++;
443         }
444         TN(2048);
445
446         printf("\nTransfer rates:\n");
447         printf("\toutside:     ");
448         rdsect(fd, 0, sectorsize);
449         T0();
450         for (i = 0; i < bulk; i++) {
451                 rdmega(fd);
452         }
453         TR(bulk * 1024);
454
455         printf("\tmiddle:      ");
456         b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
457         rdsect(fd, b0, sectorsize);
458         T0();
459         for (i = 0; i < bulk; i++) {
460                 rdmega(fd);
461         }
462         TR(bulk * 1024);
463
464         printf("\tinside:      ");
465         b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
466         rdsect(fd, b0, sectorsize);
467         T0();
468         for (i = 0; i < bulk; i++) {
469                 rdmega(fd);
470         }
471         TR(bulk * 1024);
472
473         printf("\n");
474         return;
475 }
476
477 static void
478 commandtime(int fd, off_t mediasize, u_int sectorsize)
479 {       
480         double dtmega, dtsector;
481         int i;
482
483         printf("I/O command overhead:\n");
484         i = mediasize;
485         rdsect(fd, 0, sectorsize);
486         T0();
487         for (i = 0; i < 10; i++)
488                 rdmega(fd);
489         dtmega = delta_t();
490
491         printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
492                 dtmega, dtmega*100/2048);
493
494         rdsect(fd, 0, sectorsize);
495         T0();
496         for (i = 0; i < 20480; i++)
497                 rdsect(fd, 0, sectorsize);
498         dtsector = delta_t();
499
500         printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
501                 dtsector, dtsector*100/2048);
502         printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
503                 (dtsector - dtmega)*100/2048);
504
505         printf("\n");
506         return;
507 }
508
509 static void
510 iops(int fd, off_t mediasize, u_int sectorsize)
511 {
512         struct aiocb aios[NAIO], *aiop;
513         ssize_t ret;
514         off_t sectorcount;
515         int error, i, queued, completed;
516
517         sectorcount = mediasize / sectorsize;
518
519         for (i = 0; i < NAIO; i++) {
520                 aiop = &(aios[i]);
521                 bzero(aiop, sizeof(*aiop));
522                 aiop->aio_buf = malloc(sectorsize);
523                 if (aiop->aio_buf == NULL)
524                         err(1, "malloc");
525         }
526
527         T0();
528         for (i = 0; i < NAIO; i++) {
529                 aiop = &(aios[i]);
530
531                 aiop->aio_fildes = fd;
532                 aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
533                 aiop->aio_nbytes = sectorsize;
534
535                 error = aio_read(aiop);
536                 if (error != 0)
537                         err(1, "aio_read");
538         }
539
540         queued = i;
541         completed = 0;
542
543         for (;;) {
544                 ret = aio_waitcomplete(&aiop, NULL);
545                 if (ret < 0)
546                         err(1, "aio_waitcomplete");
547                 if (ret != (ssize_t)sectorsize)
548                         errx(1, "short read");
549
550                 completed++;
551
552                 if (delta_t() < 3.0) {
553                         aiop->aio_fildes = fd;
554                         aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
555                         aiop->aio_nbytes = sectorsize;
556
557                         error = aio_read(aiop);
558                         if (error != 0)
559                                 err(1, "aio_read");
560
561                         queued++;
562                 } else if (completed == queued) {
563                         break;
564                 }
565         }
566
567         TI(completed);
568
569         return;
570 }
571
572 static void
573 iopsbench(int fd, off_t mediasize, u_int sectorsize)
574 {
575         printf("Asynchronous random reads:\n");
576
577         printf("\tsectorsize:  ");
578         iops(fd, mediasize, sectorsize);
579
580         if (sectorsize != 4096) {
581                 printf("\t4 kbytes:    ");
582                 iops(fd, mediasize, 4096);
583         }
584
585         printf("\t32 kbytes:   ");
586         iops(fd, mediasize, 32 * 1024);
587
588         printf("\t128 kbytes:  ");
589         iops(fd, mediasize, 128 * 1024);
590
591         printf("\n");
592 }
593
594 #define MAXIO (128*1024)
595 #define MAXIOS (MAXTX / MAXIO)
596
597 static void
598 parwrite(int fd, size_t size, off_t off)
599 {
600         struct aiocb aios[MAXIOS];
601         off_t o;
602         size_t s;
603         int n, error;
604         struct aiocb *aiop;
605
606         for (n = 0, o = 0; size > MAXIO; n++, size -= s, o += s) {
607                 s = (size >= MAXIO) ? MAXIO : size;
608                 aiop = &aios[n];
609                 bzero(aiop, sizeof(*aiop));
610                 aiop->aio_buf = &buf[o];
611                 aiop->aio_fildes = fd;
612                 aiop->aio_offset = off + o;
613                 aiop->aio_nbytes = s;
614                 error = aio_write(aiop);
615                 if (error != 0)
616                         err(EX_IOERR, "AIO write submit error");
617         }
618         error = pwrite(fd, &buf[o], size, off + o);
619         if (error < 0)
620                 err(EX_IOERR, "Sync write error");
621         for (; n > 0; n--) {
622                 error = aio_waitcomplete(&aiop, NULL);
623                 if (error < 0)
624                         err(EX_IOERR, "AIO write wait error");
625         }
626 }
627
628 static void
629 slogbench(int fd, int isreg, off_t mediasize, u_int sectorsize)
630 {
631         off_t off;
632         u_int size;
633         int error, n, N;
634
635         printf("Synchronous random writes:\n");
636         for (size = sectorsize; size <= MAXTX; size *= 2) {
637                 printf("\t%4.4g kbytes: ", (double)size / 1024);
638                 N = 0;
639                 T0();
640                 do {
641                         for (n = 0; n < 250; n++) {
642                                 off = random() % (mediasize / size);
643                                 parwrite(fd, size, off * size);
644                                 if (isreg)
645                                         error = fsync(fd);
646                                 else
647                                         error = ioctl(fd, DIOCGFLUSH);
648                                 if (error < 0)
649                                         err(EX_IOERR, "Flush error");
650                         }
651                         N += 250;
652                 } while (delta_t() < 1.0);
653                 TS(size, N);
654         }
655 }
656
657 static int
658 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
659 {
660         struct disk_zone_args zone_args;
661         int error;
662
663         bzero(&zone_args, sizeof(zone_args));
664
665         zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
666         error = ioctl(fd, DIOCZONECMD, &zone_args);
667
668         if (error == 0) {
669                 *zone_mode = zone_args.zone_params.disk_params.zone_mode;
670
671                 switch (*zone_mode) {
672                 case DISK_ZONE_MODE_NONE:
673                         snprintf(zone_str, zone_str_len, "Not_Zoned");
674                         break;
675                 case DISK_ZONE_MODE_HOST_AWARE:
676                         snprintf(zone_str, zone_str_len, "Host_Aware");
677                         break;
678                 case DISK_ZONE_MODE_DRIVE_MANAGED:
679                         snprintf(zone_str, zone_str_len, "Drive_Managed");
680                         break;
681                 case DISK_ZONE_MODE_HOST_MANAGED:
682                         snprintf(zone_str, zone_str_len, "Host_Managed");
683                         break;
684                 default:
685                         snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",
686                             *zone_mode);
687                         break;
688                 }
689         }
690         return (error);
691 }