summaryrefslogtreecommitdiffstats
path: root/sys/geom/vinum/geom_vinum.c
diff options
context:
space:
mode:
authorle <le@FreeBSD.org>2004-06-12 21:16:10 +0000
committerle <le@FreeBSD.org>2004-06-12 21:16:10 +0000
commitcf31d52b42bd2309bb855b34e8260283eabfc570 (patch)
treeb37e9b83eff28125aba7f626ab2e3bea5b487658 /sys/geom/vinum/geom_vinum.c
parentf66d897510d4772f7c5efd834cd66203558e9cb5 (diff)
downloadFreeBSD-src-cf31d52b42bd2309bb855b34e8260283eabfc570.zip
FreeBSD-src-cf31d52b42bd2309bb855b34e8260283eabfc570.tar.gz
Add a first version of a GEOMified vinum.
Diffstat (limited to 'sys/geom/vinum/geom_vinum.c')
-rw-r--r--sys/geom/vinum/geom_vinum.c569
1 files changed, 569 insertions, 0 deletions
diff --git a/sys/geom/vinum/geom_vinum.c b/sys/geom/vinum/geom_vinum.c
new file mode 100644
index 0000000..44a8061
--- /dev/null
+++ b/sys/geom/vinum/geom_vinum.c
@@ -0,0 +1,569 @@
+/*
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.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>
+#include <geom/vinum/geom_vinum_share.h>
+
+#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));
+
+ /* XXX */
+ pp = g_provider_by_name(d->device);
+ 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;
+ 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 */
+ pp = g_provider_by_name(d->device);
+ 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_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);
+ }
+
+ LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
+ 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;
+ }
+ LIST_REMOVE(d, drive);
+ }
+
+ g_free(sc);
+ sc = NULL;
+ 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);
OpenPOWER on IntegriCloud