1 /* list.c: vinum interface program, list routines
4 * Copyright (c) 1997, 1998
5 * Nan Yang Computer Services Limited. All rights reserved.
7 * Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
9 * Written by Greg Lehey
11 * This software is distributed under the so-called ``Berkeley
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. Neither the name of the Company nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * This software is provided ``as is'', and any express or implied
27 * warranties, including, but not limited to, the implied warranties of
28 * merchantability and fitness for a particular purpose are disclaimed.
29 * In no event shall the company or contributors be liable for any
30 * direct, indirect, incidental, special, exemplary, or consequential
31 * damages (including, but not limited to, procurement of substitute
32 * goods or services; loss of use, data, or profits; or business
33 * interruption) however caused and on any theory of liability, whether
34 * in contract, strict liability, or tort (including negligence or
35 * otherwise) arising in any way out of the use of this software, even if
36 * advised of the possibility of such damage.
38 * $Id: list.c,v 1.25 2000/12/20 03:38:43 grog Exp grog $
39 * $FreeBSD: src/sbin/vinum/list.c,v 1.25.2.4 2001/05/28 05:58:04 grog Exp $
40 * $DragonFly: src/sbin/vinum/list.c,v 1.7 2007/01/20 19:20:39 dillon Exp $
43 #define _KERNEL_STRUCTURES
56 #include <sys/ioctl.h>
57 #include <sys/utsname.h>
58 #include <dev/raid/vinum/vinumhdr.h>
60 #include <dev/raid/vinum/request.h>
64 * When a subdisk is reviving or initializing, we
65 * check to see whether it is still progressing
66 * and print a warning if not. We check every 50
67 * ms, up to a maximum of 5 seconds. This is the
70 #define STALLCOUNT 100
73 * Take a size in sectors and return a pointer to
74 * a string which represents the size best. If lj
75 * is != 0, return left justified, otherwise in a
76 * fixed 10 character field suitable for columnar
79 * Note this uses a static string: it's only
80 * intended to be used immediately for printing.
83 roughlength(int64_t bytes
, int lj
)
85 static char description
[16];
87 if (bytes
> (int64_t) MEGABYTE
* 10000) /* gigabytes */
88 sprintf(description
, lj
? "%lld GB" : "%10lld GB", bytes
/ GIGABYTE
);
89 else if (bytes
> KILOBYTE
* 10000) /* megabytes */
90 sprintf(description
, lj
? "%lld MB" : "%10lld MB", bytes
/ MEGABYTE
);
91 else if (bytes
> 10000) /* kilobytes */
92 sprintf(description
, lj
? "%lld kB" : "%10lld kB", bytes
/ KILOBYTE
);
94 sprintf(description
, lj
? "%lld B" : "%10lld B", bytes
);
99 vinum_list(int argc
, char *argv
[], char *argv0
[])
103 enum objecttype type
;
105 if (sflag
& (!vflag
)) /* just summary stats, */
106 printf("Object\t\t Reads\t\tBytes\tAverage\tRecover\t Writes"
107 "\t\tBytes\tAverage\t Mblock Mstripe\n\n");
109 listconfig(); /* list everything */
111 for (i
= 0; i
< argc
; i
++) {
112 object
= find_object(argv
[i
], &type
); /* look for it */
113 if (vinum_li(object
, type
))
114 fprintf(stderr
, "Can't find object: %s\n", argv
[i
]);
121 vinum_li(int object
, enum objecttype type
)
125 vinum_ldi(object
, recurse
);
129 vinum_lsi(object
, recurse
);
133 vinum_lpi(object
, recurse
);
137 vinum_lvi(object
, recurse
);
147 vinum_ldi(int driveno
, int recurse
)
149 time_t t
; /* because Bruce says so */
150 int sdno
; /* for recursion */
152 get_drive_info(&drive
, driveno
);
153 if (drive
.state
!= drive_unallocated
) {
155 printf("Drive %s:\tDevice %s\n",
158 t
= drive
.label
.date_of_birth
.tv_sec
;
159 printf("\t\tCreated on %s at %s",
162 t
= drive
.label
.last_update
.tv_sec
;
163 printf("\t\tConfig last updated %s", /* care: \n at end */
165 printf("\t\tSize: %16lld bytes (%lld MB)\n\t\tUsed: %16lld bytes (%lld MB)\n"
166 "\t\tAvailable: %11qd bytes (%d MB)\n",
167 (long long) drive
.label
.drive_size
, /* bytes used */
168 (long long) (drive
.label
.drive_size
/ MEGABYTE
),
169 (long long) (drive
.label
.drive_size
- drive
.sectors_available
171 (long long) (drive
.label
.drive_size
- drive
.sectors_available
172 * DEV_BSIZE
) / MEGABYTE
,
173 (long long) drive
.sectors_available
* DEV_BSIZE
,
174 (int) (drive
.sectors_available
* DEV_BSIZE
/ MEGABYTE
));
175 printf("\t\tState: %s\n", drive_state(drive
.state
));
176 if (drive
.lasterror
!= 0)
177 printf("\t\tLast error: %s\n", strerror(drive
.lasterror
));
179 printf("\t\tLast error: none\n");
180 printf("\t\tActive requests:\t%d\n\t\tMaximum active:\t\t%d\n",
183 if (Verbose
) { /* print the free list */
184 int fe
; /* freelist entry */
186 struct drive_freelist freelist
;
187 struct ferq
{ /* request to pass to ioctl */
193 printf("\t\tFree list contains %d entries:\n\t\t Offset\t Size\n",
194 drive
.freelist_entries
);
195 for (fe
= 0; fe
< drive
.freelist_entries
; fe
++) {
196 freeunion
.ferq
.driveno
= drive
.driveno
;
197 freeunion
.ferq
.fe
= fe
;
198 if (ioctl(superdev
, VINUM_GETFREELIST
, &freeunion
.freelist
) < 0) {
200 "Can't get free list element %d: %s\n",
203 longjmp(command_fail
, -1);
205 printf("\t\t%9lld\t%9lld\n",
206 (long long) freeunion
.freelist
.offset
,
207 (long long) freeunion
.freelist
.sectors
);
211 printf("D %-21s State: %s\tDevice %s\tAvail: %lld/%lld MB",
213 drive_state(drive
.state
),
215 (long long) drive
.sectors_available
* DEV_BSIZE
/ MEGABYTE
,
216 (long long) (drive
.label
.drive_size
/ MEGABYTE
));
217 if (drive
.label
.drive_size
!= 0)
219 (int) ((drive
.sectors_available
* 100 * DEV_BSIZE
)
220 / (drive
.label
.drive_size
- (DATASTART
* DEV_BSIZE
))));
223 if (vflag
|| Verbose
) {
224 printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
225 (long long) drive
.reads
,
226 (long long) drive
.bytes_read
,
227 roughlength(drive
.bytes_read
, 1));
228 if (drive
.reads
!= 0)
229 printf("\t\tAverage read:\t%16lld bytes\n",
230 (long long) drive
.bytes_read
/ drive
.reads
);
231 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
232 (long long) drive
.writes
,
233 (long long) drive
.bytes_written
,
234 roughlength(drive
.bytes_written
, 1));
235 if (drive
.writes
!= 0)
236 printf("\t\tAverage write:\t%16lld bytes\n",
237 (long long) (drive
.bytes_written
/ drive
.writes
));
238 } else { /* non-verbose stats */
239 printf("%-15s\t%7lld\t%15lld\t",
241 (long long) drive
.reads
,
242 (long long) drive
.bytes_read
);
243 if (drive
.reads
!= 0)
245 (long long) (drive
.bytes_read
/ drive
.reads
));
248 printf("%7lld\t%15lld\t",
249 (long long) drive
.writes
,
250 (long long) drive
.bytes_written
);
251 if (drive
.writes
!= 0)
253 (long long) (drive
.bytes_written
/ drive
.writes
));
258 for (sdno
= 0; sdno
< vinum_conf
.subdisks_allocated
; sdno
++) {
259 get_sd_info(&sd
, sdno
);
260 if ((sd
.state
!= sd_unallocated
)
261 && (sd
.driveno
== drive
.driveno
))
262 vinum_lsi(sd
.sdno
, 0);
270 vinum_ld(int argc
, char *argv
[], char *argv0
[])
274 enum objecttype type
;
276 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
277 perror("Can't get vinum config");
281 for (driveno
= 0; driveno
< vinum_conf
.drives_allocated
; driveno
++)
282 vinum_ldi(driveno
, recurse
);
284 for (i
= 0; i
< argc
; i
++) {
285 driveno
= find_object(argv
[i
], &type
);
286 if (type
== drive_object
)
287 vinum_ldi(driveno
, recurse
);
289 fprintf(stderr
, "%s is not a drive\n", argv
[i
]);
295 vinum_lvi(int volno
, int recurse
)
297 get_volume_info(&vol
, volno
);
298 if (vol
.state
!= volume_unallocated
) {
300 printf("Volume %s:\tSize: %lld bytes (%lld MB)\n"
301 "\t\tState: %s\n\t\tFlags: %s%s%s\n",
303 ((long long) vol
.size
) * DEV_BSIZE
,
304 ((long long) vol
.size
) * DEV_BSIZE
/ MEGABYTE
,
305 volume_state(vol
.state
),
306 vol
.flags
& VF_OPEN
? "open " : "",
307 (vol
.flags
& VF_WRITETHROUGH
? "writethrough " : ""),
308 (vol
.flags
& VF_RAW
? "raw" : ""));
309 printf("\t\t%d plexes\n\t\tRead policy: ", vol
.plexes
);
310 if (vol
.preferred_plex
< 0) /* round robin */
311 printf("round robin\n");
313 get_plex_info(&plex
, vol
.plex
[vol
.preferred_plex
]);
314 printf("plex %d (%s)\n", vol
.preferred_plex
, plex
.name
);
316 } else if (!sflag
) /* brief */
317 printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
319 volume_state(vol
.state
),
321 roughlength(vol
.size
<< DEV_BSHIFT
, 0));
323 if (vflag
|| Verbose
) {
324 printf("\t\tReads: \t%16lld\n\t\tRecovered:\t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
325 (long long) vol
.reads
,
326 (long long) vol
.recovered_reads
,
327 (long long) vol
.bytes_read
,
328 roughlength(vol
.bytes_read
, 1));
330 printf("\t\tAverage read:\t%16lld bytes\n",
331 (long long) (vol
.bytes_read
/ vol
.reads
));
332 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
333 (long long) vol
.writes
,
334 (long long) vol
.bytes_written
,
335 roughlength(vol
.bytes_written
, 1));
337 printf("\t\tAverage write:\t%16lld bytes\n",
338 (long long) (vol
.bytes_written
/ vol
.writes
));
339 printf("\t\tActive requests:\t%8d\n", vol
.active
);
340 } else { /* brief stats listing */
341 printf("%-15s\t%7lld\t%15lld\t",
343 (long long) vol
.reads
,
344 (long long) vol
.bytes_read
);
347 (long long) (vol
.bytes_read
/ vol
.reads
));
350 printf("%7lld\t", (long long) vol
.recovered_reads
);
351 printf("%7lld\t%15lld\t",
352 (long long) vol
.writes
,
356 (long long) (vol
.bytes_written
/ vol
.writes
));
361 if (vol
.plexes
> 0) {
363 if (Verbose
) { /* brief list */
364 for (plexno
= 0; plexno
< vol
.plexes
; plexno
++) {
365 get_plex_info(&plex
, vol
.plex
[plexno
]);
366 /* Just a brief summary here */
367 printf("\t\tPlex %2d:\t%s\t(%s), %s\n",
370 plex_org(plex
.organization
),
371 roughlength(plex
.length
<< DEV_BSHIFT
, 0));
375 for (plexno
= 0; plexno
< vol
.plexes
; plexno
++)
376 vinum_lpi(vol
.plex
[plexno
], 0); /* first show the plexes */
377 for (plexno
= 0; plexno
< vol
.plexes
; plexno
++) { /* then the subdisks */
378 get_plex_info(&plex
, vol
.plex
[plexno
]);
379 if (plex
.subdisks
> 0) {
382 for (sdno
= 0; sdno
< plex
.subdisks
; sdno
++) {
383 get_plex_sd_info(&sd
, vol
.plex
[plexno
], sdno
);
384 vinum_lsi(sd
.sdno
, 0);
395 vinum_lv(int argc
, char *argv
[], char *argv0
[])
399 enum objecttype type
;
401 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
402 perror("Can't get vinum config");
406 for (volno
= 0; volno
< vinum_conf
.volumes_allocated
; volno
++)
407 vinum_lvi(volno
, recurse
);
409 for (i
= 0; i
< argc
; i
++) {
410 volno
= find_object(argv
[i
], &type
);
411 if (type
== volume_object
)
412 vinum_lvi(volno
, recurse
);
414 fprintf(stderr
, "%s is not a volume\n", argv
[i
]);
420 vinum_lpi(int plexno
, int recurse
)
422 get_plex_info(&plex
, plexno
);
423 if (plex
.state
!= plex_unallocated
) {
425 printf("Plex %s:\tSize:\t%9lld bytes (%lld MB)\n\t\tSubdisks: %8d\n",
427 (long long) plex
.length
* DEV_BSIZE
,
428 (long long) plex
.length
* DEV_BSIZE
/ MEGABYTE
,
430 printf("\t\tState: %s\n\t\tOrganization: %s",
431 plex_state(plex
.state
),
432 plex_org(plex
.organization
));
433 if (isstriped((&plex
)))
434 printf("\tStripe size: %s\n", roughlength(plex
.stripesize
* DEV_BSIZE
, 1));
437 if ((isparity((&plex
)))
438 && (plex
.checkblock
!= 0))
439 printf("\t\tCheck block pointer:\t\t%s (%d%%)\n",
440 roughlength((plex
.checkblock
<< DEV_BSHIFT
) * (plex
.subdisks
- 1), 0),
441 (int) (((u_int64_t
) (plex
.checkblock
* 100)) * (plex
.subdisks
- 1) / plex
.length
));
442 if (plex
.volno
>= 0) {
443 get_volume_info(&vol
, plex
.volno
);
444 printf("\t\tPart of volume %s\n", vol
.name
);
446 } else if (!sflag
) { /* non-verbose list */
447 char *org
= ""; /* organization */
449 switch (plex
.organization
) {
450 case plex_disorg
: /* disorganized */
453 case plex_concat
: /* concatenated plex */
456 case plex_striped
: /* striped plex */
459 case plex_raid4
: /* RAID4 plex */
462 case plex_raid5
: /* RAID5 plex */
466 printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s",
469 plex_state(plex
.state
),
471 roughlength(plex
.length
<< DEV_BSHIFT
, 0));
474 if (vflag
|| Verbose
) {
475 printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
476 (long long) plex
.reads
,
477 (long long) plex
.bytes_read
,
478 roughlength(plex
.bytes_read
, 1));
480 printf("\t\tAverage read:\t%16lld bytes\n",
481 (long long) (plex
.bytes_read
/ plex
.reads
));
482 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
483 (long long) plex
.writes
,
484 (long long) plex
.bytes_written
,
485 roughlength(plex
.bytes_written
, 1));
486 if (plex
.writes
!= 0)
487 printf("\t\tAverage write:\t%16lld bytes\n",
488 (long long) (plex
.bytes_written
/ plex
.writes
));
489 if (((plex
.reads
+ plex
.writes
) > 0)
490 && isstriped((&plex
)))
491 printf("\t\tMultiblock:\t%16lld (%d%%)\n"
492 "\t\tMultistripe:\t%16lld (%d%%)\n",
493 (long long) plex
.multiblock
,
494 (int) (plex
.multiblock
* 100 / (plex
.reads
+ plex
.writes
)),
495 (long long) plex
.multistripe
,
496 (int) (plex
.multistripe
* 100 / (plex
.reads
+ plex
.writes
)));
497 if (plex
.recovered_reads
)
498 printf("\t\tRecovered reads:%16lld\n",
499 (long long) plex
.recovered_reads
);
500 if (plex
.degraded_writes
)
501 printf("\t\tDegraded writes:%16lld\n",
502 (long long) plex
.degraded_writes
);
503 if (plex
.parityless_writes
)
504 printf("\t\tParityless writes:%14lld\n",
505 (long long) plex
.parityless_writes
);
507 printf("%-15s\t%7lld\t%15lld\t",
509 (long long) plex
.reads
,
510 (long long) plex
.bytes_read
);
513 (long long) (plex
.bytes_read
/ plex
.reads
));
516 printf("%7lld\t", (long long) plex
.recovered_reads
);
517 printf("%7lld\t%15lld\t",
518 (long long) plex
.writes
,
519 (long long) plex
.bytes_written
);
520 if (plex
.writes
!= 0)
522 (long long) (plex
.bytes_written
/ plex
.writes
));
525 printf("%7lld\t%7lld\n",
526 (long long) plex
.multiblock
,
527 (long long) plex
.multistripe
);
530 if (plex
.subdisks
> 0) {
535 for (sdno
= 0; sdno
< plex
.subdisks
; sdno
++) {
536 get_plex_sd_info(&sd
, plexno
, sdno
);
537 printf("\t\tSubdisk %d:\t%s\n\t\t state: %s\tsize %11lld (%lld MB)\n",
541 (long long) sd
.sectors
* DEV_BSIZE
,
542 (long long) sd
.sectors
* DEV_BSIZE
/ MEGABYTE
);
543 if (plex
.organization
== plex_concat
)
544 printf("\t\t\toffset %9ld (0x%lx)\n",
545 (long) sd
.plexoffset
,
546 (long) sd
.plexoffset
);
551 for (sdno
= 0; sdno
< plex
.subdisks
; sdno
++) {
552 get_plex_sd_info(&sd
, plexno
, sdno
);
553 vinum_lsi(sd
.sdno
, 0);
562 vinum_lp(int argc
, char *argv
[], char *argv0
[])
566 enum objecttype type
;
568 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
569 perror("Can't get vinum config");
573 for (plexno
= 0; plexno
< vinum_conf
.plexes_allocated
; plexno
++)
574 vinum_lpi(plexno
, recurse
);
576 for (i
= 0; i
< argc
; i
++) {
577 plexno
= find_object(argv
[i
], &type
);
578 if (type
== plex_object
)
579 vinum_lpi(plexno
, recurse
);
581 fprintf(stderr
, "%s is not a plex\n", argv
[i
]);
587 vinum_lsi(int sdno
, int recurse
)
589 long long revived
; /* keep an eye on revive progress */
592 get_sd_info(&sd
, sdno
);
593 if (sd
.state
!= sd_unallocated
) {
595 printf("Subdisk %s:\n\t\tSize: %16lld bytes (%lld MB)\n\t\tState: %s\n",
597 (long long) sd
.sectors
* DEV_BSIZE
,
598 (long long) sd
.sectors
/ (MEGABYTE
/ DEV_BSIZE
),
600 if (sd
.flags
& VF_RETRYERRORS
)
601 printf("\t\tretryerrors\n");
602 if (sd
.plexno
>= 0) {
603 get_plex_info(&plex
, sd
.plexno
);
604 printf("\t\tPlex %s", plex
.name
);
605 printf(" at offset %lld (%s)\n",
606 (long long) sd
.plexoffset
* DEV_BSIZE
,
607 roughlength((long long) sd
.plexoffset
* DEV_BSIZE
, 1));
609 if (sd
.state
== sd_reviving
) {
611 printf("\t\t*** Start subdisk with 'start' command ***\n");
613 printf("\t\tReviver PID:\t%d\n", sd
.reviver
);
614 if (kill(sd
.reviver
, 0) == -1) {
615 if (errno
== ESRCH
) /* no process */
616 printf("\t\t*** Revive process has died ***\n");
617 /* Don't report a problem that "can't happen" */
619 revived
= sd
.revived
; /* note how far we were */
622 * Wait for up to a second until we
623 * see some progress with the revive.
624 * Do it like this so we don't have
625 * annoying delays in the listing.
627 for (times
= 0; times
< STALLCOUNT
; times
++) {
628 get_sd_info(&sd
, sdno
);
629 if (sd
.revived
!= revived
) /* progress? */
633 if (times
== STALLCOUNT
)
634 printf("\t\t*** Revive has stalled ***\n");
637 printf("\t\tRevive pointer:\t\t%s (%d%%)\n",
638 roughlength(sd
.revived
<< DEV_BSHIFT
, 0),
639 (int) (((u_int64_t
) (sd
.revived
* 100)) / sd
.sectors
));
640 printf("\t\tRevive blocksize:\t%s\n"
641 "\t\tRevive interval:\t%10d seconds\n",
642 roughlength(sd
.revive_blocksize
, 0),
645 if (sd
.state
== sd_initializing
) {
646 printf("\t\tInitialize pointer:\t%s (%d%%)\n",
647 roughlength(sd
.initialized
<< DEV_BSHIFT
, 0),
648 (int) (((u_int64_t
) (sd
.initialized
* 100)) / sd
.sectors
));
649 printf("\t\tInitialize blocksize:\t%s\n"
650 "\t\tInitialize interval:\t%10d seconds\n",
651 roughlength(sd
.init_blocksize
, 0),
654 get_drive_info(&drive
, sd
.driveno
);
655 if (sd
.driveoffset
< 0)
656 printf("\t\tDrive %s (%s), no offset\n",
659 else if (drive
.devicename
[0] != '\0') /* has a name */
660 printf("\t\tDrive %s (%s) at offset %lld (%s)\n",
663 (long long) (sd
.driveoffset
* DEV_BSIZE
),
664 roughlength(sd
.driveoffset
* DEV_BSIZE
, 1));
666 printf("\t\tDrive %s (*missing*) at offset %lld (%s)\n",
668 (long long) (sd
.driveoffset
* DEV_BSIZE
),
669 roughlength(sd
.driveoffset
* DEV_BSIZE
, 1));
670 } else if (!sflag
) { /* brief listing, no stats */
671 if (sd
.state
== sd_reviving
)
672 printf("S %-21s State: R %d%%\t",
674 (int) (((u_int64_t
) (sd
.revived
* 100)) / sd
.sectors
));
675 else if (sd
.state
== sd_initializing
)
676 printf("S %-21s State: I %d%%\t",
678 (int) (((u_int64_t
) (sd
.initialized
* 100)) / sd
.sectors
));
680 printf("S %-21s State: %s\t",
684 printf("(detached)\t");
687 &(roughlength(sd
.plexoffset
<< DEV_BSHIFT
, 0))[2]); /* what a kludge! */
689 roughlength(sd
.sectors
<< DEV_BSHIFT
, 0));
690 if (sd
.state
== sd_reviving
) {
692 printf("\t\t\t*** Start %s with 'start' command ***\n",
694 else if (kill(sd
.reviver
, 0) == -1) {
695 if (errno
== ESRCH
) /* no process */
696 printf("\t\t\t*** Revive process for %s has died ***\n",
698 /* Don't report a problem that "can't happen" */
700 revived
= sd
.revived
; /* note how far we were */
703 * Wait for up to a second until we
704 * see some progress with the revive.
705 * Do it like this so we don't have
706 * annoying delays in the listing.
708 for (times
= 0; times
< STALLCOUNT
; times
++) {
709 get_sd_info(&sd
, sdno
);
710 if (sd
.revived
!= revived
) /* progress? */
714 if (times
== STALLCOUNT
)
715 printf("\t\t\t*** Revive of %s has stalled ***\n",
721 if (vflag
|| Verbose
) {
722 printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
723 (long long) sd
.reads
,
724 (long long) sd
.bytes_read
,
725 roughlength(sd
.bytes_read
, 1));
727 printf("\t\tAverage read:\t%16lld bytes\n",
728 (long long) (sd
.bytes_read
/ sd
.reads
));
729 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
730 (long long) sd
.writes
,
731 (long long) sd
.bytes_written
,
732 roughlength(sd
.bytes_written
, 1));
734 printf("\t\tAverage write:\t%16lld bytes\n",
735 (long long) (sd
.bytes_written
/ sd
.writes
));
737 printf("%-15s\t%7lld\t%15lld\t",
739 (long long) sd
.reads
,
740 (long long) sd
.bytes_read
);
743 (long long) (sd
.bytes_read
/ sd
.reads
));
746 printf("%7lld\t%15lld\t",
747 (long long) sd
.writes
,
748 (long long) sd
.bytes_written
);
751 (long long) (sd
.bytes_written
/ sd
.writes
));
757 vinum_ldi(sd
.driveno
, 0);
759 printf("\n"); /* make it more readable */
764 vinum_ls(int argc
, char *argv
[], char *argv0
[])
769 /* Structures to read kernel data into */
770 struct _vinum_conf vinum_conf
;
771 enum objecttype type
;
773 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
774 perror("Can't get vinum config");
778 for (sdno
= 0; sdno
< vinum_conf
.subdisks_allocated
; sdno
++)
779 vinum_lsi(sdno
, recurse
);
780 } else { /* specific subdisks */
781 for (i
= 0; i
< argc
; i
++) {
782 sdno
= find_object(argv
[i
], &type
);
783 if (type
== sd_object
)
784 vinum_lsi(sdno
, recurse
);
786 fprintf(stderr
, "%s is not a subdisk\n", argv
[i
]);
792 /* List the complete configuration.
794 * XXX Change this to specific lists */
798 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
799 perror("Can't get vinum config");
802 printf("%d drives:\n", vinum_conf
.drives_used
);
803 if (vinum_conf
.drives_used
> 0) {
804 vinum_ld(0, NULL
, NULL
);
807 printf("%d volumes:\n", vinum_conf
.volumes_used
);
808 if (vinum_conf
.volumes_used
> 0) {
809 vinum_lv(0, NULL
, NULL
);
812 printf("%d plexes:\n", vinum_conf
.plexes_used
);
813 if (vinum_conf
.plexes_used
> 0) {
814 vinum_lp(0, NULL
, NULL
);
817 printf("%d subdisks:\n", vinum_conf
.subdisks_used
);
818 if (vinum_conf
.subdisks_used
> 0)
819 vinum_ls(0, NULL
, NULL
);
822 /* Convert a timeval to Tue Oct 13 13:54:14.0434324
823 * Return pointer to text */
825 timetext(struct timeval
*time
)
827 static char text
[30];
828 time_t t
; /* to keep Bruce happy */
831 strcpy(text
, ctime(&t
)); /* to the second */
832 sprintf(&text
[19], ".%06ld", time
->tv_usec
); /* and the microseconds */
837 vinum_info(int argc
, char *argv
[], char *argv0
[])
839 struct meminfo meminfo
;
846 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
847 perror("Can't get vinum config");
850 printf("Flags: 0x%x\n", vinum_conf
.flags
);
851 if (ioctl(superdev
, VINUM_MEMINFO
, &meminfo
) < 0) {
852 perror("Can't get information");
855 printf("Total of %d blocks malloced, total memory: %d\nMaximum allocs: %8d, malloc table at 0x%08x\n",
857 meminfo
.total_malloced
,
859 (int) meminfo
.malloced
);
861 printf("%d requests active, maximum %d active\n",
863 vinum_conf
.maxactive
);
864 if (vflag
&& (!Verbose
))
865 for (i
= 0; i
< meminfo
.mallocs
; i
++) {
867 if (ioctl(superdev
, VINUM_MALLOCINFO
, &malloced
) < 0) {
868 perror("Can't get information");
872 printf("Block\tSequence\t size\t address\t line\t\tfile\n\n");
873 printf("%6d\t%6d\t\t%6d\t0x%08x\t%6d\t\t%s\n",
877 (int) malloced
.address
,
879 (char *) &malloced
.file
);
883 printf("\nTime\t\t Event\t Buf\tDev\t Offset\tBytes\tSD\tSDoff\tDoffset\tGoffset\n\n");
884 for (i
= RQINFO_SIZE
- 1; i
>= 0; i
--) { /* go through the request list in order */
886 if (ioctl(superdev
, VINUM_RQINFO
, &rq
) < 0) {
887 perror("Can't get information");
890 /* Compress devminor into something printable. */
891 rq
.devminor
= (rq
.devminor
& 0xff)
892 | ((rq
.devminor
& 0xfff0000) >> 8);
894 case loginfo_unused
: /* never been used */
897 case loginfo_user_bp
: /* this is the bp when strategy is called */
898 printf("%s %dVS %s %p\t%d.%-6d 0x%-9x\t%ld\n",
899 timetext(&rq
.timestamp
),
901 rq
.info
.b
.b_flags
& B_READ
? "Read " : "Write",
909 case loginfo_sdiol
: /* subdisk I/O launch */
910 case loginfo_user_bpl
: /* and this is the bp at launch time */
911 printf("%s %dLR %s %p\t%d.%-6d 0x%-9x\t%ld\n",
912 timetext(&rq
.timestamp
),
914 rq
.info
.b
.b_flags
& B_READ
? "Read " : "Write",
922 case loginfo_rqe
: /* user RQE */
923 printf("%s 3RQ %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
924 timetext(&rq
.timestamp
),
925 rq
.info
.rqe
.b
.b_flags
& B_READ
? "Read " : "Write",
929 rq
.info
.rqe
.b
.b_blkno
,
930 rq
.info
.rqe
.b
.b_bcount
,
932 rq
.info
.rqe
.sdoffset
,
933 rq
.info
.rqe
.dataoffset
,
934 rq
.info
.rqe
.groupoffset
);
937 case loginfo_iodone
: /* iodone called */
938 printf("%s 4DN %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
939 timetext(&rq
.timestamp
),
940 rq
.info
.rqe
.b
.b_flags
& B_READ
? "Read " : "Write",
944 rq
.info
.rqe
.b
.b_blkno
,
945 rq
.info
.rqe
.b
.b_bcount
,
947 rq
.info
.rqe
.sdoffset
,
948 rq
.info
.rqe
.dataoffset
,
949 rq
.info
.rqe
.groupoffset
);
952 case loginfo_raid5_data
: /* RAID-5 write data block */
953 printf("%s 5RD %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
954 timetext(&rq
.timestamp
),
955 rq
.info
.rqe
.b
.b_flags
& B_READ
? "Read " : "Write",
959 rq
.info
.rqe
.b
.b_blkno
,
960 rq
.info
.rqe
.b
.b_bcount
,
962 rq
.info
.rqe
.sdoffset
,
963 rq
.info
.rqe
.dataoffset
,
964 rq
.info
.rqe
.groupoffset
);
967 case loginfo_raid5_parity
: /* RAID-5 write parity block */
968 printf("%s 6RP %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
969 timetext(&rq
.timestamp
),
970 rq
.info
.rqe
.b
.b_flags
& B_READ
? "Read " : "Write",
974 rq
.info
.rqe
.b
.b_blkno
,
975 rq
.info
.rqe
.b
.b_bcount
,
977 rq
.info
.rqe
.sdoffset
,
978 rq
.info
.rqe
.dataoffset
,
979 rq
.info
.rqe
.groupoffset
);
982 case loginfo_sdio
: /* subdisk I/O */
983 printf("%s %dVS %s %p\t\t 0x%-9x\t%ld\t%d\n",
984 timetext(&rq
.timestamp
),
986 rq
.info
.b
.b_flags
& B_READ
? "Read " : "Write",
993 case loginfo_sdiodone
: /* subdisk I/O done */
994 printf("%s %dSD %s %p\t\t 0x%-9x\t%ld\t%d\n",
995 timetext(&rq
.timestamp
),
997 rq
.info
.b
.b_flags
& B_READ
? "Read " : "Write",
1004 case loginfo_lockwait
:
1005 printf("%s Lockwait %p\t 0x%x\n",
1006 timetext(&rq
.timestamp
),
1008 rq
.info
.lockinfo
.stripe
);
1012 printf("%s Lock %p\t 0x%x\n",
1013 timetext(&rq
.timestamp
),
1015 rq
.info
.lockinfo
.stripe
);
1018 case loginfo_unlock
:
1019 printf("%s Unlock\t %p\t 0x%x\n",
1020 timetext(&rq
.timestamp
),
1022 rq
.info
.lockinfo
.stripe
);
1031 * Print config file to a file. This is a userland version
1032 * of kernel format_config
1035 vinum_printconfig(int argc
, char *argv
[], char *argv0
[])
1040 fprintf(stderr
, "Usage: \tprintconfig [<outfile>]\n");
1042 } else if (argc
== 1)
1043 of
= fopen(argv
[0], "w");
1047 fprintf(stderr
, "Can't open %s: %s\n", argv
[0], strerror(errno
));
1050 printconfig(of
, "");
1056 * The guts of printconfig. This is called from
1057 * vinum_printconfig and from vinum_create when
1058 * called without an argument, in order to give
1059 * the user something to edit.
1062 printconfig(FILE * of
, char *comment
)
1064 struct utsname uname_s
;
1072 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
1073 perror("Can't get vinum config");
1076 uname(&uname_s
); /* get our system name */
1077 time(&now
); /* and the current time */
1079 "# Vinum configuration of %s, saved at %s",
1081 ctime(&now
)); /* say who did it */
1083 if (comment
[0] != 0) /* abuse this for commented version */
1084 fprintf(of
, "# Current configuration:\n");
1085 for (i
= 0; i
< vinum_conf
.drives_allocated
; i
++) {
1086 get_drive_info(&drive
, i
);
1087 if (drive
.state
!= drive_unallocated
) {
1089 "%sdrive %s device %s\n",
1096 for (i
= 0; i
< vinum_conf
.volumes_allocated
; i
++) {
1097 get_volume_info(&vol
, i
);
1098 if (vol
.state
!= volume_unallocated
) {
1099 if (vol
.preferred_plex
>= 0) /* preferences, */
1101 "%svolume %s readpol prefer %s\n",
1104 vinum_conf
.plex
[vol
.preferred_plex
].name
);
1105 else /* default round-robin */
1106 fprintf(of
, "%svolume %s\n", comment
, vol
.name
);
1110 /* Then the plex configuration */
1111 for (i
= 0; i
< vinum_conf
.plexes_allocated
; i
++) {
1112 get_plex_info(&plex
, i
);
1113 if (plex
.state
!= plex_unallocated
) {
1114 fprintf(of
, "%splex name %s org %s ",
1117 plex_org(plex
.organization
));
1118 if (isstriped((&plex
)))
1119 fprintf(of
, "%ds ", (int) plex
.stripesize
);
1120 if (plex
.volno
>= 0) { /* we have a volume */
1121 get_volume_info(&vol
, plex
.volno
);
1122 fprintf(of
, "vol %s ", vol
.name
);
1124 fprintf(of
, "detached ");
1129 /* And finally the subdisk configuration */
1130 for (i
= 0; i
< vinum_conf
.subdisks_allocated
; i
++) {
1131 get_sd_info(&sd
, i
);
1132 if (sd
.state
!= sd_unallocated
) {
1133 get_drive_info(&drive
, sd
.driveno
);
1134 if (sd
.plexno
>= 0) {
1135 get_plex_info(&plex
, sd
.plexno
);
1137 "%ssd name %s drive %s plex %s len %llds driveoffset %llds plexoffset %llds\n",
1142 (long long) sd
.sectors
,
1143 (long long) sd
.driveoffset
,
1144 (long long) sd
.plexoffset
);
1147 "%ssd name %s drive %s detached len %llds driveoffset %llds\n",
1151 (long long) sd
.sectors
,
1152 (long long) sd
.driveoffset
);
1158 list_defective_objects(void)
1161 int heading_needed
= 1;
1163 if (ioctl(superdev
, VINUM_GETCONFIG
, &vinum_conf
) < 0) {
1164 perror("Can't get vinum config");
1167 for (o
= 0; o
< vinum_conf
.drives_allocated
; o
++) {
1168 get_drive_info(&drive
, o
);
1169 if ((drive
.state
!= drive_unallocated
) /* drive exists */
1170 &&(drive
.state
!= drive_up
)) { /* but it's not up */
1171 if (heading_needed
) {
1172 printf("Warning: defective objects\n\n");
1175 vinum_ldi(o
, 0); /* print info */
1179 for (o
= 0; o
< vinum_conf
.volumes_allocated
; o
++) {
1180 get_volume_info(&vol
, o
);
1181 if ((vol
.state
!= volume_unallocated
) /* volume exists */
1182 &&(vol
.state
!= volume_up
)) { /* but it's not up */
1183 if (heading_needed
) {
1184 printf("Warning: defective objects\n\n");
1187 vinum_lvi(o
, 0); /* print info */
1191 for (o
= 0; o
< vinum_conf
.plexes_allocated
; o
++) {
1192 get_plex_info(&plex
, o
);
1193 if ((plex
.state
!= plex_unallocated
) /* plex exists */
1194 &&(plex
.state
!= plex_up
)) { /* but it's not up */
1195 if (heading_needed
) {
1196 printf("Warning: defective objects\n\n");
1199 vinum_lpi(o
, 0); /* print info */
1203 for (o
= 0; o
< vinum_conf
.subdisks_allocated
; o
++) {
1204 get_sd_info(&sd
, o
);
1205 if ((sd
.state
!= sd_unallocated
) /* sd exists */
1206 &&(sd
.state
!= sd_up
)) { /* but it's not up */
1207 if (heading_needed
) {
1208 printf("Warning: defective objects\n\n");
1211 vinum_lsi(o
, 0); /* print info */
1216 /* Dump config from specified disk drives */
1218 vinum_dumpconfig(int argc
, char *argv
[], char *argv0
[])
1222 if (argc
== 0) { /* start everything */
1223 int devs
= getnumdevs();
1224 struct statinfo statinfo
;
1226 char *enamelist
; /* end of name list */
1228 char **token
; /* list of tokens */
1229 int tokens
; /* and their number */
1231 bzero(&statinfo
, sizeof(struct statinfo
));
1232 statinfo
.dinfo
= malloc(devs
* sizeof(struct statinfo
));
1233 namelist
= malloc(devs
* (DEVSTAT_NAME_LEN
+ 8));
1234 token
= malloc((devs
+ 1) * sizeof(char *));
1235 if ((statinfo
.dinfo
== NULL
) || (namelist
== NULL
) || (token
== NULL
)) {
1236 fprintf(stderr
, "Can't allocate memory for drive list\n");
1239 bzero(statinfo
.dinfo
, sizeof(struct devinfo
));
1241 tokens
= 0; /* no tokens yet */
1242 if (getdevs(&statinfo
) < 0) { /* find out what devices we have */
1243 perror("Can't get device list");
1246 namelist
[0] = '\0'; /* start with empty namelist */
1247 enamelist
= namelist
; /* point to the end of the list */
1249 for (i
= 0; i
< devs
; i
++) {
1250 struct devstat
*stat
= &statinfo
.dinfo
->devices
[i
];
1252 if (((stat
->device_type
& DEVSTAT_TYPE_MASK
) == DEVSTAT_TYPE_DIRECT
) /* disk device */
1253 &&((stat
->device_type
& DEVSTAT_TYPE_PASS
) == 0) /* and not passthrough */
1254 &&((stat
->device_name
[0] != '\0'))) { /* and it has a name */
1255 sprintf(enamelist
, "/dev/%s%d", stat
->device_name
, stat
->unit_number
);
1256 token
[tokens
] = enamelist
; /* point to it */
1257 tokens
++; /* one more token */
1258 enamelist
= &enamelist
[strlen(enamelist
) + 1]; /* and start beyond the end */
1261 free(statinfo
.dinfo
); /* don't need the list any more */
1262 for (i
= 0; i
< tokens
; i
++)
1263 dumpconfig(token
[i
]);
1266 } else { /* list specified drives */
1267 for (i
= 0; i
< argc
; i
++)
1268 dumpconfig(argv
[i
]);
1274 dumpconfig(char *part
)
1276 char partname
[MAXPATHLEN
];
1278 char partition
; /* UNIX partition */
1280 int founddrive
; /* flag when we find a vinum drive */
1281 struct disklabel label
; /* label of this drive */
1282 int driveno
; /* fd of drive */
1284 u_int64_t drivelength
;
1286 if (memcmp(part
, "/dev/", DEVLEN
) == 0) /* starts with /dev */
1287 memcpy(partname
, part
, MAXPATHLEN
);
1288 else { /* prepend */
1289 strcpy(partname
, "/dev/");
1290 strncat(&partname
[DEVLEN
], part
, MAXPATHLEN
- DEVLEN
);
1292 partid
= &partname
[strlen(partname
)];
1293 founddrive
= 0; /* no vinum drive found yet on this spindle */
1294 /* first try the partition table */
1295 for (slice
= 1; slice
< 5; slice
++) {
1296 sprintf(partid
, "s%dc", slice
); /* c partition */
1297 driveno
= open(partname
, O_RDONLY
);
1299 if (errno
!= ENOENT
)
1300 fprintf(stderr
, "Can't open %s: %s (%d)\n", partname
, strerror(errno
), errno
);
1303 if (ioctl(driveno
, DIOCGDINFO
, &label
) < 0) {
1304 fprintf(stderr
, "Can't get label from %s: %s (%d)\n", partname
, strerror(errno
), errno
);
1307 for (partition
= 'a'; partition
< 'i'; partition
++) {
1308 if ((partition
!= 'c') /* it's not the c partition */
1309 &&((label
.d_partitions
[partition
- 'a'].p_fstype
== FS_VINUM
) /* and it's a Vinum partition */
1310 ||Verbose
)) { /* or we're just plain curious */
1311 sprintf(partid
, "s%d%c", slice
, partition
);
1312 found
= check_drive(partname
); /* try to open it */
1313 founddrive
|= found
; /* and note if we were successful at all */
1314 if (label
.d_partitions
[partition
- 'a'].p_fstype
== FS_VINUM
) { /* it's a Vinum partition */
1315 drivelength
= ((u_int64_t
) label
.d_partitions
[partition
- 'a'].p_size
) * DEV_BSIZE
;
1316 printf("Drive %s: %s (%lld bytes)\n",
1318 roughlength(drivelength
, 1),
1320 if ((!found
) && vflag
) /* we're talkative */
1321 printf("*** no configuration found ***\n");
1326 if (founddrive
== 0) { /* didn't find anything, */
1327 sprintf(partid
, "c"); /* c partition */
1328 driveno
= open(partname
, O_RDONLY
);
1330 if (errno
!= ENOENT
)
1331 fprintf(stderr
, "Can't open %s: %s (%d)\n", partname
, strerror(errno
), errno
);
1334 if (ioctl(driveno
, DIOCGDINFO
, &label
) < 0) {
1335 fprintf(stderr
, "Can't get label from %s: %s (%d)\n", partname
, strerror(errno
), errno
);
1338 for (partition
= 'a'; partition
< 'i'; partition
++) { /* try the compatibility partition */
1339 if ((partition
!= 'c') /* it's not the c partition */
1340 &&((label
.d_partitions
[partition
- 'a'].p_fstype
== FS_VINUM
) /* and it's a Vinum partition */
1341 ||Verbose
)) { /* or we're just plain curious */
1342 sprintf(partid
, "%c", partition
);
1343 found
= check_drive(partname
); /* try to open it */
1344 founddrive
|= found
; /* and note if we were successful at all */
1345 if (label
.d_partitions
[partition
- 'a'].p_fstype
== FS_VINUM
) { /* it's a Vinum partition */
1346 drivelength
= ((u_int64_t
) label
.d_partitions
[partition
- 'a'].p_size
) * DEV_BSIZE
;
1347 printf("Drive %s: %s (%lld bytes)\n",
1349 roughlength(drivelength
, 1),
1351 if ((!found
) && vflag
) /* we're talkative */
1352 printf("*** no configuration found ***\n");
1360 * Check a drive for a Vinum header. If found,
1361 * print configuration information from the drive.
1363 * Return 1 if Vinum config found.
1366 check_drive(char *devicename
)
1369 char vinumlabel
[DEV_BSIZE
]; /* one sector for label */
1370 struct vinum_hdr
*hdr
= (struct vinum_hdr
*) vinumlabel
; /* with this structure */
1371 char *config_text
; /* read the config info from disk into here */
1374 fd
= open(devicename
, O_RDONLY
);
1376 if (lseek(fd
, VINUM_LABEL_OFFSET
, SEEK_SET
) < 0) {
1378 "Can't seek label for %s: %s (%d)\n",
1385 if (read(fd
, vinumlabel
, DEV_BSIZE
) != DEV_BSIZE
) {
1386 if (errno
!= EINVAL
)
1388 "Can't read label from %s: %s (%d)\n",
1395 if ((hdr
->magic
== VINUM_MAGIC
)
1396 || (vflag
&& (hdr
->magic
== VINUM_NOMAGIC
))) {
1397 printf("Drive %s:\tDevice %s\n",
1400 if (hdr
->magic
== VINUM_NOMAGIC
)
1401 printf("*** Drive has been obliterated ***\n");
1402 t
= hdr
->label
.date_of_birth
.tv_sec
;
1403 printf("\t\tCreated on %s at %s",
1406 t
= hdr
->label
.last_update
.tv_sec
;
1407 printf("\t\tConfig last updated %s", /* care: \n at end */
1409 printf("\t\tSize: %16lld bytes (%lld MB)\n",
1410 (long long) hdr
->label
.drive_size
, /* bytes used */
1411 (long long) (hdr
->label
.drive_size
/ MEGABYTE
));
1412 config_text
= (char *) malloc(MAXCONFIG
);
1413 if (config_text
== NULL
)
1414 fprintf(stderr
, "Can't allocate memory\n");
1416 if (read(fd
, config_text
, MAXCONFIG
) != MAXCONFIG
)
1418 "Can't read config from %s: %s (%d)\n",
1433 /* Local Variables: */
1434 /* fill-column: 50 */