iostat.c revision 1.48 1 /* $NetBSD: iostat.c,v 1.48 2006/02/12 22:11:54 dsl Exp $ */
2
3 /*
4 * Copyright (c) 1996 John M. Vinopal
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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
18 * by John M. Vinopal.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /*-
36 * Copyright (c) 1986, 1991, 1993
37 * The Regents of the University of California. All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\n\
67 The Regents of the University of California. All rights reserved.\n");
68 #endif /* not lint */
69
70 #ifndef lint
71 #if 0
72 static char sccsid[] = "@(#)iostat.c 8.3 (Berkeley) 4/28/95";
73 #else
74 __RCSID("$NetBSD: iostat.c,v 1.48 2006/02/12 22:11:54 dsl Exp $");
75 #endif
76 #endif /* not lint */
77
78 #include <sys/types.h>
79 #include <sys/ioctl.h>
80 #include <sys/sched.h>
81 #include <sys/time.h>
82
83 #include <err.h>
84 #include <ctype.h>
85 #include <signal.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90
91 #include "dkstats.h"
92 #include "tpstats.h"
93
94 /* Namelist and memory files. */
95 char *nlistf, *memf;
96
97 int hz, reps, interval;
98 static int todo = 0;
99 static int defdrives;
100 static int winlines = 20;
101 static int wincols = 80;
102
103 #define ISSET(x, a) ((x) & (a))
104 #define SHOW_CPU 1<<0
105 #define SHOW_TTY 1<<1
106 #define SHOW_STATS_1 1<<2
107 #define SHOW_STATS_2 1<<3
108 #define SHOW_STATS_X 1<<4
109 #define SHOW_TOTALS 1<<7
110 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
111
112 static void cpustats(void);
113 static void disk_stats(double);
114 static void disk_stats2(double);
115 static void disk_statsx(double);
116 static void sig_header(int);
117 static volatile int do_header;
118 static void header(void);
119 static void usage(void);
120 static void display(void);
121 static int selectdrives(int, char *[]);
122
123 int main(int, char *[]);
124
125 int
126 main(int argc, char *argv[])
127 {
128 int ch, hdrcnt, ndrives, lines;
129 struct timespec tv;
130 struct ttysize ts;
131
132 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1)
133 switch (ch) {
134 case 'c':
135 if ((reps = atoi(optarg)) <= 0)
136 errx(1, "repetition count <= 0.");
137 break;
138 case 'C':
139 todo |= SHOW_CPU;
140 break;
141 case 'd':
142 todo &= ~SHOW_STATS_ALL;
143 todo |= SHOW_STATS_1;
144 break;
145 case 'D':
146 todo &= ~SHOW_STATS_ALL;
147 todo |= SHOW_STATS_2;
148 break;
149 case 'I':
150 todo |= SHOW_TOTALS;
151 break;
152 case 'M':
153 memf = optarg;
154 break;
155 case 'N':
156 nlistf = optarg;
157 break;
158 case 'T':
159 todo |= SHOW_TTY;
160 break;
161 case 'w':
162 if ((interval = atoi(optarg)) <= 0)
163 errx(1, "interval <= 0.");
164 break;
165 case 'x':
166 todo &= ~SHOW_STATS_ALL;
167 todo |= SHOW_STATS_X;
168 break;
169 case '?':
170 default:
171 usage();
172 }
173 argc -= optind;
174 argv += optind;
175
176 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
177 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
178 if (ISSET(todo, SHOW_STATS_X)) {
179 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
180 todo |= SHOW_STATS_X;
181 }
182
183 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
184 if (ts.ts_lines)
185 winlines = ts.ts_lines;
186 if (ts.ts_cols)
187 wincols = ts.ts_cols;
188 }
189
190 defdrives = wincols;
191 if (ISSET(todo, SHOW_CPU))
192 defdrives -= 16; /* XXX magic number */
193 if (ISSET(todo, SHOW_TTY))
194 defdrives -= 9; /* XXX magic number */
195 defdrives /= 18; /* XXX magic number */
196
197 dkinit(0);
198 tpinit(0);
199 cpureadstats();
200 dkreadstats();
201 tpreadstats();
202 ndrives = selectdrives(argc, argv);
203 if (ndrives == 0) {
204 /* No drives are selected. No need to show disk stats. */
205 todo &= ~SHOW_STATS_ALL;
206 if (todo == 0)
207 errx(1, "no drives");
208 }
209 if (ISSET(todo, SHOW_STATS_X))
210 lines = ndrives;
211 else
212 lines = 1;
213
214 tv.tv_sec = interval;
215 tv.tv_nsec = 0;
216
217 /* print a new header on sigcont */
218 (void)signal(SIGCONT, sig_header);
219
220 for (hdrcnt = 1;;) {
221 if (do_header || lines > 1 || (hdrcnt -= lines) <= 0) {
222 do_header = 0;
223 header();
224 hdrcnt = winlines - 4;
225 }
226
227 if (!ISSET(todo, SHOW_TOTALS)) {
228 cpuswap();
229 dkswap();
230 tpswap();
231 }
232
233 display();
234
235 if (reps >= 0 && --reps <= 0)
236 break;
237 nanosleep(&tv, NULL);
238 cpureadstats();
239 dkreadstats();
240 tpreadstats();
241 }
242 exit(0);
243 }
244
245 static void
246 sig_header(int signo)
247 {
248 do_header = 1;
249 }
250
251 static void
252 header()
253 {
254 int i;
255
256 /* Main Headers. */
257 if (ISSET(todo, SHOW_STATS_X)) {
258 if (ISSET(todo, SHOW_TOTALS)) {
259 (void)printf(
260 "device read KB/t xfr time MB ");
261 (void)printf(" write KB/t xfr time MB\n");
262 } else {
263 (void)printf(
264 "device read KB/t r/s time MB/s");
265 (void)printf(" write KB/t w/s time MB/s\n");
266 }
267 return;
268 }
269
270 if (ISSET(todo, SHOW_TTY))
271 (void)printf(" tty");
272
273 if (ISSET(todo, SHOW_STATS_1)) {
274 for (i = 0; i < dk_ndrive; i++)
275 if (cur.dk_select[i])
276 (void)printf(" %9.9s ", cur.dk_name[i]);
277 for (i = 0; i < tp_ndrive; i++)
278 if (cur_tape.select[i])
279 (void)printf(" %9.9s ",
280 cur_tape.name[i]);
281 }
282
283 if (ISSET(todo, SHOW_STATS_2)) {
284 for (i = 0; i < dk_ndrive; i++)
285 if (cur.dk_select[i])
286 (void)printf(" %9.9s ", cur.dk_name[i]);
287 for (i = 0; i < tp_ndrive; i++)
288 if (cur_tape.select[i])
289 (void)printf(" %9.9s ",
290 cur_tape.name[i]);
291 }
292
293 if (ISSET(todo, SHOW_CPU))
294 (void)printf(" CPU");
295
296 printf("\n");
297
298 /* Sub-Headers. */
299 if (ISSET(todo, SHOW_TTY))
300 printf(" tin tout");
301
302 if (ISSET(todo, SHOW_STATS_1)) {
303 for (i = 0; i < dk_ndrive; i++)
304 if (cur.dk_select[i]) {
305 if (ISSET(todo, SHOW_TOTALS))
306 (void)printf(" KB/t xfr MB ");
307 else
308 (void)printf(" KB/t t/s MB/s ");
309 }
310 for (i = 0; i < tp_ndrive; i++)
311 if (cur_tape.select[i]) {
312 if (ISSET(todo, SHOW_TOTALS))
313 (void)printf(" KB/t xfr MB ");
314 else
315 (void)printf(" KB/t t/s MB/s ");
316 }
317 }
318
319 if (ISSET(todo, SHOW_STATS_2)) {
320 for (i = 0; i < dk_ndrive; i++)
321 if (cur.dk_select[i])
322 (void)printf(" KB xfr time ");
323 for (i = 0; i < tp_ndrive; i++)
324 if (cur_tape.select[i])
325 (void)printf(" KB xfr time ");
326 }
327
328 if (ISSET(todo, SHOW_CPU))
329 (void)printf(" us ni sy in id");
330 printf("\n");
331 }
332
333 static void
334 disk_stats(double etime)
335 {
336 int dn;
337 double atime, mbps;
338
339 for (dn = 0; dn < dk_ndrive; ++dn) {
340 if (!cur.dk_select[dn])
341 continue;
342 /* average Kbytes per transfer. */
343 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
344 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
345 1024.0) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
346 else
347 mbps = 0.0;
348 (void)printf(" %5.2f", mbps);
349
350 /* average transfers per second. */
351 (void)printf(" %4.0f",
352 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
353
354 /* time busy in disk activity */
355 atime = (double)cur.dk_time[dn].tv_sec +
356 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
357
358 /* Megabytes per second. */
359 if (atime != 0.0)
360 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
361 (double)(1024 * 1024);
362 else
363 mbps = 0;
364 (void)printf(" %5.2f ", mbps / etime);
365 }
366 }
367
368 static void
369 disk_stats2(double etime)
370 {
371 int dn;
372 double atime;
373
374 for (dn = 0; dn < dk_ndrive; ++dn) {
375 if (!cur.dk_select[dn])
376 continue;
377
378 /* average kbytes per second. */
379 (void)printf(" %5.0f",
380 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0 / etime);
381
382 /* average transfers per second. */
383 (void)printf(" %5.0f",
384 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
385
386 /* average time busy in disk activity */
387 atime = (double)cur.dk_time[dn].tv_sec +
388 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
389 (void)printf(" %4.2f ", atime / etime);
390 }
391 }
392
393 static void
394 disk_statsx(double etime)
395 {
396 int dn;
397 double atime, kbps;
398
399 for (dn = 0; dn < dk_ndrive; ++dn) {
400 if (!cur.dk_select[dn])
401 continue;
402
403 (void)printf("%-8.8s", cur.dk_name[dn]);
404
405 /* average read Kbytes per transfer */
406 if (cur.dk_rxfer[dn])
407 kbps = (cur.dk_rbytes[dn] / 1024.0) / cur.dk_rxfer[dn];
408 else
409 kbps = 0.0;
410 (void)printf(" %8.2f", kbps);
411
412 /* average read transfers
413 (per second) */
414 (void)printf(" %6.0f", cur.dk_rxfer[dn] / etime);
415
416 /* time read busy in disk activity */
417 atime = (double)cur.dk_time[dn].tv_sec +
418 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
419 (void)printf(" %6.2f", atime / etime);
420
421 /* average read megabytes
422 (per second) */
423 (void)printf(" %8.2f",
424 cur.dk_rbytes[dn] / (1024.0 * 1024) / etime);
425
426
427 /* average write Kbytes per transfer */
428 if (cur.dk_wxfer[dn])
429 kbps = (cur.dk_wbytes[dn] / 1024.0) / cur.dk_wxfer[dn];
430 else
431 kbps = 0.0;
432 (void)printf(" %8.2f", kbps);
433
434 /* average write transfers
435 (per second) */
436 (void)printf(" %6.0f", cur.dk_wxfer[dn] / etime);
437
438 /* time write busy in disk activity */
439 atime = (double)cur.dk_time[dn].tv_sec +
440 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
441 (void)printf(" %6.2f", atime / etime);
442
443 /* average write megabytes
444 (per second) */
445 (void)printf(" %8.2f\n",
446 cur.dk_wbytes[dn] / (1024.0 * 1024) / etime);
447 }
448 }
449
450 static void
451 tape_stats(double etime)
452 {
453 int dn;
454 double atime, mbps;
455
456 for (dn = 0; dn < tp_ndrive; ++dn) {
457 if (!cur_tape.select[dn])
458 continue;
459 /* average Kbytes per transfer. */
460 if (cur_tape.rxfer[dn] + cur_tape.wxfer[dn])
461 mbps = ((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
462 1024.0) / (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]);
463 else
464 mbps = 0.0;
465 (void)printf(" %5.2f", mbps);
466
467 /* average transfers per second. */
468 (void)printf(" %4.0f",
469 (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
470
471 /* time busy in disk activity */
472 atime = (double)cur_tape.time[dn].tv_sec +
473 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
474
475 /* Megabytes per second. */
476 if (atime != 0.0)
477 mbps = (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
478 (double)(1024 * 1024);
479 else
480 mbps = 0;
481 (void)printf(" %5.2f ", mbps / etime);
482 }
483 }
484
485 static void
486 tape_stats2(double etime)
487 {
488 int dn;
489 double atime;
490
491 for (dn = 0; dn < tp_ndrive; ++dn) {
492 if (!cur_tape.select[dn])
493 continue;
494
495 /* average kbytes per second. */
496 (void)printf(" %5.0f",
497 (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / 1024.0 / etime);
498
499 /* average transfers per second. */
500 (void)printf(" %5.0f",
501 (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
502
503 /* average time busy in disk activity */
504 atime = (double)cur_tape.time[dn].tv_sec +
505 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
506 (void)printf(" %4.2f ", atime / etime);
507 }
508 }
509
510 static void
511 tape_statsx(double etime)
512 {
513 int dn;
514 double atime, kbps;
515
516 for (dn = 0; dn < tp_ndrive; ++dn) {
517 if (!cur_tape.select[dn])
518 continue;
519
520 (void)printf("%-8.8s", cur_tape.name[dn]);
521
522 /* average read Kbytes per transfer */
523 if (cur.dk_rxfer[dn])
524 kbps = (cur_tape.rbytes[dn] / 1024.0) / cur_tape.rxfer[dn];
525 else
526 kbps = 0.0;
527 (void)printf(" %8.2f", kbps);
528
529 /* average read transfers
530 (per second) */
531 (void)printf(" %6.0f", cur_tape.rxfer[dn] / etime);
532
533 /* time read busy in disk activity */
534 atime = (double)cur_tape.time[dn].tv_sec +
535 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
536 (void)printf(" %6.2f", atime / etime);
537
538 /* average read megabytes
539 (per second) */
540 (void)printf(" %8.2f",
541 cur_tape.rbytes[dn] / (1024.0 * 1024) / etime);
542
543
544 /* average write Kbytes per transfer */
545 if (cur_tape.wxfer[dn])
546 kbps = (cur_tape.wbytes[dn] / 1024.0) / cur_tape.wxfer[dn];
547 else
548 kbps = 0.0;
549 (void)printf(" %8.2f", kbps);
550
551 /* average write transfers
552 (per second) */
553 (void)printf(" %6.0f", cur_tape.wxfer[dn] / etime);
554
555 /* time write busy in disk activity */
556 atime = (double)cur_tape.time[dn].tv_sec +
557 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
558 (void)printf(" %6.2f", atime / etime);
559
560 /* average write megabytes
561 (per second) */
562 (void)printf(" %8.2f\n",
563 cur_tape.wbytes[dn] / (1024.0 * 1024) / etime);
564 }
565 }
566
567 static void
568 cpustats(void)
569 {
570 int state;
571 double time;
572
573 time = 0;
574 for (state = 0; state < CPUSTATES; ++state)
575 time += cur.cp_time[state];
576 if (!time)
577 time = 1.0;
578 /* States are generally never 100% and can use %3.0f. */
579 for (state = 0; state < CPUSTATES; ++state)
580 printf(" %2.0f", 100. * cur.cp_time[state] / time);
581 }
582
583 static void
584 usage(void)
585 {
586
587 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] "
588 "[-N system] [-w wait] [drives]\n");
589 exit(1);
590 }
591
592 static void
593 display(void)
594 {
595 double etime;
596
597 /* Sum up the elapsed ticks. */
598 etime = cur.cp_etime;
599
600 /*
601 * If we're showing totals only, then don't divide by the
602 * system time.
603 */
604 if (ISSET(todo, SHOW_TOTALS))
605 etime = 1.0;
606
607 if (ISSET(todo, SHOW_STATS_X)) {
608 disk_statsx(etime);
609 tape_statsx(etime);
610 goto out;
611 }
612
613 if (ISSET(todo, SHOW_TTY))
614 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
615
616 if (ISSET(todo, SHOW_STATS_1)) {
617 disk_stats(etime);
618 tape_stats(etime);
619 }
620
621
622 if (ISSET(todo, SHOW_STATS_2)) {
623 disk_stats2(etime);
624 tape_stats2(etime);
625 }
626
627
628 if (ISSET(todo, SHOW_CPU))
629 cpustats();
630
631 (void)printf("\n");
632
633 out:
634 (void)fflush(stdout);
635 }
636
637 static int
638 selectdrives(int argc, char *argv[])
639 {
640 int i, maxdrives, ndrives, tried;
641
642 /*
643 * Choose drives to be displayed. Priority goes to (in order) drives
644 * supplied as arguments and default drives. If everything isn't
645 * filled in and there are drives not taken care of, display the first
646 * few that fit.
647 *
648 * The backward compatibility #ifdefs permit the syntax:
649 * iostat [ drives ] [ interval [ count ] ]
650 */
651
652 #define BACKWARD_COMPATIBILITY
653 for (tried = ndrives = 0; *argv; ++argv) {
654 #ifdef BACKWARD_COMPATIBILITY
655 if (isdigit((unsigned char)**argv))
656 break;
657 #endif
658 tried++;
659 for (i = 0; i < dk_ndrive; i++) {
660 if (strcmp(cur.dk_name[i], *argv))
661 continue;
662 cur.dk_select[i] = 1;
663 ++ndrives;
664 }
665
666 for (i = 0; i < tp_ndrive; i++) {
667 if (strcmp(cur_tape.name[i], *argv))
668 continue;
669 cur_tape.select[i] = 1;
670 ++ndrives;
671 }
672 }
673
674 if (ndrives == 0 && tried == 0) {
675 /*
676 * Pick up to defdrives (or all if -x is given) drives
677 * if none specified.
678 */
679 maxdrives = (ISSET(todo, SHOW_STATS_X) ||
680 (dk_ndrive + tp_ndrive) < defdrives)
681 ? (dk_ndrive + tp_ndrive) : defdrives;
682 for (i = 0; i < maxdrives; i++) {
683 if (i >= dk_ndrive) {
684 cur_tape.select[i - dk_ndrive] = 1;
685 } else {
686 cur.dk_select[i] = 1;
687 }
688
689 ++ndrives;
690 if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
691 break;
692 }
693 }
694
695 #ifdef BACKWARD_COMPATIBILITY
696 if (*argv) {
697 interval = atoi(*argv);
698 if (*++argv)
699 reps = atoi(*argv);
700 }
701 #endif
702
703 if (interval) {
704 if (!reps)
705 reps = -1;
706 } else
707 if (reps)
708 interval = 1;
709
710 return (ndrives);
711 }
712