Index: head/sys/geom/vinum/geom_vinum.c =================================================================== --- head/sys/geom/vinum/geom_vinum.c (revision 131106) +++ head/sys/geom/vinum/geom_vinum.c (revision 131107) @@ -1,620 +1,621 @@ /* * Copyright (c) 2004 Lukas Ertl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #if 0 SYSCTL_DECL(_kern_geom); SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, "GEOM_VINUM stuff"); SYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &gv_debug, 0, "Debug level"); #endif int gv_create(struct g_geom *, struct gctl_req *); void config_new_drive(struct gv_drive *); static void gv_orphan(struct g_consumer *cp) { struct g_geom *gp; struct gv_softc *sc; int error; g_topology_assert(); KASSERT(cp != NULL, ("gv_orphan: null cp")); gp = cp->geom; KASSERT(gp != NULL, ("gv_orphan: null gp")); sc = gp->softc; g_trace(G_T_TOPOLOGY, "gv_orphan(%s)", gp->name); if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) g_access(cp, -cp->acr, -cp->acw, -cp->ace); error = cp->provider->error; if (error == 0) error = ENXIO; g_detach(cp); g_destroy_consumer(cp); if (!LIST_EMPTY(&gp->consumer)) return; g_free(sc); g_wither_geom(gp, error); } static void gv_start(struct bio *bp) { struct bio *bp2; struct g_geom *gp; gp = bp->bio_to->geom; switch(bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: bp2 = g_clone_bio(bp); bp2->bio_done = g_std_done; g_io_request(bp2, LIST_FIRST(&gp->consumer)); return; default: g_io_deliver(bp, EOPNOTSUPP); return; } } static int gv_access(struct g_provider *pp, int dr, int dw, int de) { struct g_geom *gp; struct g_consumer *cp; int error; gp = pp->geom; error = ENXIO; cp = LIST_FIRST(&gp->consumer); error = g_access(cp, dr, dw, de); return (error); } static struct g_geom * gv_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) { struct g_geom *gp; struct g_consumer *cp; struct gv_softc *sc; struct gv_hdr *vhdr; int error, first; char *buf; vhdr = NULL; buf = NULL; first = 0; g_trace(G_T_TOPOLOGY, "gv_taste(%s, %s)", mp->name, pp->name); g_topology_assert(); if (pp->sectorsize == 0) return (NULL); /* Check if we already have a VINUM geom, or create a new one. */ if (LIST_EMPTY(&mp->geom)) { gp = g_new_geomf(mp, "VINUM"); gp->spoiled = gv_orphan; gp->orphan = gv_orphan; gp->access = gv_access; gp->start = gv_start; gp->softc = g_malloc(sizeof(struct gv_softc), M_WAITOK | M_ZERO); sc = gp->softc; sc->geom = gp; LIST_INIT(&sc->drives); LIST_INIT(&sc->subdisks); LIST_INIT(&sc->plexes); LIST_INIT(&sc->volumes); first++; } else { gp = LIST_FIRST(&mp->geom); sc = gp->softc; } /* We need a temporary consumer to read the config from. */ cp = g_new_consumer(gp); error = g_attach(cp, pp); if (error) { g_destroy_consumer(cp); if (first) { g_free(sc); g_destroy_geom(gp); } return (NULL); } error = g_access(cp, 1, 0, 0); if (error) { g_detach(cp); g_destroy_consumer(cp); if (first) { g_free(gp->softc); g_destroy_geom(gp); } return (NULL); } g_topology_unlock(); /* Check if the provided slice is a valid vinum drive. */ vhdr = g_read_data(cp, GV_HDR_OFFSET, GV_HDR_LEN, &error); if (vhdr == NULL || error != 0) { g_topology_lock(); g_access(cp, -1, 0, 0); g_detach(cp); g_destroy_consumer(cp); if (first) { g_free(sc); g_destroy_geom(gp); } return (NULL); } /* This provider has no vinum magic on board. */ if (vhdr->magic != GV_MAGIC) { /* Release the temporary consumer, we don't need it anymore. */ g_topology_lock(); g_access(cp, -1, 0, 0); g_detach(cp); g_destroy_consumer(cp); g_free(vhdr); /* * If there is no other VINUM geom yet just take this one; the * configuration is still empty, but it can be filled by other * valid vinum drives later. */ if (first) return (gp); else return (NULL); /* * We have found a valid vinum drive, now read the on-disk * configuration. */ } else { g_free(vhdr); buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, &error); if (buf == NULL || error != 0) { g_topology_lock(); g_access(cp, -1, 0, 0); g_detach(cp); g_destroy_consumer(cp); if (first) { g_free(sc); g_destroy_geom(gp); } return (NULL); } /* Release the temporary consumer, we don't need it anymore. */ g_topology_lock(); g_access(cp, -1, 0, 0); g_detach(cp); g_destroy_consumer(cp); /* We are the first VINUM geom. */ if (first) { gv_parse_config(sc, buf, 0); g_free(buf); return (gp); /* Just merge the configs. */ } else { gv_parse_config(sc, buf, 1); g_free(buf); return (NULL); } } } /* XXX this really belongs somewhere else */ void config_new_drive(struct gv_drive *d) { struct gv_hdr *vhdr; struct gv_freelist *fl; KASSERT(d != NULL, ("config_new_drive: NULL d")); vhdr = g_malloc(sizeof(*vhdr), M_WAITOK | M_ZERO); vhdr->magic = GV_MAGIC; vhdr->config_length = GV_CFG_LEN; bcopy(hostname, vhdr->label.sysname, GV_HOSTNAME_LEN); strncpy(vhdr->label.name, d->name, GV_MAXDRIVENAME); microtime(&vhdr->label.date_of_birth); d->hdr = vhdr; LIST_INIT(&d->subdisks); LIST_INIT(&d->freelist); fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO); fl->offset = GV_DATA_START; fl->size = d->avail; LIST_INSERT_HEAD(&d->freelist, fl, freelist); d->freelist_entries = 1; } /* Handle userland requests for creating new objects. */ int gv_create(struct g_geom *gp, struct gctl_req *req) { struct gv_softc *sc; struct gv_drive *d, *d2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; struct gv_volume *v, *v2; struct g_consumer *cp; struct g_provider *pp; int error, i, *drives, *plexes, *subdisks, *volumes; char buf[20], errstr[ERRBUFSIZ]; g_topology_assert(); sc = gp->softc; /* Find out how many of each object have been passed in. */ volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes)); plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes)); subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks)); drives = gctl_get_paraml(req, "drives", sizeof(*drives)); /* First, handle drive definitions ... */ for (i = 0; i < *drives; i++) { snprintf(buf, sizeof(buf), "drive%d", i); d2 = gctl_get_paraml(req, buf, sizeof(*d2)); d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); bcopy(d2, d, sizeof(*d)); /* * Make sure that the provider specified in the drive * specification is an active GEOM provider. */ pp = g_provider_by_name(d->device); if (pp == NULL) { gctl_error(req, "%s: drive not found", d->device); g_free(d); return (-1); } d->size = pp->mediasize - GV_DATA_START; d->avail = d->size; config_new_drive(d); LIST_INSERT_HEAD(&sc->drives, d, drive); } /* ... then volume definitions ... */ for (i = 0; i < *volumes; i++) { error = 0; snprintf(buf, sizeof(buf), "volume%d", i); v2 = gctl_get_paraml(req, buf, sizeof(*v2)); v = gv_find_vol(sc, v2->name); if (v != NULL) { gctl_error(req, "volume '%s' is already known", v->name); return (-1); } v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); bcopy(v2, v, sizeof(*v)); v->vinumconf = sc; LIST_INIT(&v->plexes); LIST_INSERT_HEAD(&sc->volumes, v, volume); } /* ... then plex definitions ... */ for (i = 0; i < *plexes; i++) { error = 0; snprintf(buf, sizeof(buf), "plex%d", i); p2 = gctl_get_paraml(req, buf, sizeof(*p2)); p = gv_find_plex(sc, p2->name); if (p != NULL) { gctl_error(req, "plex '%s' is already known", p->name); return (-1); } p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); bcopy(p2, p, sizeof(*p)); /* Find the volume this plex should be attached to. */ v = gv_find_vol(sc, p->volume); if (v != NULL) { if (v->plexcount) p->flags |= GV_PLEX_ADDED; p->vol_sc = v; v->plexcount++; LIST_INSERT_HEAD(&v->plexes, p, in_volume); } p->vinumconf = sc; + p->flags |= GV_PLEX_NEWBORN; LIST_INIT(&p->subdisks); LIST_INSERT_HEAD(&sc->plexes, p, plex); } /* ... and finally, subdisk definitions. */ for (i = 0; i < *subdisks; i++) { error = 0; snprintf(buf, sizeof(buf), "sd%d", i); s2 = gctl_get_paraml(req, buf, sizeof(*s2)); s = gv_find_sd(sc, s2->name); if (s != NULL) { gctl_error(req, "subdisk '%s' is already known", s->name); return (-1); } s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); bcopy(s2, s, sizeof(*s)); /* Find the drive where this subdisk should be put on. */ d = gv_find_drive(sc, s->drive); /* drive not found - XXX */ if (d == NULL) { printf("FOO: drive '%s' not found\n", s->drive); g_free(s); continue; } /* Find the plex where this subdisk belongs to. */ p = gv_find_plex(sc, s->plex); /* plex not found - XXX */ if (p == NULL) { printf("FOO: plex '%s' not found\n", s->plex); g_free(s); continue; } /* * First we give the subdisk to the drive, to handle autosized * values ... */ error = gv_sd_to_drive(sc, d, s, errstr, sizeof(errstr)); if (error) { gctl_error(req, errstr); g_free(s); continue; } /* * Then, we give the subdisk to the plex; we check if the * given values are correct and maybe adjust them. */ error = gv_sd_to_plex(p, s, 1); if (error) { printf("FOO: couldn't give sd '%s' to plex '%s'\n", s->name, p->name); } s->flags |= GV_SD_NEWBORN; s->vinumconf = sc; LIST_INSERT_HEAD(&sc->subdisks, s, sd); } LIST_FOREACH(s, &sc->subdisks, sd) gv_update_sd_state(s); LIST_FOREACH(p, &sc->plexes, plex) gv_update_plex_config(p); LIST_FOREACH(v, &sc->volumes, volume) gv_update_vol_state(v); /* * Write out the configuration to each drive. If the drive doesn't * have a valid geom_slice geom yet, attach it temporarily to our VINUM * geom. */ LIST_FOREACH(d, &sc->drives, drive) { if (d->geom == NULL) { /* * XXX if the provider disapears before we get a chance * to write the config out to the drive, should this * be handled any differently? */ pp = g_provider_by_name(d->device); if (pp == NULL) { printf("geom_vinum: %s: drive disapeared?\n", d->device); continue; } cp = g_new_consumer(gp); g_attach(cp, pp); gv_save_config(cp, d, sc); g_detach(cp); g_destroy_consumer(cp); } else gv_save_config(NULL, d, sc); } return (0); } static void gv_config(struct gctl_req *req, struct g_class *mp, char const *verb) { struct g_geom *gp; struct gv_softc *sc; struct sbuf *sb; char *comment; g_topology_assert(); gp = LIST_FIRST(&mp->geom); sc = gp->softc; if (!strcmp(verb, "list")) { gv_list(gp, req); /* Save our configuration back to disk. */ } else if (!strcmp(verb, "saveconfig")) { gv_save_config_all(sc); /* Return configuration in string form. */ } else if (!strcmp(verb, "getconfig")) { comment = gctl_get_param(req, "comment", NULL); sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); gv_format_config(sc, sb, 0, comment); sbuf_finish(sb); gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); sbuf_delete(sb); } else if (!strcmp(verb, "create")) { gv_create(gp, req); } else if (!strcmp(verb, "remove")) { gv_remove(gp, req); } else if (!strcmp(verb, "start")) { gv_start_obj(gp, req); } else gctl_error(req, "Unknown verb parameter"); } static int gv_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) { struct g_geom *gp2; struct gv_softc *sc; struct gv_drive *d, *d2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; struct gv_volume *v, *v2; struct gv_freelist *fl, *fl2; g_trace(G_T_TOPOLOGY, "gv_destroy_geom: %s", gp->name); g_topology_assert(); KASSERT(gp != NULL, ("gv_destroy_geom: null gp")); KASSERT(gp->softc != NULL, ("gv_destroy_geom: null sc")); sc = gp->softc; /* * Check if any of our drives is still open; if so, refuse destruction. */ LIST_FOREACH(d, &sc->drives, drive) { gp2 = d->geom; if (gv_is_open(gp2)) return (EBUSY); } /* Clean up and deallocate what we allocated. */ LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) { LIST_REMOVE(d, drive); g_free(d->hdr); d->hdr = NULL; LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { d->freelist_entries--; LIST_REMOVE(fl, freelist); g_free(fl); fl = NULL; } d->geom->softc = NULL; g_free(d); } LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) { LIST_REMOVE(s, sd); s->drive_sc = NULL; s->plex_sc = NULL; s->provider = NULL; s->consumer = NULL; g_free(s); } LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) { LIST_REMOVE(p, plex); gv_kill_thread(p); p->vol_sc = NULL; p->geom->softc = NULL; p->provider = NULL; p->consumer = NULL; if (p->org == GV_PLEX_RAID5) { mtx_destroy(&p->worklist_mtx); } g_free(p); } LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) { LIST_REMOVE(v, volume); v->geom->softc = NULL; g_free(v); } gp->softc = NULL; g_free(sc); g_wither_geom(gp, ENXIO); return (0); } #define VINUM_CLASS_NAME "VINUM" static struct g_class g_vinum_class = { .name = VINUM_CLASS_NAME, .taste = gv_taste, .destroy_geom = gv_destroy_geom, .ctlreq = gv_config, }; DECLARE_GEOM_CLASS(g_vinum_class, g_vinum); Index: head/sys/geom/vinum/geom_vinum_subr.c =================================================================== --- head/sys/geom/vinum/geom_vinum_subr.c (revision 131106) +++ head/sys/geom/vinum/geom_vinum_subr.c (revision 131107) @@ -1,806 +1,808 @@ /*- * Copyright (c) 2004 Lukas Ertl * Copyright (c) 1997, 1998, 1999 * Nan Yang Computer Services Limited. All rights reserved. * * Parts written by Greg Lehey * * This software is distributed under the so-called ``Berkeley * License'': * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Nan Yang Computer * Services Limited. * 4. Neither the name of the Company nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * This software is provided ``as is'', and any express or implied * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose are disclaimed. * In no event shall the company or contributors be liable for any * direct, indirect, incidental, special, exemplary, or consequential * damages (including, but not limited to, procurement of substitute * goods or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, whether * in contract, strict liability, or tort (including negligence or * otherwise) arising in any way out of the use of this software, even if * advised of the possibility of such damage. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* Find the VINUM class and it's associated geom. */ struct g_geom * find_vinum_geom(void) { struct g_class *mp; struct g_geom *gp; g_topology_assert(); gp = NULL; LIST_FOREACH(mp, &g_classes, class) { if (!strcmp(mp->name, "VINUM")) { gp = LIST_FIRST(&mp->geom); break; } } return (gp); } /* * Parse the vinum config provided in *buf and store it in *gp's softc. * If parameter 'merge' is non-zero, then the given config is merged into * *gp. */ void gv_parse_config(struct gv_softc *sc, u_char *buf, int merge) { char *aptr, *bptr, *cptr; struct gv_volume *v, *v2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; int tokens; char *token[GV_MAXARGS]; g_topology_assert(); KASSERT(sc != NULL, ("gv_parse_config: NULL softc")); /* Until the end of the string *buf. */ for (aptr = buf; *aptr != '\0'; aptr = bptr) { bptr = aptr; cptr = aptr; /* Seperate input lines. */ while (*bptr != '\n') bptr++; *bptr = '\0'; bptr++; tokens = gv_tokenize(cptr, token, GV_MAXARGS); if (tokens > 0) { if (!strcmp(token[0], "volume")) { v = gv_new_volume(tokens, token); if (v == NULL) { printf("geom_vinum: failed volume\n"); break; } if (merge) { v2 = gv_find_vol(sc, v->name); if (v2 != NULL) { g_free(v); continue; } } v->vinumconf = sc; LIST_INIT(&v->plexes); LIST_INSERT_HEAD(&sc->volumes, v, volume); } else if (!strcmp(token[0], "plex")) { p = gv_new_plex(tokens, token); if (p == NULL) { printf("geom_vinum: failed plex\n"); break; } if (merge) { p2 = gv_find_plex(sc, p->name); if (p2 != NULL) { g_free(p); continue; } } p->vinumconf = sc; LIST_INIT(&p->subdisks); LIST_INSERT_HEAD(&sc->plexes, p, plex); } else if (!strcmp(token[0], "sd")) { s = gv_new_sd(tokens, token); if (s == NULL) { printf("geom_vinum: failed subdisk\n"); break; } if (merge) { s2 = gv_find_sd(sc, s->name); if (s2 != NULL) { g_free(s); continue; } } s->vinumconf = sc; LIST_INSERT_HEAD(&sc->subdisks, s, sd); } } } } /* * Format the vinum configuration properly. If ondisk is non-zero then the * configuration is intended to be written to disk later. */ void gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix) { struct gv_drive *d; struct gv_sd *s; struct gv_plex *p; struct gv_volume *v; g_topology_assert(); /* * We don't need the drive configuration if we're not writing the * config to disk. */ if (!ondisk) { LIST_FOREACH(d, &sc->drives, drive) { sbuf_printf(sb, "%sdrive %s device %s\n", prefix, d->name, d->device); } } LIST_FOREACH(v, &sc->volumes, volume) { if (!ondisk) sbuf_printf(sb, "%s", prefix); sbuf_printf(sb, "volume %s", v->name); if (ondisk) sbuf_printf(sb, " state %s", gv_volstate(v->state)); sbuf_printf(sb, "\n"); } LIST_FOREACH(p, &sc->plexes, plex) { if (!ondisk) sbuf_printf(sb, "%s", prefix); sbuf_printf(sb, "plex name %s org %s ", p->name, gv_plexorg(p->org)); if (gv_is_striped(p)) sbuf_printf(sb, "%ds ", p->stripesize / 512); if (p->vol_sc != NULL) sbuf_printf(sb, "vol %s", p->volume); if (ondisk) sbuf_printf(sb, " state %s", gv_plexstate(p->state)); sbuf_printf(sb, "\n"); } LIST_FOREACH(s, &sc->subdisks, sd) { if (!ondisk) sbuf_printf(sb, "%s", prefix); sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset " "%jds", s->name, s->drive, s->size / 512, s->drive_offset / 512); if (s->plex_sc != NULL) { sbuf_printf(sb, " plex %s plexoffset %jds", s->plex, s->plex_offset / 512); } if (ondisk) sbuf_printf(sb, " state %s", gv_sdstate(s->state)); sbuf_printf(sb, "\n"); } return; } /* * Take a size in bytes and return a pointer to a string which represents the * size best. If lj is != 0, return left justified, otherwise in a fixed 10 * character field suitable for columnar printing. * * Note this uses a static string: it's only intended to be used immediately * for printing. */ const char * gv_roughlength(off_t bytes, int lj) { static char desc[16]; /* Gigabytes. */ if (bytes > (off_t)MEGABYTE * 10000) snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB", bytes / GIGABYTE); /* Megabytes. */ else if (bytes > KILOBYTE * 10000) snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB", bytes / MEGABYTE); /* Kilobytes. */ else if (bytes > 10000) snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB", bytes / KILOBYTE); /* Bytes. */ else snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes); return (desc); } int gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check) { struct gv_sd *s2; g_topology_assert(); /* If this subdisk was already given to this plex, do nothing. */ if (s->plex_sc == p) return (0); /* Find the correct plex offset for this subdisk, if needed. */ if (s->plex_offset == -1) { if (p->sdcount) { LIST_FOREACH(s2, &p->subdisks, in_plex) { if (gv_is_striped(p)) s->plex_offset = p->sdcount * p->stripesize; else s->plex_offset = s2->plex_offset + s2->size; } } else s->plex_offset = 0; } p->sdcount++; /* Adjust the size of our plex. */ switch (p->org) { case GV_PLEX_CONCAT: case GV_PLEX_STRIPED: p->size += s->size; break; case GV_PLEX_RAID5: p->size = (p->sdcount - 1) * s->size; break; default: break; } /* There are no subdisks for this plex yet, just insert it. */ if (LIST_EMPTY(&p->subdisks)) { LIST_INSERT_HEAD(&p->subdisks, s, in_plex); /* Insert in correct order, depending on plex_offset. */ } else { LIST_FOREACH(s2, &p->subdisks, in_plex) { if (s->plex_offset < s2->plex_offset) { LIST_INSERT_BEFORE(s2, s, in_plex); break; } else if (LIST_NEXT(s2, in_plex) == NULL) { LIST_INSERT_AFTER(s2, s, in_plex); break; } } } s->plex_sc = p; return (0); } void gv_update_plex_config(struct gv_plex *p) { struct gv_sd *s, *s2; off_t remainder; int required_sds, state; KASSERT(p != NULL, ("gv_update_plex_config: NULL p")); /* This is what we want the plex to be. */ state = GV_PLEX_UP; /* The plex was added to an already running volume. */ if (p->flags & GV_PLEX_ADDED) state = GV_PLEX_DOWN; switch (p->org) { case GV_PLEX_STRIPED: required_sds = 2; break; case GV_PLEX_RAID5: required_sds = 3; break; case GV_PLEX_CONCAT: default: required_sds = 0; break; } if (required_sds) { if (p->sdcount < required_sds) { state = GV_PLEX_DOWN; } /* * The subdisks in striped plexes must all have the same size. */ s = LIST_FIRST(&p->subdisks); LIST_FOREACH(s2, &p->subdisks, in_plex) { if (s->size != s2->size) { printf("geom_vinum: subdisk size mismatch " "%s (%jd) <> %s (%jd)\n", s->name, s->size, s2->name, s2->size); state = GV_PLEX_DOWN; } } /* Trim subdisk sizes so that they match the stripe size. */ LIST_FOREACH(s, &p->subdisks, in_plex) { remainder = s->size % p->stripesize; if (remainder) { printf("gvinum: size of sd %s is not a " "multiple of plex stripesize, taking off " "%jd bytes\n", s->name, (intmax_t)remainder); gv_adjust_freespace(s, remainder); } } } /* Adjust the size of our plex. */ if (p->sdcount > 0) { p->size = 0; switch (p->org) { case GV_PLEX_CONCAT: LIST_FOREACH(s, &p->subdisks, in_plex) p->size += s->size; break; case GV_PLEX_STRIPED: s = LIST_FIRST(&p->subdisks); p->size = p->sdcount * s->size; break; case GV_PLEX_RAID5: s = LIST_FIRST(&p->subdisks); p->size = (p->sdcount - 1) * s->size; break; default: break; } } if (p->sdcount == 0) state = GV_PLEX_DOWN; - else if ((p->flags & GV_PLEX_ADDED) || (p->org == GV_PLEX_RAID5)) { + else if ((p->flags & GV_PLEX_ADDED) || + ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) { LIST_FOREACH(s, &p->subdisks, in_plex) s->state = GV_SD_STALE; p->flags &= ~GV_PLEX_ADDED; + p->flags &= ~GV_PLEX_NEWBORN; p->state = GV_PLEX_DOWN; } } /* * Give a subdisk to a drive, check and adjust several parameters, adjust * freelist. */ int gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s, char *errstr, int errlen) { struct gv_sd *s2; struct gv_freelist *fl, *fl2; off_t tmp; int i; g_topology_assert(); fl2 = NULL; KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc")); KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive")); KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk")); KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr")); KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen", errlen)); /* Check if this subdisk was already given to this drive. */ if (s->drive_sc == d) return (0); /* Preliminary checks. */ if (s->size > d->avail || d->freelist_entries == 0) { snprintf(errstr, errlen, "not enough space on '%s' for '%s'", d->name, s->name); return (-1); } /* No size given, autosize it. */ if (s->size == -1) { /* Find the largest available slot. */ LIST_FOREACH(fl, &d->freelist, freelist) { if (fl->size >= s->size) { s->size = fl->size; s->drive_offset = fl->offset; fl2 = fl; } } /* No good slot found? */ if (s->size == -1) { snprintf(errstr, errlen, "couldn't autosize '%s' on " "'%s'", s->name, d->name); return (-1); } /* * Check if we have a free slot that's large enough for the given size. */ } else { i = 0; LIST_FOREACH(fl, &d->freelist, freelist) { /* Yes, this subdisk fits. */ if (fl->size >= s->size) { i++; /* Assign drive offset, if not given. */ if (s->drive_offset == -1) s->drive_offset = fl->offset; fl2 = fl; break; } } /* Couldn't find a good free slot. */ if (i == 0) { snprintf(errstr, errlen, "free slots to small for '%s' " "on '%s'", s->name, d->name); return (-1); } } /* No drive offset given, try to calculate it. */ if (s->drive_offset == -1) { /* Add offsets and sizes from other subdisks on this drive. */ LIST_FOREACH(s2, &d->subdisks, from_drive) { s->drive_offset = s2->drive_offset + s2->size; } /* * If there are no other subdisks yet, then set the default * offset to GV_DATA_START. */ if (s->drive_offset == -1) s->drive_offset = GV_DATA_START; /* Check if we have a free slot at the given drive offset. */ } else { i = 0; LIST_FOREACH(fl, &d->freelist, freelist) { /* Yes, this subdisk fits. */ if ((fl->offset <= s->drive_offset) && (fl->offset + fl->size >= s->drive_offset + s->size)) { i++; fl2 = fl; break; } } /* Couldn't find a good free slot. */ if (i == 0) { snprintf(errstr, errlen, "given drive_offset for '%s' " "won't fit on '%s'", s->name, d->name); return (-1); } } /* * Now that all parameters are checked and set up, we can give the * subdisk to the drive and adjust the freelist. */ /* First, adjust the freelist. */ LIST_FOREACH(fl, &d->freelist, freelist) { /* This is the free slot that we have found before. */ if (fl == fl2) { /* * The subdisk starts at the beginning of the free * slot. */ if (fl->offset == s->drive_offset) { fl->offset += s->size; fl->size -= s->size; /* * The subdisk uses the whole slot, so remove * it. */ if (fl->size == 0) { d->freelist_entries--; LIST_REMOVE(fl, freelist); } /* * The subdisk does not start at the beginning of the * free slot. */ } else { tmp = fl->offset + fl->size; fl->size = s->drive_offset - fl->offset; /* * The subdisk didn't use the complete rest of * the free slot, so we need to split it. */ if (s->drive_offset + s->size != tmp) { fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO); fl2->offset = s->drive_offset + s->size; fl2->size = tmp - fl2->offset; LIST_INSERT_AFTER(fl, fl2, freelist); d->freelist_entries++; } } break; } } /* * This is the first subdisk on this drive, just insert it into the * list. */ if (LIST_EMPTY(&d->subdisks)) { LIST_INSERT_HEAD(&d->subdisks, s, from_drive); /* There are other subdisks, so insert this one in correct order. */ } else { LIST_FOREACH(s2, &d->subdisks, from_drive) { if (s->drive_offset < s2->drive_offset) { LIST_INSERT_BEFORE(s2, s, from_drive); break; } else if (LIST_NEXT(s2, from_drive) == NULL) { LIST_INSERT_AFTER(s2, s, from_drive); break; } } } d->sdcount++; d->avail -= s->size; /* Link back from the subdisk to this drive. */ s->drive_sc = d; return (0); } void gv_adjust_freespace(struct gv_sd *s, off_t remainder) { struct gv_drive *d; struct gv_freelist *fl, *fl2; KASSERT(s != NULL, ("gv_adjust_freespace: NULL s")); d = s->drive_sc; KASSERT(d != NULL, ("gv_adjust_freespace: NULL d")); /* First, find the free slot that's immediately after this subdisk. */ fl = NULL; LIST_FOREACH(fl, &d->freelist, freelist) { if (fl->offset == s->drive_offset + s->size) break; } /* If there is no free slot behind this subdisk, so create one. */ if (fl == NULL) { fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); fl->size = remainder; fl->offset = s->drive_offset + s->size - remainder; if (d->freelist_entries == 0) { LIST_INSERT_HEAD(&d->freelist, fl, freelist); } else { LIST_FOREACH(fl2, &d->freelist, freelist) { if (fl->offset < fl2->offset) { LIST_INSERT_BEFORE(fl2, fl, freelist); break; } else if (LIST_NEXT(fl2, freelist) == NULL) { LIST_INSERT_AFTER(fl2, fl, freelist); break; } } } d->freelist_entries++; /* Expand the free slot we just found. */ } else { fl->offset -= remainder; fl->size += remainder; } s->size -= remainder; d->avail += remainder; } /* Check if the given plex is a striped one. */ int gv_is_striped(struct gv_plex *p) { KASSERT(p != NULL, ("gv_is_striped: NULL p")); switch(p->org) { case GV_PLEX_STRIPED: case GV_PLEX_RAID5: return (1); default: return (0); } } /* Find a volume by name. */ struct gv_volume * gv_find_vol(struct gv_softc *sc, char *name) { struct gv_volume *v; LIST_FOREACH(v, &sc->volumes, volume) { if (!strncmp(v->name, name, GV_MAXVOLNAME)) return (v); } return (NULL); } /* Find a plex by name. */ struct gv_plex * gv_find_plex(struct gv_softc *sc, char *name) { struct gv_plex *p; LIST_FOREACH(p, &sc->plexes, plex) { if (!strncmp(p->name, name, GV_MAXPLEXNAME)) return (p); } return (NULL); } /* Find a subdisk by name. */ struct gv_sd * gv_find_sd(struct gv_softc *sc, char *name) { struct gv_sd *s; LIST_FOREACH(s, &sc->subdisks, sd) { if (!strncmp(s->name, name, GV_MAXSDNAME)) return (s); } return (NULL); } /* Find a drive by name. */ struct gv_drive * gv_find_drive(struct gv_softc *sc, char *name) { struct gv_drive *d; LIST_FOREACH(d, &sc->drives, drive) { if (!strncmp(d->name, name, GV_MAXDRIVENAME)) return (d); } return (NULL); } /* Check if any consumer of the given geom is open. */ int gv_is_open(struct g_geom *gp) { struct g_consumer *cp; if (gp == NULL) return (0); LIST_FOREACH(cp, &gp->consumer, consumer) { if (cp->acr || cp->acw || cp->ace) return (1); } return (0); } /* Return the type of object identified by string 'name'. */ int gv_object_type(struct gv_softc *sc, char *name) { struct gv_drive *d; struct gv_plex *p; struct gv_sd *s; struct gv_volume *v; LIST_FOREACH(v, &sc->volumes, volume) { if (!strncmp(v->name, name, GV_MAXVOLNAME)) return (GV_TYPE_VOL); } LIST_FOREACH(p, &sc->plexes, plex) { if (!strncmp(p->name, name, GV_MAXPLEXNAME)) return (GV_TYPE_PLEX); } LIST_FOREACH(s, &sc->subdisks, sd) { if (!strncmp(s->name, name, GV_MAXSDNAME)) return (GV_TYPE_SD); } LIST_FOREACH(d, &sc->drives, drive) { if (!strncmp(d->name, name, GV_MAXDRIVENAME)) return (GV_TYPE_DRIVE); } return (-1); } void gv_kill_thread(struct gv_plex *p) { if ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_THREAD_ACTIVE)) { p->flags |= GV_PLEX_THREAD_DIE; wakeup(p); while (!(p->flags & GV_PLEX_THREAD_DEAD)) tsleep(p, PRIBIO, "gv_die", hz); p->flags &= ~GV_PLEX_THREAD_ACTIVE; } }