1139778Simp/*- 2190507Slulf * Copyright (c) 2004, 2007 Lukas Ertl 3130389Sle * All rights reserved. 4130389Sle * 5130389Sle * Redistribution and use in source and binary forms, with or without 6130389Sle * modification, are permitted provided that the following conditions 7130389Sle * are met: 8130389Sle * 1. Redistributions of source code must retain the above copyright 9130389Sle * notice, this list of conditions and the following disclaimer. 10130389Sle * 2. Redistributions in binary form must reproduce the above copyright 11130389Sle * notice, this list of conditions and the following disclaimer in the 12130389Sle * documentation and/or other materials provided with the distribution. 13130389Sle * 14130389Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130389Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16130389Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17130389Sle * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18130389Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19130389Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20130389Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21130389Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22130389Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23130389Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24130389Sle * SUCH DAMAGE. 25130389Sle * 26130389Sle */ 27130389Sle 28130389Sle#include <sys/cdefs.h> 29130389Sle__FBSDID("$FreeBSD$"); 30130389Sle 31223921Sae#include <sys/types.h> 32130389Sle#include <sys/libkern.h> 33130389Sle#include <sys/malloc.h> 34223921Sae#include <sys/sbuf.h> 35130389Sle 36130389Sle#include <geom/geom.h> 37130389Sle#include <geom/vinum/geom_vinum_var.h> 38130389Sle#include <geom/vinum/geom_vinum.h> 39130389Sle#include <geom/vinum/geom_vinum_share.h> 40130389Sle 41130389Slevoid gv_lvi(struct gv_volume *, struct sbuf *, int); 42130389Slevoid gv_lpi(struct gv_plex *, struct sbuf *, int); 43130389Slevoid gv_lsi(struct gv_sd *, struct sbuf *, int); 44130389Slevoid gv_ldi(struct gv_drive *, struct sbuf *, int); 45130389Sle 46130389Slevoid 47130389Slegv_list(struct g_geom *gp, struct gctl_req *req) 48130389Sle{ 49130389Sle struct gv_softc *sc; 50130389Sle struct gv_drive *d; 51130389Sle struct gv_plex *p; 52130389Sle struct gv_sd *s; 53130389Sle struct gv_volume *v; 54130389Sle struct sbuf *sb; 55130389Sle int *argc, i, *flags, type; 56130389Sle char *arg, buf[20], *cmd; 57130389Sle 58130389Sle argc = gctl_get_paraml(req, "argc", sizeof(*argc)); 59130389Sle 60130389Sle if (argc == NULL) { 61130389Sle gctl_error(req, "no arguments given"); 62130389Sle return; 63130389Sle } 64130389Sle 65130389Sle flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 66185309Slulf if (flags == NULL) { 67185309Slulf gctl_error(req, "no flags given"); 68185309Slulf return; 69185309Slulf } 70130389Sle 71130389Sle sc = gp->softc; 72130389Sle 73130389Sle sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 74130389Sle 75130389Sle /* Figure out which command was given. */ 76130389Sle cmd = gctl_get_param(req, "cmd", NULL); 77185309Slulf if (cmd == NULL) { 78185309Slulf gctl_error(req, "no command given"); 79185309Slulf return; 80185309Slulf } 81130389Sle 82130389Sle /* List specific objects or everything. */ 83130389Sle if (!strcmp(cmd, "list") || !strcmp(cmd, "l")) { 84130389Sle if (*argc) { 85130389Sle for (i = 0; i < *argc; i++) { 86130389Sle snprintf(buf, sizeof(buf), "argv%d", i); 87130389Sle arg = gctl_get_param(req, buf, NULL); 88130389Sle if (arg == NULL) 89130389Sle continue; 90130389Sle type = gv_object_type(sc, arg); 91130389Sle switch (type) { 92130389Sle case GV_TYPE_VOL: 93130389Sle v = gv_find_vol(sc, arg); 94130389Sle gv_lvi(v, sb, *flags); 95130389Sle break; 96130389Sle case GV_TYPE_PLEX: 97130389Sle p = gv_find_plex(sc, arg); 98130389Sle gv_lpi(p, sb, *flags); 99130389Sle break; 100130389Sle case GV_TYPE_SD: 101130389Sle s = gv_find_sd(sc, arg); 102130389Sle gv_lsi(s, sb, *flags); 103130389Sle break; 104130389Sle case GV_TYPE_DRIVE: 105130389Sle d = gv_find_drive(sc, arg); 106130389Sle gv_ldi(d, sb, *flags); 107130389Sle break; 108130389Sle default: 109130389Sle gctl_error(req, "unknown object '%s'", 110130389Sle arg); 111130389Sle break; 112130389Sle } 113130389Sle } 114130389Sle } else { 115130389Sle gv_ld(gp, req, sb); 116130389Sle sbuf_printf(sb, "\n"); 117130389Sle gv_lv(gp, req, sb); 118130389Sle sbuf_printf(sb, "\n"); 119130389Sle gv_lp(gp, req, sb); 120130389Sle sbuf_printf(sb, "\n"); 121130389Sle gv_ls(gp, req, sb); 122130389Sle } 123130389Sle 124130389Sle /* List drives. */ 125130389Sle } else if (!strcmp(cmd, "ld")) { 126130389Sle if (*argc) { 127130389Sle for (i = 0; i < *argc; i++) { 128130389Sle snprintf(buf, sizeof(buf), "argv%d", i); 129130389Sle arg = gctl_get_param(req, buf, NULL); 130130389Sle if (arg == NULL) 131130389Sle continue; 132130389Sle type = gv_object_type(sc, arg); 133130389Sle if (type != GV_TYPE_DRIVE) { 134130389Sle gctl_error(req, "'%s' is not a drive", 135130389Sle arg); 136130389Sle continue; 137130389Sle } else { 138130389Sle d = gv_find_drive(sc, arg); 139130389Sle gv_ldi(d, sb, *flags); 140130389Sle } 141130389Sle } 142130389Sle } else 143130389Sle gv_ld(gp, req, sb); 144130389Sle 145130389Sle /* List volumes. */ 146130389Sle } else if (!strcmp(cmd, "lv")) { 147130389Sle if (*argc) { 148130389Sle for (i = 0; i < *argc; i++) { 149130389Sle snprintf(buf, sizeof(buf), "argv%d", i); 150130389Sle arg = gctl_get_param(req, buf, NULL); 151130389Sle if (arg == NULL) 152130389Sle continue; 153130389Sle type = gv_object_type(sc, arg); 154130389Sle if (type != GV_TYPE_VOL) { 155130389Sle gctl_error(req, "'%s' is not a volume", 156130389Sle arg); 157130389Sle continue; 158130389Sle } else { 159130389Sle v = gv_find_vol(sc, arg); 160130389Sle gv_lvi(v, sb, *flags); 161130389Sle } 162130389Sle } 163130389Sle } else 164130389Sle gv_lv(gp, req, sb); 165130389Sle 166130389Sle /* List plexes. */ 167130389Sle } else if (!strcmp(cmd, "lp")) { 168130389Sle if (*argc) { 169130389Sle for (i = 0; i < *argc; i++) { 170130389Sle snprintf(buf, sizeof(buf), "argv%d", i); 171130389Sle arg = gctl_get_param(req, buf, NULL); 172130389Sle if (arg == NULL) 173130389Sle continue; 174130389Sle type = gv_object_type(sc, arg); 175130389Sle if (type != GV_TYPE_PLEX) { 176130389Sle gctl_error(req, "'%s' is not a plex", 177130389Sle arg); 178130389Sle continue; 179130389Sle } else { 180130389Sle p = gv_find_plex(sc, arg); 181130389Sle gv_lpi(p, sb, *flags); 182130389Sle } 183130389Sle } 184130389Sle } else 185130389Sle gv_lp(gp, req, sb); 186130389Sle 187130389Sle /* List subdisks. */ 188130389Sle } else if (!strcmp(cmd, "ls")) { 189130389Sle if (*argc) { 190130389Sle for (i = 0; i < *argc; i++) { 191130389Sle snprintf(buf, sizeof(buf), "argv%d", i); 192130389Sle arg = gctl_get_param(req, buf, NULL); 193130389Sle if (arg == NULL) 194130389Sle continue; 195130389Sle type = gv_object_type(sc, arg); 196130389Sle if (type != GV_TYPE_SD) { 197130389Sle gctl_error(req, "'%s' is not a subdisk", 198130389Sle arg); 199130389Sle continue; 200130389Sle } else { 201130389Sle s = gv_find_sd(sc, arg); 202130389Sle gv_lsi(s, sb, *flags); 203130389Sle } 204130389Sle } 205130389Sle } else 206130389Sle gv_ls(gp, req, sb); 207130389Sle 208130389Sle } else 209130389Sle gctl_error(req, "unknown command '%s'", cmd); 210130389Sle 211130389Sle sbuf_finish(sb); 212130389Sle gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); 213130389Sle sbuf_delete(sb); 214130389Sle} 215130389Sle 216130389Sle/* List one or more volumes. */ 217130389Slevoid 218130389Slegv_lv(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) 219130389Sle{ 220130389Sle struct gv_softc *sc; 221130389Sle struct gv_volume *v; 222130389Sle int i, *flags; 223130389Sle 224130389Sle sc = gp->softc; 225130389Sle i = 0; 226130389Sle 227130389Sle LIST_FOREACH(v, &sc->volumes, volume) 228130389Sle i++; 229130389Sle 230130389Sle sbuf_printf(sb, "%d volume%s:\n", i, i == 1 ? "" : "s"); 231130389Sle 232130389Sle if (i) { 233130389Sle flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 234130389Sle LIST_FOREACH(v, &sc->volumes, volume) 235130389Sle gv_lvi(v, sb, *flags); 236130389Sle } 237130389Sle} 238130389Sle 239130389Sle/* List a single volume. */ 240130389Slevoid 241130389Slegv_lvi(struct gv_volume *v, struct sbuf *sb, int flags) 242130389Sle{ 243130389Sle struct gv_plex *p; 244130389Sle int i; 245130389Sle 246130389Sle if (flags & GV_FLAG_V) { 247130389Sle sbuf_printf(sb, "Volume %s:\tSize: %jd bytes (%jd MB)\n", 248130389Sle v->name, (intmax_t)v->size, (intmax_t)v->size / MEGABYTE); 249130389Sle sbuf_printf(sb, "\t\tState: %s\n", gv_volstate(v->state)); 250130389Sle } else { 251130389Sle sbuf_printf(sb, "V %-21s State: %s\tPlexes: %7d\tSize: %s\n", 252130389Sle v->name, gv_volstate(v->state), v->plexcount, 253130389Sle gv_roughlength(v->size, 0)); 254130389Sle } 255130389Sle 256130389Sle if (flags & GV_FLAG_VV) { 257130389Sle i = 0; 258130389Sle LIST_FOREACH(p, &v->plexes, in_volume) { 259130389Sle sbuf_printf(sb, "\t\tPlex %2d:\t%s\t(%s), %s\n", i, 260130389Sle p->name, gv_plexstate(p->state), 261130389Sle gv_roughlength(p->size, 0)); 262130389Sle i++; 263130389Sle } 264130389Sle } 265130389Sle 266130389Sle if (flags & GV_FLAG_R) { 267130389Sle LIST_FOREACH(p, &v->plexes, in_volume) 268130389Sle gv_lpi(p, sb, flags); 269130389Sle } 270130389Sle} 271130389Sle 272130389Sle/* List one or more plexes. */ 273130389Slevoid 274130389Slegv_lp(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) 275130389Sle{ 276130389Sle struct gv_softc *sc; 277130389Sle struct gv_plex *p; 278130389Sle int i, *flags; 279130389Sle 280130389Sle sc = gp->softc; 281130389Sle i = 0; 282130389Sle 283130389Sle LIST_FOREACH(p, &sc->plexes, plex) 284130389Sle i++; 285130389Sle 286130389Sle sbuf_printf(sb, "%d plex%s:\n", i, i == 1 ? "" : "es"); 287130389Sle 288130389Sle if (i) { 289130389Sle flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 290130389Sle LIST_FOREACH(p, &sc->plexes, plex) 291130389Sle gv_lpi(p, sb, *flags); 292130389Sle } 293130389Sle} 294130389Sle 295130389Sle/* List a single plex. */ 296130389Slevoid 297130389Slegv_lpi(struct gv_plex *p, struct sbuf *sb, int flags) 298130389Sle{ 299130389Sle struct gv_sd *s; 300130389Sle int i; 301130389Sle 302130389Sle if (flags & GV_FLAG_V) { 303130389Sle sbuf_printf(sb, "Plex %s:\tSize:\t%9jd bytes (%jd MB)\n", 304130389Sle p->name, (intmax_t)p->size, (intmax_t)p->size / MEGABYTE); 305130389Sle sbuf_printf(sb, "\t\tSubdisks: %8d\n", p->sdcount); 306190507Slulf sbuf_printf(sb, "\t\tState: %s\n", gv_plexstate(p->state)); 307190507Slulf if ((p->flags & GV_PLEX_SYNCING) || 308190507Slulf (p->flags & GV_PLEX_GROWING) || 309190507Slulf (p->flags & GV_PLEX_REBUILDING)) { 310190507Slulf sbuf_printf(sb, "\t\tSynced: "); 311190507Slulf sbuf_printf(sb, "%16jd bytes (%d%%)\n", 312190507Slulf (intmax_t)p->synced, 313190507Slulf (p->size > 0) ? (int)((p->synced * 100) / p->size) : 314190507Slulf 0); 315190507Slulf } 316190507Slulf sbuf_printf(sb, "\t\tOrganization: %s", gv_plexorg(p->org)); 317130389Sle if (gv_is_striped(p)) { 318130389Sle sbuf_printf(sb, "\tStripe size: %s\n", 319130389Sle gv_roughlength(p->stripesize, 1)); 320130389Sle } 321190507Slulf sbuf_printf(sb, "\t\tFlags: %d\n", p->flags); 322130389Sle if (p->vol_sc != NULL) { 323130389Sle sbuf_printf(sb, "\t\tPart of volume %s\n", p->volume); 324130389Sle } 325130389Sle } else { 326190507Slulf sbuf_printf(sb, "P %-18s %2s State: ", p->name, 327190507Slulf gv_plexorg_short(p->org)); 328190507Slulf if ((p->flags & GV_PLEX_SYNCING) || 329190507Slulf (p->flags & GV_PLEX_GROWING) || 330190507Slulf (p->flags & GV_PLEX_REBUILDING)) { 331190507Slulf sbuf_printf(sb, "S %d%%\t", (int)((p->synced * 100) / 332190507Slulf p->size)); 333190507Slulf } else { 334190507Slulf sbuf_printf(sb, "%s\t", gv_plexstate(p->state)); 335190507Slulf } 336190507Slulf sbuf_printf(sb, "Subdisks: %5d\tSize: %s\n", p->sdcount, 337130389Sle gv_roughlength(p->size, 0)); 338130389Sle } 339130389Sle 340130389Sle if (flags & GV_FLAG_VV) { 341130389Sle i = 0; 342130389Sle LIST_FOREACH(s, &p->subdisks, in_plex) { 343130389Sle sbuf_printf(sb, "\t\tSubdisk %d:\t%s\n", i, s->name); 344130389Sle sbuf_printf(sb, "\t\t state: %s\tsize %11jd " 345130389Sle "(%jd MB)\n", gv_sdstate(s->state), 346130389Sle (intmax_t)s->size, (intmax_t)s->size / MEGABYTE); 347130389Sle if (p->org == GV_PLEX_CONCAT) { 348130389Sle sbuf_printf(sb, "\t\t\toffset %9jd (0x%jx)\n", 349130389Sle (intmax_t)s->plex_offset, 350130389Sle (intmax_t)s->plex_offset); 351130389Sle } 352130389Sle i++; 353130389Sle } 354130389Sle } 355130389Sle 356130389Sle if (flags & GV_FLAG_R) { 357130389Sle LIST_FOREACH(s, &p->subdisks, in_plex) 358130389Sle gv_lsi(s, sb, flags); 359130389Sle } 360130389Sle} 361130389Sle 362130389Sle/* List one or more subdisks. */ 363130389Slevoid 364130389Slegv_ls(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) 365130389Sle{ 366130389Sle struct gv_softc *sc; 367130389Sle struct gv_sd *s; 368130389Sle int i, *flags; 369130389Sle 370130389Sle sc = gp->softc; 371130389Sle i = 0; 372130389Sle 373130389Sle LIST_FOREACH(s, &sc->subdisks, sd) 374130389Sle i++; 375130389Sle 376130389Sle sbuf_printf(sb, "%d subdisk%s:\n", i, i == 1 ? "" : "s"); 377130389Sle 378130389Sle if (i) { 379130389Sle flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 380130389Sle LIST_FOREACH(s, &sc->subdisks, sd) 381130389Sle gv_lsi(s, sb, *flags); 382130389Sle } 383130389Sle} 384130389Sle 385130389Sle/* List a single subdisk. */ 386130389Slevoid 387130389Slegv_lsi(struct gv_sd *s, struct sbuf *sb, int flags) 388130389Sle{ 389130389Sle if (flags & GV_FLAG_V) { 390130389Sle sbuf_printf(sb, "Subdisk %s:\n", s->name); 391130389Sle sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n", 392130389Sle (intmax_t)s->size, (intmax_t)s->size / MEGABYTE); 393130389Sle sbuf_printf(sb, "\t\tState: %s\n", gv_sdstate(s->state)); 394130389Sle 395135966Sle if (s->state == GV_SD_INITIALIZING || 396135966Sle s->state == GV_SD_REVIVING) { 397135966Sle if (s->state == GV_SD_INITIALIZING) 398135966Sle sbuf_printf(sb, "\t\tInitialized: "); 399135966Sle else 400135966Sle sbuf_printf(sb, "\t\tRevived: "); 401135966Sle 402135966Sle sbuf_printf(sb, "%16jd bytes (%d%%)\n", 403135966Sle (intmax_t)s->initialized, 404130389Sle (int)((s->initialized * 100) / s->size)); 405130389Sle } 406130389Sle 407130389Sle if (s->plex_sc != NULL) { 408130389Sle sbuf_printf(sb, "\t\tPlex %s at offset %jd (%s)\n", 409130389Sle s->plex, (intmax_t)s->plex_offset, 410130389Sle gv_roughlength(s->plex_offset, 1)); 411130389Sle } 412130389Sle 413130389Sle sbuf_printf(sb, "\t\tDrive %s (%s) at offset %jd (%s)\n", 414130389Sle s->drive, 415130389Sle s->drive_sc == NULL ? "*missing*" : s->drive_sc->name, 416130389Sle (intmax_t)s->drive_offset, 417130389Sle gv_roughlength(s->drive_offset, 1)); 418190507Slulf sbuf_printf(sb, "\t\tFlags: %d\n", s->flags); 419130389Sle } else { 420130389Sle sbuf_printf(sb, "S %-21s State: ", s->name); 421135966Sle if (s->state == GV_SD_INITIALIZING || 422135966Sle s->state == GV_SD_REVIVING) { 423135966Sle if (s->state == GV_SD_INITIALIZING) 424135966Sle sbuf_printf(sb, "I "); 425135966Sle else 426135966Sle sbuf_printf(sb, "R "); 427135966Sle sbuf_printf(sb, "%d%%\t", 428130389Sle (int)((s->initialized * 100) / s->size)); 429130389Sle } else { 430130389Sle sbuf_printf(sb, "%s\t", gv_sdstate(s->state)); 431130389Sle } 432130389Sle sbuf_printf(sb, "D: %-12s Size: %s\n", s->drive, 433130389Sle gv_roughlength(s->size, 0)); 434130389Sle } 435130389Sle} 436130389Sle 437130389Sle/* List one or more drives. */ 438130389Slevoid 439130389Slegv_ld(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) 440130389Sle{ 441130389Sle struct gv_softc *sc; 442130389Sle struct gv_drive *d; 443130389Sle int i, *flags; 444130389Sle 445130389Sle sc = gp->softc; 446130389Sle i = 0; 447130389Sle 448130389Sle LIST_FOREACH(d, &sc->drives, drive) 449130389Sle i++; 450130389Sle 451130389Sle sbuf_printf(sb, "%d drive%s:\n", i, i == 1 ? "" : "s"); 452130389Sle 453130389Sle if (i) { 454130389Sle flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 455130389Sle LIST_FOREACH(d, &sc->drives, drive) 456130389Sle gv_ldi(d, sb, *flags); 457130389Sle } 458130389Sle} 459130389Sle 460130389Sle/* List a single drive. */ 461130389Slevoid 462130389Slegv_ldi(struct gv_drive *d, struct sbuf *sb, int flags) 463130389Sle{ 464130389Sle struct gv_freelist *fl; 465130389Sle struct gv_sd *s; 466130389Sle 467130389Sle /* Verbose listing. */ 468130389Sle if (flags & GV_FLAG_V) { 469130389Sle sbuf_printf(sb, "Drive %s:\tDevice %s\n", d->name, d->device); 470130389Sle sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n", 471130389Sle (intmax_t)d->size, (intmax_t)d->size / MEGABYTE); 472130389Sle sbuf_printf(sb, "\t\tUsed: %16jd bytes (%jd MB)\n", 473130389Sle (intmax_t)d->size - d->avail, 474130389Sle (intmax_t)(d->size - d->avail) / MEGABYTE); 475130389Sle sbuf_printf(sb, "\t\tAvailable: %11jd bytes (%jd MB)\n", 476130389Sle (intmax_t)d->avail, (intmax_t)d->avail / MEGABYTE); 477130389Sle sbuf_printf(sb, "\t\tState: %s\n", gv_drivestate(d->state)); 478190507Slulf sbuf_printf(sb, "\t\tFlags: %d\n", d->flags); 479130389Sle 480130389Sle /* Be very verbose. */ 481130389Sle if (flags & GV_FLAG_VV) { 482130389Sle sbuf_printf(sb, "\t\tFree list contains %d entries:\n", 483130389Sle d->freelist_entries); 484130389Sle sbuf_printf(sb, "\t\t Offset\t Size\n"); 485130389Sle LIST_FOREACH(fl, &d->freelist, freelist) 486130389Sle sbuf_printf(sb, "\t\t%9jd\t%9jd\n", 487130389Sle (intmax_t)fl->offset, (intmax_t)fl->size); 488130389Sle } 489130389Sle } else { 490130389Sle sbuf_printf(sb, "D %-21s State: %s\t/dev/%s\tA: %jd/%jd MB " 491130389Sle "(%d%%)\n", d->name, gv_drivestate(d->state), d->device, 492130389Sle (intmax_t)d->avail / MEGABYTE, (intmax_t)d->size / MEGABYTE, 493190507Slulf d->size > 0 ? (int)((d->avail * 100) / d->size) : 0); 494130389Sle } 495130389Sle 496130389Sle /* Recursive listing. */ 497130389Sle if (flags & GV_FLAG_R) { 498130389Sle LIST_FOREACH(s, &d->subdisks, from_drive) 499130389Sle gv_lsi(s, sb, flags); 500130389Sle } 501130389Sle} 502