]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/diskinfo/diskinfo.c
Make it possible for diskinfo(8) to operate on files. This is useful
[FreeBSD/FreeBSD.git] / usr.sbin / diskinfo / diskinfo.c
1 /*-
2  * Copyright (c) 2003 Poul-Henning Kamp
3  * Copyright (c) 2015 Spectra Logic Corporation
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The names of the authors may not be used to endorse or promote
15  *    products derived from this software without specific prior written
16  *    permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <libutil.h>
41 #include <paths.h>
42 #include <err.h>
43 #include <sys/disk.h>
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/time.h>
47
48 static void
49 usage(void)
50 {
51         fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n");
52         exit (1);
53 }
54
55 static int opt_c, opt_t, opt_v;
56
57 static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
58 static void commandtime(int fd, off_t mediasize, u_int sectorsize);
59 static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str,
60                      size_t zone_str_len);
61
62 int
63 main(int argc, char **argv)
64 {
65         struct stat sb;
66         int i, ch, fd, error, exitval = 0;
67         char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
68         char zone_desc[64];
69         off_t   mediasize, stripesize, stripeoffset;
70         u_int   sectorsize, fwsectors, fwheads, zoned = 0;
71         uint32_t zone_mode;
72
73         while ((ch = getopt(argc, argv, "ctv")) != -1) {
74                 switch (ch) {
75                 case 'c':
76                         opt_c = 1;
77                         opt_v = 1;
78                         break;
79                 case 't':
80                         opt_t = 1;
81                         opt_v = 1;
82                         break;
83                 case 'v':
84                         opt_v = 1;
85                         break;
86                 default:
87                         usage();
88                 }
89         }
90         argc -= optind;
91         argv += optind;
92
93         if (argc < 1)
94                 usage();
95
96         for (i = 0; i < argc; i++) {
97                 fd = open(argv[i], O_RDONLY | O_DIRECT);
98                 if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
99                         sprintf(buf, "%s%s", _PATH_DEV, argv[i]);
100                         fd = open(buf, O_RDONLY);
101                 }
102                 if (fd < 0) {
103                         warn("%s", argv[i]);
104                         exitval = 1;
105                         goto out;
106                 }
107                 error = fstat(fd, &sb);
108                 if (error != 0) {
109                         warn("cannot stat %s", argv[i]);
110                         exitval = 1;
111                         goto out;
112                 }
113                 if (S_ISREG(sb.st_mode)) {
114                         mediasize = sb.st_size;
115                         sectorsize = S_BLKSIZE;
116                         fwsectors = 0;
117                         fwheads = 0;
118                         stripesize = sb.st_blksize;
119                         stripeoffset = 0;
120                 } else {
121                         error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
122                         if (error) {
123                                 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
124                                 exitval = 1;
125                                 goto out;
126                         }
127                         error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
128                         if (error) {
129                                 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
130                                 exitval = 1;
131                                 goto out;
132                         }
133                         error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
134                         if (error)
135                                 fwsectors = 0;
136                         error = ioctl(fd, DIOCGFWHEADS, &fwheads);
137                         if (error)
138                                 fwheads = 0;
139                         error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
140                         if (error)
141                                 stripesize = 0;
142                         error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
143                         if (error)
144                                 stripeoffset = 0;
145                         error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc));
146                         if (error == 0)
147                                 zoned = 1;
148                 }
149                 if (!opt_v) {
150                         printf("%s", argv[i]);
151                         printf("\t%u", sectorsize);
152                         printf("\t%jd", (intmax_t)mediasize);
153                         printf("\t%jd", (intmax_t)mediasize/sectorsize);
154                         printf("\t%jd", (intmax_t)stripesize);
155                         printf("\t%jd", (intmax_t)stripeoffset);
156                         if (fwsectors != 0 && fwheads != 0) {
157                                 printf("\t%jd", (intmax_t)mediasize /
158                                     (fwsectors * fwheads * sectorsize));
159                                 printf("\t%u", fwheads);
160                                 printf("\t%u", fwsectors);
161                         } 
162                 } else {
163                         humanize_number(buf, 5, (int64_t)mediasize, "",
164                             HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
165                         printf("%s\n", argv[i]);
166                         printf("\t%-12u\t# sectorsize\n", sectorsize);
167                         printf("\t%-12jd\t# mediasize in bytes (%s)\n",
168                             (intmax_t)mediasize, buf);
169                         printf("\t%-12jd\t# mediasize in sectors\n",
170                             (intmax_t)mediasize/sectorsize);
171                         printf("\t%-12jd\t# stripesize\n", stripesize);
172                         printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
173                         if (fwsectors != 0 && fwheads != 0) {
174                                 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
175                                     (fwsectors * fwheads * sectorsize));
176                                 printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
177                                 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
178                         } 
179                         if (ioctl(fd, DIOCGIDENT, ident) == 0)
180                                 printf("\t%-12s\t# Disk ident.\n", ident);
181                         if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
182                                 printf("\t%-12s\t# Physical path\n", physpath);
183                         if (zoned != 0)
184                                 printf("\t%-12s\t# Zone Mode\n", zone_desc);
185                 }
186                 printf("\n");
187                 if (opt_c)
188                         commandtime(fd, mediasize, sectorsize);
189                 if (opt_t)
190                         speeddisk(fd, mediasize, sectorsize);
191 out:
192                 close(fd);
193         }
194         exit (exitval);
195 }
196
197
198 static char sector[65536];
199 static char mega[1024 * 1024];
200
201 static void
202 rdsect(int fd, off_t blockno, u_int sectorsize)
203 {
204         int error;
205
206         lseek(fd, (off_t)blockno * sectorsize, SEEK_SET);
207         error = read(fd, sector, sectorsize);
208         if (error == -1)
209                 err(1, "read");
210         if (error != (int)sectorsize)
211                 errx(1, "disk too small for test.");
212 }
213
214 static void
215 rdmega(int fd)
216 {
217         int error;
218
219         error = read(fd, mega, sizeof(mega));
220         if (error == -1)
221                 err(1, "read");
222         if (error != sizeof(mega))
223                 errx(1, "disk too small for test.");
224 }
225
226 static struct timeval tv1, tv2;
227
228 static void
229 T0(void)
230 {
231
232         fflush(stdout);
233         sync();
234         sleep(1);
235         sync();
236         sync();
237         gettimeofday(&tv1, NULL);
238 }
239
240 static void
241 TN(int count)
242 {
243         double dt;
244
245         gettimeofday(&tv2, NULL);
246         dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
247         dt += (tv2.tv_sec - tv1.tv_sec);
248         printf("%5d iter in %10.6f sec = %8.3f msec\n",
249                 count, dt, dt * 1000.0 / count);
250 }
251
252 static void
253 TR(double count)
254 {
255         double dt;
256
257         gettimeofday(&tv2, NULL);
258         dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
259         dt += (tv2.tv_sec - tv1.tv_sec);
260         printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
261                 count, dt, count / dt);
262 }
263
264 static void
265 speeddisk(int fd, off_t mediasize, u_int sectorsize)
266 {
267         int bulk, i;
268         off_t b0, b1, sectorcount, step;
269
270         sectorcount = mediasize / sectorsize;
271         step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
272         if (step > 16384)
273                 step = 16384;
274         bulk = mediasize / (1024 * 1024);
275         if (bulk > 100)
276                 bulk = 100;
277
278         printf("Seek times:\n");
279         printf("\tFull stroke:\t");
280         b0 = 0;
281         b1 = sectorcount - step;
282         T0();
283         for (i = 0; i < 125; i++) {
284                 rdsect(fd, b0, sectorsize);
285                 b0 += step;
286                 rdsect(fd, b1, sectorsize);
287                 b1 -= step;
288         }
289         TN(250);
290
291         printf("\tHalf stroke:\t");
292         b0 = sectorcount / 4;
293         b1 = b0 + sectorcount / 2;
294         T0();
295         for (i = 0; i < 125; i++) {
296                 rdsect(fd, b0, sectorsize);
297                 b0 += step;
298                 rdsect(fd, b1, sectorsize);
299                 b1 += step;
300         }
301         TN(250);
302         printf("\tQuarter stroke:\t");
303         b0 = sectorcount / 4;
304         b1 = b0 + sectorcount / 4;
305         T0();
306         for (i = 0; i < 250; i++) {
307                 rdsect(fd, b0, sectorsize);
308                 b0 += step;
309                 rdsect(fd, b1, sectorsize);
310                 b1 += step;
311         }
312         TN(500);
313
314         printf("\tShort forward:\t");
315         b0 = sectorcount / 2;
316         T0();
317         for (i = 0; i < 400; i++) {
318                 rdsect(fd, b0, sectorsize);
319                 b0 += step;
320         }
321         TN(400);
322
323         printf("\tShort backward:\t");
324         b0 = sectorcount / 2;
325         T0();
326         for (i = 0; i < 400; i++) {
327                 rdsect(fd, b0, sectorsize);
328                 b0 -= step;
329         }
330         TN(400);
331
332         printf("\tSeq outer:\t");
333         b0 = 0;
334         T0();
335         for (i = 0; i < 2048; i++) {
336                 rdsect(fd, b0, sectorsize);
337                 b0++;
338         }
339         TN(2048);
340
341         printf("\tSeq inner:\t");
342         b0 = sectorcount - 2048;
343         T0();
344         for (i = 0; i < 2048; i++) {
345                 rdsect(fd, b0, sectorsize);
346                 b0++;
347         }
348         TN(2048);
349
350         printf("Transfer rates:\n");
351         printf("\toutside:     ");
352         rdsect(fd, 0, sectorsize);
353         T0();
354         for (i = 0; i < bulk; i++) {
355                 rdmega(fd);
356         }
357         TR(bulk * 1024);
358
359         printf("\tmiddle:      ");
360         b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
361         rdsect(fd, b0, sectorsize);
362         T0();
363         for (i = 0; i < bulk; i++) {
364                 rdmega(fd);
365         }
366         TR(bulk * 1024);
367
368         printf("\tinside:      ");
369         b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
370         rdsect(fd, b0, sectorsize);
371         T0();
372         for (i = 0; i < bulk; i++) {
373                 rdmega(fd);
374         }
375         TR(bulk * 1024);
376
377         printf("\n");
378         return;
379 }
380
381 static void
382 commandtime(int fd, off_t mediasize, u_int sectorsize)
383 {       
384         double dtmega, dtsector;
385         int i;
386
387         printf("I/O command overhead:\n");
388         i = mediasize;
389         rdsect(fd, 0, sectorsize);
390         T0();
391         for (i = 0; i < 10; i++)
392                 rdmega(fd);
393         gettimeofday(&tv2, NULL);
394         dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6;
395         dtmega += (tv2.tv_sec - tv1.tv_sec);
396
397         printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
398                 dtmega, dtmega*100/2048);
399
400         rdsect(fd, 0, sectorsize);
401         T0();
402         for (i = 0; i < 20480; i++)
403                 rdsect(fd, 0, sectorsize);
404         gettimeofday(&tv2, NULL);
405         dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6;
406         dtsector += (tv2.tv_sec - tv1.tv_sec);
407
408         printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
409                 dtsector, dtsector*100/2048);
410         printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
411                 (dtsector - dtmega)*100/2048);
412
413         printf("\n");
414         return;
415 }
416
417 static int
418 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
419 {
420         struct disk_zone_args zone_args;
421         int error;
422
423         bzero(&zone_args, sizeof(zone_args));
424
425         zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
426         error = ioctl(fd, DIOCZONECMD, &zone_args);
427
428         if (error == 0) {
429                 *zone_mode = zone_args.zone_params.disk_params.zone_mode;
430
431                 switch (*zone_mode) {
432                 case DISK_ZONE_MODE_NONE:
433                         snprintf(zone_str, zone_str_len, "Not_Zoned");
434                         break;
435                 case DISK_ZONE_MODE_HOST_AWARE:
436                         snprintf(zone_str, zone_str_len, "Host_Aware");
437                         break;
438                 case DISK_ZONE_MODE_DRIVE_MANAGED:
439                         snprintf(zone_str, zone_str_len, "Drive_Managed");
440                         break;
441                 case DISK_ZONE_MODE_HOST_MANAGED:
442                         snprintf(zone_str, zone_str_len, "Host_Managed");
443                         break;
444                 default:
445                         snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",
446                             *zone_mode);
447                         break;
448                 }
449         }
450         return (error);
451 }