diff options
author | lulf <lulf@FreeBSD.org> | 2009-03-28 21:06:59 +0000 |
---|---|---|
committer | lulf <lulf@FreeBSD.org> | 2009-03-28 21:06:59 +0000 |
commit | 4b994f250d069744dacf99e4dafde0bc5f2a2606 (patch) | |
tree | 52dc3f20e5e91289e9c2a9b46dc9d948aa3206a3 /sys/geom/vinum | |
parent | f13fbbd88157cee0db8034356602dc2095594794 (diff) | |
download | FreeBSD-src-4b994f250d069744dacf99e4dafde0bc5f2a2606.zip FreeBSD-src-4b994f250d069744dacf99e4dafde0bc5f2a2606.tar.gz |
- Add files that should have been added in r190507.
Diffstat (limited to 'sys/geom/vinum')
-rw-r--r-- | sys/geom/vinum/geom_vinum_create.c | 614 | ||||
-rw-r--r-- | sys/geom/vinum/geom_vinum_events.c | 217 |
2 files changed, 831 insertions, 0 deletions
diff --git a/sys/geom/vinum/geom_vinum_create.c b/sys/geom/vinum/geom_vinum_create.c new file mode 100644 index 0000000..3f8212c --- /dev/null +++ b/sys/geom/vinum/geom_vinum_create.c @@ -0,0 +1,614 @@ +/*- + * Copyright (c) 2007 Lukas Ertl + * Copyright (c) 2007, 2009 Ulf Lilleengen + * 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 THE 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 THE 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bio.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/vimage.h> + +#include <geom/geom.h> +#include <geom/vinum/geom_vinum_var.h> +#include <geom/vinum/geom_vinum.h> + +#define DEFAULT_STRIPESIZE 262144 + +/* + * Create a new drive object, either by user request, during taste of the drive + * itself, or because it was referenced by a subdisk during taste. + */ +int +gv_create_drive(struct gv_softc *sc, struct gv_drive *d) +{ + struct g_geom *gp; + struct g_provider *pp; + struct g_consumer *cp, *cp2; + struct gv_drive *d2; + struct gv_hdr *hdr; + struct gv_freelist *fl; + + KASSERT(d != NULL, ("gv_create_drive: NULL d")); + + gp = sc->geom; + + pp = NULL; + cp = cp2 = NULL; + + /* The drive already has a consumer if it was tasted before. */ + if (d->consumer != NULL) { + cp = d->consumer; + cp->private = d; + pp = cp->provider; + } else if (!(d->flags & GV_DRIVE_REFERENCED)) { + if (gv_find_drive(sc, d->name) != NULL) { + G_VINUM_DEBUG(0, "drive '%s' already exists", d->name); + g_free(d); + return (GV_ERR_CREATE); + } + + if (gv_find_drive_device(sc, d->device) != NULL) { + G_VINUM_DEBUG(0, "provider '%s' already in use by " + "gvinum", d->device); + return (GV_ERR_CREATE); + } + + pp = g_provider_by_name(d->device); + if (pp == NULL) { + G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared", + d->name, d->device); + g_free(d); + return (GV_ERR_CREATE); + } + + g_topology_lock(); + cp = g_new_consumer(gp); + if (g_attach(cp, pp) != 0) { + g_destroy_consumer(cp); + g_topology_unlock(); + G_VINUM_DEBUG(0, "create drive '%s': couldn't attach", + d->name); + g_free(d); + return (GV_ERR_CREATE); + } + g_topology_unlock(); + + d->consumer = cp; + cp->private = d; + } + + /* + * If this was just a "referenced" drive, we're almost finished, but + * insert this drive not on the head of the drives list, as + * gv_drive_is_newer() expects a "real" drive from LIST_FIRST(). + */ + if (d->flags & GV_DRIVE_REFERENCED) { + snprintf(d->device, sizeof(d->device), "???"); + d2 = LIST_FIRST(&sc->drives); + if (d2 == NULL) + LIST_INSERT_HEAD(&sc->drives, d, drive); + else + LIST_INSERT_AFTER(d2, d, drive); + return (0); + } + + /* + * Update access counts of the new drive to those of an already + * existing drive. + */ + LIST_FOREACH(d2, &sc->drives, drive) { + if ((d == d2) || (d2->consumer == NULL)) + continue; + + cp2 = d2->consumer; + g_topology_lock(); + if ((cp2->acr || cp2->acw || cp2->ace) && + (g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) { + g_detach(cp); + g_destroy_consumer(cp); + g_topology_unlock(); + G_VINUM_DEBUG(0, "create drive '%s': couldn't update " + "access counts", d->name); + if (d->hdr != NULL) + g_free(d->hdr); + g_free(d); + return (GV_ERR_CREATE); + } + g_topology_unlock(); + break; + } + + d->size = pp->mediasize - GV_DATA_START; + d->avail = d->size; + d->vinumconf = sc; + LIST_INIT(&d->subdisks); + LIST_INIT(&d->freelist); + + /* The header might have been set during taste. */ + if (d->hdr == NULL) { + hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO); + hdr->magic = GV_MAGIC; + hdr->config_length = GV_CFG_LEN; + mtx_lock(&hostname_mtx); + bcopy(G_hostname, hdr->label.sysname, GV_HOSTNAME_LEN); + mtx_unlock(&hostname_mtx); + strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name)); + microtime(&hdr->label.date_of_birth); + d->hdr = hdr; + } + + /* We also need a freelist entry. */ + 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; + + if (gv_find_drive(sc, d->name) == NULL) + LIST_INSERT_HEAD(&sc->drives, d, drive); + + gv_set_drive_state(d, GV_DRIVE_UP, 0); + return (0); +} + +int +gv_create_volume(struct gv_softc *sc, struct gv_volume *v) +{ + KASSERT(v != NULL, ("gv_create_volume: NULL v")); + + v->vinumconf = sc; + v->flags |= GV_VOL_NEWBORN; + LIST_INIT(&v->plexes); + LIST_INSERT_HEAD(&sc->volumes, v, volume); + v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); + bioq_init(v->wqueue); + return (0); +} + +int +gv_create_plex(struct gv_softc *sc, struct gv_plex *p) +{ + struct gv_volume *v; + + KASSERT(p != NULL, ("gv_create_plex: NULL p")); + + /* Find the volume this plex should be attached to. */ + v = gv_find_vol(sc, p->volume); + if (v == NULL) { + G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found", + p->name, p->volume); + g_free(p); + return (GV_ERR_CREATE); + } + if (!(v->flags & GV_VOL_NEWBORN)) + p->flags |= GV_PLEX_ADDED; + p->vol_sc = v; + v->plexcount++; + p->vinumconf = sc; + p->synced = 0; + p->flags |= GV_PLEX_NEWBORN; + LIST_INSERT_HEAD(&v->plexes, p, in_volume); + LIST_INIT(&p->subdisks); + TAILQ_INIT(&p->packets); + LIST_INSERT_HEAD(&sc->plexes, p, plex); + p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); + bioq_init(p->bqueue); + p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); + bioq_init(p->wqueue); + p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); + bioq_init(p->rqueue); + return (0); +} + +int +gv_create_sd(struct gv_softc *sc, struct gv_sd *s) +{ + struct gv_plex *p; + struct gv_drive *d; + + KASSERT(s != NULL, ("gv_create_sd: NULL s")); + + /* Find the drive where this subdisk should be put on. */ + d = gv_find_drive(sc, s->drive); + if (d == NULL) { + /* + * It's possible that the subdisk references a drive that + * doesn't exist yet (during the taste process), so create a + * practically empty "referenced" drive. + */ + if (s->flags & GV_SD_TASTED) { + d = g_malloc(sizeof(struct gv_drive), + M_WAITOK | M_ZERO); + d->flags |= GV_DRIVE_REFERENCED; + strlcpy(d->name, s->drive, sizeof(d->name)); + gv_create_drive(sc, d); + } else { + G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found", + s->name, s->drive); + g_free(s); + return (GV_ERR_CREATE); + } + } + + /* Find the plex where this subdisk belongs to. */ + p = gv_find_plex(sc, s->plex); + if (p == NULL) { + G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found", + s->name, s->plex); + g_free(s); + return (GV_ERR_CREATE); + } + + /* + * First we give the subdisk to the drive, to handle autosized + * values ... + */ + if (gv_sd_to_drive(s, d) != 0) { + g_free(s); + return (GV_ERR_CREATE); + } + + /* + * Then, we give the subdisk to the plex; we check if the + * given values are correct and maybe adjust them. + */ + if (gv_sd_to_plex(s, p) != 0) { + G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'", + s->name, p->name); + if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED)) + LIST_REMOVE(s, from_drive); + gv_free_sd(s); + g_free(s); + /* + * If this subdisk can't be created, we won't create + * the attached plex either, if it is also a new one. + */ + if (!(p->flags & GV_PLEX_NEWBORN)) + return (GV_ERR_CREATE); + gv_rm_plex(sc, p); + return (GV_ERR_CREATE); + } + s->flags |= GV_SD_NEWBORN; + + s->vinumconf = sc; + LIST_INSERT_HEAD(&sc->subdisks, s, sd); + + return (0); +} + +/* + * Create a concatenated volume from specified drives or drivegroups. + */ +void +gv_concat(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_drive *d; + struct gv_sd *s; + struct gv_volume *v; + struct gv_plex *p; + struct gv_softc *sc; + char *drive, buf[30], *vol; + int *drives, *flags, dcount; + + sc = gp->softc; + dcount = 0; + vol = gctl_get_param(req, "name", NULL); + if (vol == NULL) { + gctl_error(req, "volume names not given"); + return; + } + + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + + if (drives == NULL) { + gctl_error(req, "drive names not given"); + return; + } + + /* First we create the volume. */ + v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); + strlcpy(v->name, vol, sizeof(v->name)); + v->state = GV_VOL_UP; + gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); + + /* Then we create the plex. */ + p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); + snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); + strlcpy(p->volume, v->name, sizeof(p->volume)); + p->org = GV_PLEX_CONCAT; + p->stripesize = 0; + gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); + + /* Drives are first (right now) priority */ + for (dcount = 0; dcount < *drives; dcount++) { + snprintf(buf, sizeof(buf), "drive%d", dcount); + drive = gctl_get_param(req, buf, NULL); + d = gv_find_drive(sc, drive); + if (d == NULL) { + gctl_error(req, "No such drive '%s'", drive); + continue; + } + s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); + snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); + strlcpy(s->plex, p->name, sizeof(s->plex)); + strlcpy(s->drive, drive, sizeof(s->drive)); + s->plex_offset = -1; + s->drive_offset = -1; + s->size = -1; + gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); + } + gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); + gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); +} + +/* + * Create a mirrored volume from specified drives or drivegroups. + */ +void +gv_mirror(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_drive *d; + struct gv_sd *s; + struct gv_volume *v; + struct gv_plex *p; + struct gv_softc *sc; + char *drive, buf[30], *vol; + int *drives, *flags, dcount, pcount, scount; + + sc = gp->softc; + dcount = 0; + scount = 0; + pcount = 0; + vol = gctl_get_param(req, "name", NULL); + if (vol == NULL) { + gctl_error(req, "volume's not given"); + return; + } + + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + + if (drives == NULL) { + gctl_error(req, "drives not given"); + return; + } + + /* We must have an even number of drives. */ + if (*drives % 2 != 0) { + gctl_error(req, "mirror organization must have an even number " + "of drives"); + return; + } + if (*flags & GV_FLAG_S && *drives < 4) { + gctl_error(req, "must have at least 4 drives for striped plex"); + return; + } + + /* First we create the volume. */ + v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); + strlcpy(v->name, vol, sizeof(v->name)); + v->state = GV_VOL_UP; + gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); + + /* Then we create the plexes. */ + for (pcount = 0; pcount < 2; pcount++) { + p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); + snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, + pcount); + strlcpy(p->volume, v->name, sizeof(p->volume)); + if (*flags & GV_FLAG_S) { + p->org = GV_PLEX_STRIPED; + p->stripesize = DEFAULT_STRIPESIZE; + } else { + p->org = GV_PLEX_CONCAT; + p->stripesize = -1; + } + gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); + + /* + * We just gives each even drive to plex one, and each odd to + * plex two. + */ + scount = 0; + for (dcount = pcount; dcount < *drives; dcount += 2) { + snprintf(buf, sizeof(buf), "drive%d", dcount); + drive = gctl_get_param(req, buf, NULL); + d = gv_find_drive(sc, drive); + if (d == NULL) { + gctl_error(req, "No such drive '%s', aborting", + drive); + scount++; + break; + } + s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); + snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, + scount); + strlcpy(s->plex, p->name, sizeof(s->plex)); + strlcpy(s->drive, drive, sizeof(s->drive)); + s->plex_offset = -1; + s->drive_offset = -1; + s->size = -1; + gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); + scount++; + } + } + gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); + gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); +} + +void +gv_raid5(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_softc *sc; + struct gv_drive *d; + struct gv_volume *v; + struct gv_plex *p; + struct gv_sd *s; + int *drives, *flags, dcount; + char *vol, *drive, buf[30]; + off_t *stripesize; + + dcount = 0; + sc = gp->softc; + + vol = gctl_get_param(req, "name", NULL); + if (vol == NULL) { + gctl_error(req, "volume's not given"); + return; + } + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize)); + + if (stripesize == NULL) { + gctl_error(req, "no stripesize given"); + return; + } + + if (drives == NULL) { + gctl_error(req, "drives not given"); + return; + } + + /* We must have at least three drives. */ + if (*drives < 3) { + gctl_error(req, "must have at least three drives for this " + "plex organisation"); + return; + } + /* First we create the volume. */ + v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); + strlcpy(v->name, vol, sizeof(v->name)); + v->state = GV_VOL_UP; + gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); + + /* Then we create the plex. */ + p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); + snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); + strlcpy(p->volume, v->name, sizeof(p->volume)); + p->org = GV_PLEX_RAID5; + p->stripesize = *stripesize; + gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); + + /* Create subdisks on drives. */ + for (dcount = 0; dcount < *drives; dcount++) { + snprintf(buf, sizeof(buf), "drive%d", dcount); + drive = gctl_get_param(req, buf, NULL); + d = gv_find_drive(sc, drive); + if (d == NULL) { + gctl_error(req, "No such drive '%s'", drive); + continue; + } + s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); + snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); + strlcpy(s->plex, p->name, sizeof(s->plex)); + strlcpy(s->drive, drive, sizeof(s->drive)); + s->plex_offset = -1; + s->drive_offset = -1; + s->size = -1; + gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); + } + gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); + gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); +} + +/* + * Create a striped volume from specified drives or drivegroups. + */ +void +gv_stripe(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_drive *d; + struct gv_sd *s; + struct gv_volume *v; + struct gv_plex *p; + struct gv_softc *sc; + char *drive, buf[30], *vol; + int *drives, *flags, dcount, pcount; + + sc = gp->softc; + dcount = 0; + pcount = 0; + vol = gctl_get_param(req, "name", NULL); + if (vol == NULL) { + gctl_error(req, "volume's not given"); + return; + } + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + + if (drives == NULL) { + gctl_error(req, "drives not given"); + return; + } + + /* We must have at least two drives. */ + if (*drives < 2) { + gctl_error(req, "must have at least 2 drives"); + return; + } + + /* First we create the volume. */ + v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); + strlcpy(v->name, vol, sizeof(v->name)); + v->state = GV_VOL_UP; + gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); + + /* Then we create the plex. */ + p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); + snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); + strlcpy(p->volume, v->name, sizeof(p->volume)); + p->org = GV_PLEX_STRIPED; + p->stripesize = 262144; + gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); + + /* Create subdisks on drives. */ + for (dcount = 0; dcount < *drives; dcount++) { + snprintf(buf, sizeof(buf), "drive%d", dcount); + drive = gctl_get_param(req, buf, NULL); + d = gv_find_drive(sc, drive); + if (d == NULL) { + gctl_error(req, "No such drive '%s'", drive); + continue; + } + s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); + snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); + strlcpy(s->plex, p->name, sizeof(s->plex)); + strlcpy(s->drive, drive, sizeof(s->drive)); + s->plex_offset = -1; + s->drive_offset = -1; + s->size = -1; + gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); + } + gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); + gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); +} diff --git a/sys/geom/vinum/geom_vinum_events.c b/sys/geom/vinum/geom_vinum_events.c new file mode 100644 index 0000000..ce7fec0 --- /dev/null +++ b/sys/geom/vinum/geom_vinum_events.c @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2007 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/systm.h> + +#include <geom/geom.h> +#include <geom/vinum/geom_vinum_var.h> +#include <geom/vinum/geom_vinum.h> + +void +gv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2, + intmax_t arg3, intmax_t arg4) +{ + struct gv_event *ev; + + ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); + ev->type = event; + ev->arg1 = arg1; + ev->arg2 = arg2; + ev->arg3 = arg3; + ev->arg4 = arg4; + + mtx_lock(&sc->queue_mtx); + TAILQ_INSERT_TAIL(&sc->equeue, ev, events); + wakeup(sc); + mtx_unlock(&sc->queue_mtx); +} + +void +gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp) +{ + struct g_geom *gp; + struct g_consumer *cp; + struct gv_hdr *hdr; + struct gv_drive *d; + char *buf; + int error; + + hdr = NULL; + buf = NULL; + + G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name); + + gp = sc->geom; + g_topology_lock(); + cp = g_new_consumer(gp); + if (g_attach(cp, pp) != 0) { + g_destroy_consumer(cp); + g_topology_unlock(); + G_VINUM_DEBUG(0, "failed to attach to provider on taste event"); + return; + } + if (g_access(cp, 1, 0, 0) != 0) { + g_detach(cp); + g_destroy_consumer(cp); + g_topology_unlock(); + G_VINUM_DEBUG(0, "failed to access consumer on taste event"); + return; + } + g_topology_unlock(); + + hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); + /* Read header and on-disk configuration. */ + error = gv_read_header(cp, hdr); + if (error) { + G_VINUM_DEBUG(0, "failed to read header during taste"); + goto failed; + } + + /* + * Setup the drive before we parse the on-disk configuration, so that + * we already know about the drive then. + */ + d = gv_find_drive(sc, hdr->label.name); + if (d == NULL) { + d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); + strlcpy(d->name, hdr->label.name, sizeof(d->name)); + strlcpy(d->device, pp->name, sizeof(d->device)); + } else if (d->flags & GV_DRIVE_REFERENCED) { + strlcpy(d->device, pp->name, sizeof(d->device)); + d->flags &= ~GV_DRIVE_REFERENCED; + } else { + G_VINUM_DEBUG(2, "drive '%s' is already known", d->name); + goto failed; + } + + /* Add the consumer and header to the new drive. */ + d->consumer = cp; + d->hdr = hdr; + gv_create_drive(sc, d); + + buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL); + if (buf == NULL) { + G_VINUM_DEBUG(0, "failed to read config during taste"); + goto failed; + } + gv_parse_config(sc, buf, d); + g_free(buf); + + g_topology_lock(); + g_access(cp, -1, 0, 0); + g_topology_unlock(); + + gv_setup_objects(sc); + gv_set_drive_state(d, GV_DRIVE_UP, 0); + + return; + +failed: + if (hdr != NULL) + g_free(hdr); + g_topology_lock(); + g_access(cp, -1, 0, 0); + g_detach(cp); + g_destroy_consumer(cp); + g_topology_unlock(); +} + +/* + * When losing a drive (e.g. hardware failure), we cut down the consumer + * attached to the underlying device and bring the drive itself to a + * "referenced" state so that normal tasting could bring it up cleanly if it + * possibly arrives again. + */ +void +gv_drive_lost(struct gv_softc *sc, struct gv_drive *d) +{ + struct g_consumer *cp; + struct gv_drive *d2; + struct gv_sd *s, *s2; + struct gv_freelist *fl, *fl2; + + gv_set_drive_state(d, GV_DRIVE_DOWN, + GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); + + cp = d->consumer; + + if (cp != NULL) { + if (cp->nstart != cp->nend) { + G_VINUM_DEBUG(0, "dead drive '%s' has still active " + "requests, can't detach consumer", d->name); + gv_post_event(sc, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0); + return; + } + g_topology_lock(); + if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) + g_access(cp, -cp->acr, -cp->acw, -cp->ace); + g_detach(cp); + g_destroy_consumer(cp); + g_topology_unlock(); + } + + LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { + LIST_REMOVE(fl, freelist); + g_free(fl); + } + + d->consumer = NULL; + g_free(d->hdr); + d->hdr = NULL; + d->flags |= GV_DRIVE_REFERENCED; + snprintf(d->device, sizeof(d->device), "???"); + d->size = 0; + d->avail = 0; + d->freelist_entries = 0; + d->sdcount = 0; + + /* Put the subdisk in tasted mode, and remove from drive list. */ + LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { + LIST_REMOVE(s, from_drive); + s->flags |= GV_SD_TASTED; + } + + /* + * Don't forget that gv_is_newer wants a "real" drive at the beginning + * of the list, so, just to be safe, we shuffle around. + */ + LIST_REMOVE(d, drive); + d2 = LIST_FIRST(&sc->drives); + if (d2 == NULL) + LIST_INSERT_HEAD(&sc->drives, d, drive); + else + LIST_INSERT_AFTER(d2, d, drive); + gv_save_config(sc); +} |