summaryrefslogtreecommitdiffstats
path: root/sys/geom
diff options
context:
space:
mode:
authorlulf <lulf@FreeBSD.org>2009-03-28 21:06:59 +0000
committerlulf <lulf@FreeBSD.org>2009-03-28 21:06:59 +0000
commit4b994f250d069744dacf99e4dafde0bc5f2a2606 (patch)
tree52dc3f20e5e91289e9c2a9b46dc9d948aa3206a3 /sys/geom
parentf13fbbd88157cee0db8034356602dc2095594794 (diff)
downloadFreeBSD-src-4b994f250d069744dacf99e4dafde0bc5f2a2606.zip
FreeBSD-src-4b994f250d069744dacf99e4dafde0bc5f2a2606.tar.gz
- Add files that should have been added in r190507.
Diffstat (limited to 'sys/geom')
-rw-r--r--sys/geom/vinum/geom_vinum_create.c614
-rw-r--r--sys/geom/vinum/geom_vinum_events.c217
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);
+}
OpenPOWER on IntegriCloud