1139778Simp/*- 2190507Slulf * Copyright (c) 2004, 2007 Lukas Ertl 3190507Slulf * Copyright (c) 2007, 2009 Ulf Lilleengen 4130389Sle * All rights reserved. 5130389Sle * 6130389Sle * Redistribution and use in source and binary forms, with or without 7130389Sle * modification, are permitted provided that the following conditions 8130389Sle * are met: 9130389Sle * 1. Redistributions of source code must retain the above copyright 10130389Sle * notice, this list of conditions and the following disclaimer. 11130389Sle * 2. Redistributions in binary form must reproduce the above copyright 12130389Sle * notice, this list of conditions and the following disclaimer in the 13130389Sle * documentation and/or other materials provided with the distribution. 14130389Sle * 15130389Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16130389Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17130389Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18130389Sle * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19130389Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20130389Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21130389Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22130389Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23130389Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24130389Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25130389Sle * SUCH DAMAGE. 26130389Sle * 27130389Sle */ 28130389Sle 29130389Sle#include <sys/cdefs.h> 30130389Sle__FBSDID("$FreeBSD$"); 31130389Sle 32130389Sle#include <sys/param.h> 33130389Sle#include <sys/bio.h> 34130389Sle#include <sys/kernel.h> 35190507Slulf#include <sys/kthread.h> 36130389Sle#include <sys/lock.h> 37130389Sle#include <sys/malloc.h> 38130389Sle#include <sys/module.h> 39130389Sle#include <sys/mutex.h> 40223921Sae#include <sys/sbuf.h> 41184292Slulf#include <sys/sysctl.h> 42130389Sle#include <sys/systm.h> 43130389Sle 44130389Sle#include <geom/geom.h> 45130389Sle#include <geom/vinum/geom_vinum_var.h> 46130389Sle#include <geom/vinum/geom_vinum.h> 47190507Slulf#include <geom/vinum/geom_vinum_raid5.h> 48130389Sle 49130389SleSYSCTL_DECL(_kern_geom); 50227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, 51227309Sed "GEOM_VINUM stuff"); 52184292Slulfu_int g_vinum_debug = 0; 53184292SlulfTUNABLE_INT("kern.geom.vinum.debug", &g_vinum_debug); 54184292SlulfSYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &g_vinum_debug, 0, 55130389Sle "Debug level"); 56130389Sle 57190507Slulfstatic int gv_create(struct g_geom *, struct gctl_req *); 58190507Slulfstatic void gv_attach(struct gv_softc *, struct gctl_req *); 59190507Slulfstatic void gv_detach(struct gv_softc *, struct gctl_req *); 60190507Slulfstatic void gv_parityop(struct gv_softc *, struct gctl_req *); 61130389Sle 62190507Slulf 63130389Slestatic void 64130389Slegv_orphan(struct g_consumer *cp) 65130389Sle{ 66130389Sle struct g_geom *gp; 67130389Sle struct gv_softc *sc; 68190507Slulf struct gv_drive *d; 69130389Sle 70130389Sle g_topology_assert(); 71130389Sle 72130389Sle KASSERT(cp != NULL, ("gv_orphan: null cp")); 73130389Sle gp = cp->geom; 74130389Sle KASSERT(gp != NULL, ("gv_orphan: null gp")); 75130389Sle sc = gp->softc; 76190507Slulf KASSERT(sc != NULL, ("gv_orphan: null sc")); 77190507Slulf d = cp->private; 78190507Slulf KASSERT(d != NULL, ("gv_orphan: null d")); 79130389Sle 80130389Sle g_trace(G_T_TOPOLOGY, "gv_orphan(%s)", gp->name); 81130389Sle 82190507Slulf gv_post_event(sc, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0); 83130389Sle} 84130389Sle 85190507Slulfvoid 86130389Slegv_start(struct bio *bp) 87130389Sle{ 88130389Sle struct g_geom *gp; 89190507Slulf struct gv_softc *sc; 90130389Sle 91130389Sle gp = bp->bio_to->geom; 92190507Slulf sc = gp->softc; 93190507Slulf 94190507Slulf switch (bp->bio_cmd) { 95130389Sle case BIO_READ: 96130389Sle case BIO_WRITE: 97130389Sle case BIO_DELETE: 98190507Slulf break; 99190507Slulf case BIO_GETATTR: 100130389Sle default: 101130389Sle g_io_deliver(bp, EOPNOTSUPP); 102130389Sle return; 103130389Sle } 104191856Slulf mtx_lock(&sc->bqueue_mtx); 105191856Slulf bioq_disksort(sc->bqueue_down, bp); 106191856Slulf wakeup(sc); 107191856Slulf mtx_unlock(&sc->bqueue_mtx); 108130389Sle} 109130389Sle 110190507Slulfvoid 111190507Slulfgv_done(struct bio *bp) 112190507Slulf{ 113190507Slulf struct g_geom *gp; 114190507Slulf struct gv_softc *sc; 115190507Slulf 116190507Slulf KASSERT(bp != NULL, ("NULL bp")); 117190507Slulf 118190507Slulf gp = bp->bio_from->geom; 119190507Slulf sc = gp->softc; 120190507Slulf 121191856Slulf mtx_lock(&sc->bqueue_mtx); 122191856Slulf bioq_disksort(sc->bqueue_up, bp); 123191856Slulf wakeup(sc); 124191856Slulf mtx_unlock(&sc->bqueue_mtx); 125190507Slulf} 126190507Slulf 127190507Slulfint 128130389Slegv_access(struct g_provider *pp, int dr, int dw, int de) 129130389Sle{ 130130389Sle struct g_geom *gp; 131190507Slulf struct gv_softc *sc; 132190507Slulf struct gv_drive *d, *d2; 133130389Sle int error; 134130389Sle 135130389Sle gp = pp->geom; 136190507Slulf sc = gp->softc; 137195752Slulf /* 138195752Slulf * We want to modify the read count with the write count in case we have 139195752Slulf * plexes in a RAID-5 organization. 140195752Slulf */ 141195752Slulf dr += dw; 142195752Slulf 143190507Slulf LIST_FOREACH(d, &sc->drives, drive) { 144190507Slulf if (d->consumer == NULL) 145190507Slulf continue; 146190507Slulf error = g_access(d->consumer, dr, dw, de); 147190507Slulf if (error) { 148190507Slulf LIST_FOREACH(d2, &sc->drives, drive) { 149190507Slulf if (d == d2) 150190507Slulf break; 151190507Slulf g_access(d2->consumer, -dr, -dw, -de); 152190507Slulf } 153190507Slulf G_VINUM_DEBUG(0, "g_access '%s' failed: %d", d->name, 154190507Slulf error); 155190507Slulf return (error); 156190507Slulf } 157190507Slulf } 158190507Slulf return (0); 159130389Sle} 160130389Sle 161152773Slestatic void 162152773Slegv_init(struct g_class *mp) 163130389Sle{ 164130389Sle struct g_geom *gp; 165130389Sle struct gv_softc *sc; 166130389Sle 167152773Sle g_trace(G_T_TOPOLOGY, "gv_init(%p)", mp); 168130389Sle 169152773Sle gp = g_new_geomf(mp, "VINUM"); 170152773Sle gp->spoiled = gv_orphan; 171152773Sle gp->orphan = gv_orphan; 172152773Sle gp->access = gv_access; 173152773Sle gp->start = gv_start; 174152773Sle gp->softc = g_malloc(sizeof(struct gv_softc), M_WAITOK | M_ZERO); 175152773Sle sc = gp->softc; 176152773Sle sc->geom = gp; 177191856Slulf sc->bqueue_down = g_malloc(sizeof(struct bio_queue_head), 178191856Slulf M_WAITOK | M_ZERO); 179191856Slulf sc->bqueue_up = g_malloc(sizeof(struct bio_queue_head), 180191856Slulf M_WAITOK | M_ZERO); 181191856Slulf bioq_init(sc->bqueue_down); 182191856Slulf bioq_init(sc->bqueue_up); 183152773Sle LIST_INIT(&sc->drives); 184152773Sle LIST_INIT(&sc->subdisks); 185152773Sle LIST_INIT(&sc->plexes); 186152773Sle LIST_INIT(&sc->volumes); 187190507Slulf TAILQ_INIT(&sc->equeue); 188190507Slulf mtx_init(&sc->config_mtx, "gv_config", NULL, MTX_DEF); 189191849Slulf mtx_init(&sc->equeue_mtx, "gv_equeue", NULL, MTX_DEF); 190191849Slulf mtx_init(&sc->bqueue_mtx, "gv_bqueue", NULL, MTX_DEF); 191207878Sjh kproc_create(gv_worker, sc, &sc->worker, 0, 0, "gv_worker"); 192130389Sle} 193130389Sle 194190507Slulfstatic int 195190507Slulfgv_unload(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 196190507Slulf{ 197190507Slulf struct gv_softc *sc; 198190507Slulf 199190507Slulf g_trace(G_T_TOPOLOGY, "gv_unload(%p)", mp); 200190507Slulf 201190507Slulf g_topology_assert(); 202190507Slulf sc = gp->softc; 203190507Slulf 204190507Slulf if (sc != NULL) { 205207878Sjh gv_worker_exit(sc); 206190507Slulf gp->softc = NULL; 207190507Slulf g_wither_geom(gp, ENXIO); 208190507Slulf } 209190507Slulf 210190507Slulf return (0); 211190507Slulf} 212190507Slulf 213190507Slulf/* Handle userland request of attaching object. */ 214190507Slulfstatic void 215190507Slulfgv_attach(struct gv_softc *sc, struct gctl_req *req) 216190507Slulf{ 217190507Slulf struct gv_volume *v; 218190507Slulf struct gv_plex *p; 219190507Slulf struct gv_sd *s; 220190507Slulf off_t *offset; 221190507Slulf int *rename, type_child, type_parent; 222190507Slulf char *child, *parent; 223190507Slulf 224190507Slulf child = gctl_get_param(req, "child", NULL); 225190507Slulf if (child == NULL) { 226190507Slulf gctl_error(req, "no child given"); 227190507Slulf return; 228190507Slulf } 229190507Slulf parent = gctl_get_param(req, "parent", NULL); 230190507Slulf if (parent == NULL) { 231190507Slulf gctl_error(req, "no parent given"); 232190507Slulf return; 233190507Slulf } 234190507Slulf offset = gctl_get_paraml(req, "offset", sizeof(*offset)); 235190507Slulf if (offset == NULL) { 236190507Slulf gctl_error(req, "no offset given"); 237190507Slulf return; 238190507Slulf } 239190507Slulf rename = gctl_get_paraml(req, "rename", sizeof(*rename)); 240190507Slulf if (rename == NULL) { 241190507Slulf gctl_error(req, "no rename flag given"); 242190507Slulf return; 243190507Slulf } 244190507Slulf 245190507Slulf type_child = gv_object_type(sc, child); 246190507Slulf type_parent = gv_object_type(sc, parent); 247190507Slulf 248190507Slulf switch (type_child) { 249190507Slulf case GV_TYPE_PLEX: 250190507Slulf if (type_parent != GV_TYPE_VOL) { 251190507Slulf gctl_error(req, "no such volume to attach to"); 252190507Slulf return; 253190507Slulf } 254190507Slulf v = gv_find_vol(sc, parent); 255190507Slulf p = gv_find_plex(sc, child); 256190507Slulf gv_post_event(sc, GV_EVENT_ATTACH_PLEX, p, v, *offset, *rename); 257190507Slulf break; 258190507Slulf case GV_TYPE_SD: 259190507Slulf if (type_parent != GV_TYPE_PLEX) { 260190507Slulf gctl_error(req, "no such plex to attach to"); 261190507Slulf return; 262190507Slulf } 263190507Slulf p = gv_find_plex(sc, parent); 264190507Slulf s = gv_find_sd(sc, child); 265190507Slulf gv_post_event(sc, GV_EVENT_ATTACH_SD, s, p, *offset, *rename); 266190507Slulf break; 267190507Slulf default: 268190507Slulf gctl_error(req, "invalid child type"); 269190507Slulf break; 270190507Slulf } 271190507Slulf} 272190507Slulf 273190507Slulf/* Handle userland request of detaching object. */ 274190507Slulfstatic void 275190507Slulfgv_detach(struct gv_softc *sc, struct gctl_req *req) 276190507Slulf{ 277190507Slulf struct gv_plex *p; 278190507Slulf struct gv_sd *s; 279190507Slulf int *flags, type; 280190507Slulf char *object; 281190507Slulf 282190507Slulf object = gctl_get_param(req, "object", NULL); 283190507Slulf if (object == NULL) { 284190507Slulf gctl_error(req, "no argument given"); 285190507Slulf return; 286190507Slulf } 287190507Slulf 288190507Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 289190507Slulf type = gv_object_type(sc, object); 290190507Slulf switch (type) { 291190507Slulf case GV_TYPE_PLEX: 292190507Slulf p = gv_find_plex(sc, object); 293190507Slulf gv_post_event(sc, GV_EVENT_DETACH_PLEX, p, NULL, *flags, 0); 294190507Slulf break; 295190507Slulf case GV_TYPE_SD: 296190507Slulf s = gv_find_sd(sc, object); 297190507Slulf gv_post_event(sc, GV_EVENT_DETACH_SD, s, NULL, *flags, 0); 298190507Slulf break; 299190507Slulf default: 300190507Slulf gctl_error(req, "invalid object type"); 301190507Slulf break; 302190507Slulf } 303190507Slulf} 304190507Slulf 305130389Sle/* Handle userland requests for creating new objects. */ 306190507Slulfstatic int 307130389Slegv_create(struct g_geom *gp, struct gctl_req *req) 308130389Sle{ 309130389Sle struct gv_softc *sc; 310130389Sle struct gv_drive *d, *d2; 311130389Sle struct gv_plex *p, *p2; 312130389Sle struct gv_sd *s, *s2; 313130389Sle struct gv_volume *v, *v2; 314130389Sle struct g_provider *pp; 315190507Slulf int error, i, *drives, *flags, *plexes, *subdisks, *volumes; 316190507Slulf char buf[20]; 317130389Sle 318130389Sle g_topology_assert(); 319130389Sle 320130389Sle sc = gp->softc; 321130389Sle 322130389Sle /* Find out how many of each object have been passed in. */ 323130389Sle volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes)); 324130389Sle plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes)); 325130389Sle subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks)); 326130389Sle drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 327185309Slulf if (volumes == NULL || plexes == NULL || subdisks == NULL || 328185309Slulf drives == NULL) { 329185309Slulf gctl_error(req, "number of objects not given"); 330185309Slulf return (-1); 331185309Slulf } 332190507Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 333190507Slulf if (flags == NULL) { 334190507Slulf gctl_error(req, "flags not given"); 335190507Slulf return (-1); 336190507Slulf } 337130389Sle 338130389Sle /* First, handle drive definitions ... */ 339130389Sle for (i = 0; i < *drives; i++) { 340130389Sle snprintf(buf, sizeof(buf), "drive%d", i); 341130389Sle d2 = gctl_get_paraml(req, buf, sizeof(*d2)); 342185309Slulf if (d2 == NULL) { 343185309Slulf gctl_error(req, "no drive definition given"); 344185309Slulf return (-1); 345185309Slulf } 346131015Scsjp /* 347190507Slulf * Make sure that the device specified in the drive config is 348190507Slulf * an active GEOM provider. 349131015Scsjp */ 350190507Slulf pp = g_provider_by_name(d2->device); 351131015Scsjp if (pp == NULL) { 352190507Slulf gctl_error(req, "%s: device not found", d2->device); 353190507Slulf goto error; 354131015Scsjp } 355190507Slulf if (gv_find_drive(sc, d2->name) != NULL) { 356190507Slulf /* Ignore error. */ 357190507Slulf if (*flags & GV_FLAG_F) 358190507Slulf continue; 359190507Slulf gctl_error(req, "drive '%s' already exists", d2->name); 360190507Slulf goto error; 361190507Slulf } 362190507Slulf if (gv_find_drive_device(sc, d2->device) != NULL) { 363190507Slulf gctl_error(req, "device '%s' already configured in " 364190507Slulf "gvinum", d2->device); 365190507Slulf goto error; 366190507Slulf } 367130389Sle 368130389Sle 369190507Slulf d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 370190507Slulf bcopy(d2, d, sizeof(*d)); 371190507Slulf 372190507Slulf gv_post_event(sc, GV_EVENT_CREATE_DRIVE, d, NULL, 0, 0); 373130389Sle } 374130389Sle 375130389Sle /* ... then volume definitions ... */ 376130389Sle for (i = 0; i < *volumes; i++) { 377130389Sle error = 0; 378130389Sle snprintf(buf, sizeof(buf), "volume%d", i); 379130389Sle v2 = gctl_get_paraml(req, buf, sizeof(*v2)); 380185309Slulf if (v2 == NULL) { 381185309Slulf gctl_error(req, "no volume definition given"); 382185309Slulf return (-1); 383185309Slulf } 384190507Slulf if (gv_find_vol(sc, v2->name) != NULL) { 385190507Slulf /* Ignore error. */ 386190507Slulf if (*flags & GV_FLAG_F) 387190507Slulf continue; 388190507Slulf gctl_error(req, "volume '%s' already exists", v2->name); 389190507Slulf goto error; 390130389Sle } 391130389Sle 392130389Sle v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 393130389Sle bcopy(v2, v, sizeof(*v)); 394130389Sle 395190507Slulf gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 396130389Sle } 397130389Sle 398130389Sle /* ... then plex definitions ... */ 399130389Sle for (i = 0; i < *plexes; i++) { 400130389Sle error = 0; 401130389Sle snprintf(buf, sizeof(buf), "plex%d", i); 402130389Sle p2 = gctl_get_paraml(req, buf, sizeof(*p2)); 403185309Slulf if (p2 == NULL) { 404185309Slulf gctl_error(req, "no plex definition given"); 405185309Slulf return (-1); 406185309Slulf } 407190507Slulf if (gv_find_plex(sc, p2->name) != NULL) { 408190507Slulf /* Ignore error. */ 409190507Slulf if (*flags & GV_FLAG_F) 410190507Slulf continue; 411190507Slulf gctl_error(req, "plex '%s' already exists", p2->name); 412190507Slulf goto error; 413130389Sle } 414130389Sle 415130389Sle p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 416130389Sle bcopy(p2, p, sizeof(*p)); 417130389Sle 418190507Slulf gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 419130389Sle } 420130389Sle 421190507Slulf /* ... and, finally, subdisk definitions. */ 422130389Sle for (i = 0; i < *subdisks; i++) { 423130389Sle error = 0; 424130389Sle snprintf(buf, sizeof(buf), "sd%d", i); 425130389Sle s2 = gctl_get_paraml(req, buf, sizeof(*s2)); 426185309Slulf if (s2 == NULL) { 427185309Slulf gctl_error(req, "no subdisk definition given"); 428185309Slulf return (-1); 429185309Slulf } 430190507Slulf if (gv_find_sd(sc, s2->name) != NULL) { 431190507Slulf /* Ignore error. */ 432190507Slulf if (*flags & GV_FLAG_F) 433190507Slulf continue; 434190507Slulf gctl_error(req, "sd '%s' already exists", s2->name); 435190507Slulf goto error; 436130389Sle } 437130389Sle 438130389Sle s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 439130389Sle bcopy(s2, s, sizeof(*s)); 440130389Sle 441190507Slulf gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 442130389Sle } 443130389Sle 444190507Slulferror: 445190507Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 446190507Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 447130389Sle 448130389Sle return (0); 449130389Sle} 450130389Sle 451130389Slestatic void 452130389Slegv_config(struct gctl_req *req, struct g_class *mp, char const *verb) 453130389Sle{ 454130389Sle struct g_geom *gp; 455130389Sle struct gv_softc *sc; 456130389Sle struct sbuf *sb; 457130389Sle char *comment; 458130389Sle 459130389Sle g_topology_assert(); 460130389Sle 461130389Sle gp = LIST_FIRST(&mp->geom); 462130389Sle sc = gp->softc; 463130389Sle 464190507Slulf if (!strcmp(verb, "attach")) { 465190507Slulf gv_attach(sc, req); 466190507Slulf 467190507Slulf } else if (!strcmp(verb, "concat")) { 468190507Slulf gv_concat(gp, req); 469190507Slulf 470190507Slulf } else if (!strcmp(verb, "detach")) { 471190507Slulf gv_detach(sc, req); 472190507Slulf 473190507Slulf } else if (!strcmp(verb, "list")) { 474130389Sle gv_list(gp, req); 475130389Sle 476130389Sle /* Save our configuration back to disk. */ 477130389Sle } else if (!strcmp(verb, "saveconfig")) { 478190507Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 479130389Sle 480130389Sle /* Return configuration in string form. */ 481130389Sle } else if (!strcmp(verb, "getconfig")) { 482130389Sle comment = gctl_get_param(req, "comment", NULL); 483185309Slulf if (comment == NULL) { 484185309Slulf gctl_error(req, "no comment parameter given"); 485185309Slulf return; 486185309Slulf } 487130389Sle sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 488130389Sle gv_format_config(sc, sb, 0, comment); 489130389Sle sbuf_finish(sb); 490130389Sle gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); 491130389Sle sbuf_delete(sb); 492130389Sle 493130389Sle } else if (!strcmp(verb, "create")) { 494130389Sle gv_create(gp, req); 495130389Sle 496190507Slulf } else if (!strcmp(verb, "mirror")) { 497190507Slulf gv_mirror(gp, req); 498190507Slulf 499152615Sle } else if (!strcmp(verb, "move")) { 500152615Sle gv_move(gp, req); 501152615Sle 502190507Slulf } else if (!strcmp(verb, "raid5")) { 503190507Slulf gv_raid5(gp, req); 504138110Sle 505190507Slulf } else if (!strcmp(verb, "rebuildparity") || 506190507Slulf !strcmp(verb, "checkparity")) { 507190507Slulf gv_parityop(sc, req); 508190507Slulf 509130389Sle } else if (!strcmp(verb, "remove")) { 510130389Sle gv_remove(gp, req); 511130389Sle 512152615Sle } else if (!strcmp(verb, "rename")) { 513152615Sle gv_rename(gp, req); 514157052Sle 515157052Sle } else if (!strcmp(verb, "resetconfig")) { 516190507Slulf gv_post_event(sc, GV_EVENT_RESET_CONFIG, sc, NULL, 0, 0); 517152615Sle 518130389Sle } else if (!strcmp(verb, "start")) { 519130389Sle gv_start_obj(gp, req); 520130389Sle 521190507Slulf } else if (!strcmp(verb, "stripe")) { 522190507Slulf gv_stripe(gp, req); 523190507Slulf 524138112Sle } else if (!strcmp(verb, "setstate")) { 525138112Sle gv_setstate(gp, req); 526130389Sle } else 527130389Sle gctl_error(req, "Unknown verb parameter"); 528130389Sle} 529130389Sle 530190507Slulfstatic void 531190507Slulfgv_parityop(struct gv_softc *sc, struct gctl_req *req) 532130389Sle{ 533190507Slulf struct gv_plex *p; 534190507Slulf int *flags, *rebuild, type; 535190507Slulf char *plex; 536130389Sle 537190507Slulf plex = gctl_get_param(req, "plex", NULL); 538190507Slulf if (plex == NULL) { 539190507Slulf gctl_error(req, "no plex given"); 540190507Slulf return; 541190507Slulf } 542130389Sle 543190507Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 544190507Slulf if (flags == NULL) { 545190507Slulf gctl_error(req, "no flags given"); 546190507Slulf return; 547190507Slulf } 548130389Sle 549190507Slulf rebuild = gctl_get_paraml(req, "rebuild", sizeof(*rebuild)); 550190507Slulf if (rebuild == NULL) { 551190507Slulf gctl_error(req, "no operation given"); 552190507Slulf return; 553190507Slulf } 554130389Sle 555190507Slulf type = gv_object_type(sc, plex); 556190507Slulf if (type != GV_TYPE_PLEX) { 557190507Slulf gctl_error(req, "'%s' is not a plex", plex); 558190507Slulf return; 559130389Sle } 560190507Slulf p = gv_find_plex(sc, plex); 561130389Sle 562190507Slulf if (p->state != GV_PLEX_UP) { 563190507Slulf gctl_error(req, "plex %s is not completely accessible", 564190507Slulf p->name); 565190507Slulf return; 566130389Sle } 567130389Sle 568190507Slulf if (p->org != GV_PLEX_RAID5) { 569190507Slulf gctl_error(req, "plex %s is not a RAID5 plex", p->name); 570190507Slulf return; 571130697Sle } 572130697Sle 573190507Slulf /* Put it in the event queue. */ 574190507Slulf /* XXX: The state of the plex might have changed when this event is 575190507Slulf * picked up ... We should perhaps check this afterwards. */ 576190507Slulf if (*rebuild) 577190507Slulf gv_post_event(sc, GV_EVENT_PARITY_REBUILD, p, NULL, 0, 0); 578190507Slulf else 579190507Slulf gv_post_event(sc, GV_EVENT_PARITY_CHECK, p, NULL, 0, 0); 580190507Slulf} 581190507Slulf 582190507Slulf 583190507Slulfstatic struct g_geom * 584190507Slulfgv_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 585190507Slulf{ 586190507Slulf struct g_geom *gp; 587190507Slulf struct g_consumer *cp; 588190507Slulf struct gv_softc *sc; 589190507Slulf struct gv_hdr vhdr; 590190507Slulf int error; 591190507Slulf 592190507Slulf g_topology_assert(); 593190507Slulf g_trace(G_T_TOPOLOGY, "gv_taste(%s, %s)", mp->name, pp->name); 594190507Slulf 595190507Slulf gp = LIST_FIRST(&mp->geom); 596190507Slulf if (gp == NULL) { 597190507Slulf G_VINUM_DEBUG(0, "error: tasting, but not initialized?"); 598190507Slulf return (NULL); 599130697Sle } 600190507Slulf sc = gp->softc; 601130697Sle 602190507Slulf cp = g_new_consumer(gp); 603190507Slulf if (g_attach(cp, pp) != 0) { 604190507Slulf g_destroy_consumer(cp); 605190507Slulf return (NULL); 606130697Sle } 607190507Slulf if (g_access(cp, 1, 0, 0) != 0) { 608190507Slulf g_detach(cp); 609190507Slulf g_destroy_consumer(cp); 610190507Slulf return (NULL); 611190507Slulf } 612190507Slulf g_topology_unlock(); 613130697Sle 614190507Slulf error = gv_read_header(cp, &vhdr); 615190507Slulf 616190507Slulf g_topology_lock(); 617190507Slulf g_access(cp, -1, 0, 0); 618190507Slulf g_detach(cp); 619190507Slulf g_destroy_consumer(cp); 620190507Slulf 621190507Slulf /* Check if what we've been given is a valid vinum drive. */ 622190507Slulf if (!error) 623190507Slulf gv_post_event(sc, GV_EVENT_DRIVE_TASTED, pp, NULL, 0, 0); 624190507Slulf 625190507Slulf return (NULL); 626130389Sle} 627130389Sle 628190507Slulfvoid 629190507Slulfgv_worker(void *arg) 630190507Slulf{ 631190507Slulf struct g_provider *pp; 632190507Slulf struct gv_softc *sc; 633190507Slulf struct gv_event *ev; 634190507Slulf struct gv_volume *v; 635190507Slulf struct gv_plex *p; 636190507Slulf struct gv_sd *s; 637190507Slulf struct gv_drive *d; 638190507Slulf struct bio *bp; 639190507Slulf int newstate, flags, err, rename; 640190507Slulf char *newname; 641190507Slulf off_t offset; 642190507Slulf 643190507Slulf sc = arg; 644190507Slulf KASSERT(sc != NULL, ("NULL sc")); 645190507Slulf for (;;) { 646190507Slulf /* Look at the events first... */ 647191849Slulf ev = gv_get_event(sc); 648190507Slulf if (ev != NULL) { 649191849Slulf gv_remove_event(sc, ev); 650190507Slulf 651190507Slulf switch (ev->type) { 652190507Slulf case GV_EVENT_DRIVE_TASTED: 653190507Slulf G_VINUM_DEBUG(2, "event 'drive tasted'"); 654190507Slulf pp = ev->arg1; 655190507Slulf gv_drive_tasted(sc, pp); 656190507Slulf break; 657190507Slulf 658190507Slulf case GV_EVENT_DRIVE_LOST: 659190507Slulf G_VINUM_DEBUG(2, "event 'drive lost'"); 660190507Slulf d = ev->arg1; 661190507Slulf gv_drive_lost(sc, d); 662190507Slulf break; 663190507Slulf 664190507Slulf case GV_EVENT_CREATE_DRIVE: 665190507Slulf G_VINUM_DEBUG(2, "event 'create drive'"); 666190507Slulf d = ev->arg1; 667190507Slulf gv_create_drive(sc, d); 668190507Slulf break; 669190507Slulf 670190507Slulf case GV_EVENT_CREATE_VOLUME: 671190507Slulf G_VINUM_DEBUG(2, "event 'create volume'"); 672190507Slulf v = ev->arg1; 673190507Slulf gv_create_volume(sc, v); 674190507Slulf break; 675190507Slulf 676190507Slulf case GV_EVENT_CREATE_PLEX: 677190507Slulf G_VINUM_DEBUG(2, "event 'create plex'"); 678190507Slulf p = ev->arg1; 679190507Slulf gv_create_plex(sc, p); 680190507Slulf break; 681190507Slulf 682190507Slulf case GV_EVENT_CREATE_SD: 683190507Slulf G_VINUM_DEBUG(2, "event 'create sd'"); 684190507Slulf s = ev->arg1; 685190507Slulf gv_create_sd(sc, s); 686190507Slulf break; 687190507Slulf 688190507Slulf case GV_EVENT_RM_DRIVE: 689190507Slulf G_VINUM_DEBUG(2, "event 'remove drive'"); 690190507Slulf d = ev->arg1; 691190507Slulf flags = ev->arg3; 692190507Slulf gv_rm_drive(sc, d, flags); 693190507Slulf /*gv_setup_objects(sc);*/ 694190507Slulf break; 695190507Slulf 696190507Slulf case GV_EVENT_RM_VOLUME: 697190507Slulf G_VINUM_DEBUG(2, "event 'remove volume'"); 698190507Slulf v = ev->arg1; 699190507Slulf gv_rm_vol(sc, v); 700190507Slulf /*gv_setup_objects(sc);*/ 701190507Slulf break; 702190507Slulf 703190507Slulf case GV_EVENT_RM_PLEX: 704190507Slulf G_VINUM_DEBUG(2, "event 'remove plex'"); 705190507Slulf p = ev->arg1; 706190507Slulf gv_rm_plex(sc, p); 707190507Slulf /*gv_setup_objects(sc);*/ 708190507Slulf break; 709190507Slulf 710190507Slulf case GV_EVENT_RM_SD: 711190507Slulf G_VINUM_DEBUG(2, "event 'remove sd'"); 712190507Slulf s = ev->arg1; 713190507Slulf gv_rm_sd(sc, s); 714190507Slulf /*gv_setup_objects(sc);*/ 715190507Slulf break; 716190507Slulf 717190507Slulf case GV_EVENT_SAVE_CONFIG: 718190507Slulf G_VINUM_DEBUG(2, "event 'save config'"); 719190507Slulf gv_save_config(sc); 720190507Slulf break; 721190507Slulf 722190507Slulf case GV_EVENT_SET_SD_STATE: 723190507Slulf G_VINUM_DEBUG(2, "event 'setstate sd'"); 724190507Slulf s = ev->arg1; 725190507Slulf newstate = ev->arg3; 726190507Slulf flags = ev->arg4; 727190507Slulf err = gv_set_sd_state(s, newstate, flags); 728190507Slulf if (err) 729190507Slulf G_VINUM_DEBUG(0, "error setting subdisk" 730190507Slulf " state: error code %d", err); 731190507Slulf break; 732190507Slulf 733190507Slulf case GV_EVENT_SET_DRIVE_STATE: 734190507Slulf G_VINUM_DEBUG(2, "event 'setstate drive'"); 735190507Slulf d = ev->arg1; 736190507Slulf newstate = ev->arg3; 737190507Slulf flags = ev->arg4; 738190507Slulf err = gv_set_drive_state(d, newstate, flags); 739190507Slulf if (err) 740190507Slulf G_VINUM_DEBUG(0, "error setting drive " 741190507Slulf "state: error code %d", err); 742190507Slulf break; 743190507Slulf 744190507Slulf case GV_EVENT_SET_VOL_STATE: 745190507Slulf G_VINUM_DEBUG(2, "event 'setstate volume'"); 746190507Slulf v = ev->arg1; 747190507Slulf newstate = ev->arg3; 748190507Slulf flags = ev->arg4; 749190507Slulf err = gv_set_vol_state(v, newstate, flags); 750190507Slulf if (err) 751190507Slulf G_VINUM_DEBUG(0, "error setting volume " 752190507Slulf "state: error code %d", err); 753190507Slulf break; 754190507Slulf 755190507Slulf case GV_EVENT_SET_PLEX_STATE: 756190507Slulf G_VINUM_DEBUG(2, "event 'setstate plex'"); 757190507Slulf p = ev->arg1; 758190507Slulf newstate = ev->arg3; 759190507Slulf flags = ev->arg4; 760190507Slulf err = gv_set_plex_state(p, newstate, flags); 761190507Slulf if (err) 762190507Slulf G_VINUM_DEBUG(0, "error setting plex " 763190507Slulf "state: error code %d", err); 764190507Slulf break; 765190507Slulf 766190507Slulf case GV_EVENT_SETUP_OBJECTS: 767190507Slulf G_VINUM_DEBUG(2, "event 'setup objects'"); 768190507Slulf gv_setup_objects(sc); 769190507Slulf break; 770190507Slulf 771190507Slulf case GV_EVENT_RESET_CONFIG: 772190507Slulf G_VINUM_DEBUG(2, "event 'resetconfig'"); 773190507Slulf err = gv_resetconfig(sc); 774190507Slulf if (err) 775190507Slulf G_VINUM_DEBUG(0, "error resetting " 776190507Slulf "config: error code %d", err); 777190507Slulf break; 778190507Slulf 779190507Slulf case GV_EVENT_PARITY_REBUILD: 780190507Slulf /* 781190507Slulf * Start the rebuild. The gv_plex_done will 782190507Slulf * handle issuing of the remaining rebuild bio's 783190507Slulf * until it's finished. 784190507Slulf */ 785190507Slulf G_VINUM_DEBUG(2, "event 'rebuild'"); 786190507Slulf p = ev->arg1; 787190507Slulf if (p->state != GV_PLEX_UP) { 788190507Slulf G_VINUM_DEBUG(0, "plex %s is not " 789190507Slulf "completely accessible", p->name); 790190507Slulf break; 791190507Slulf } 792204886Slulf if (p->flags & GV_PLEX_SYNCING || 793204886Slulf p->flags & GV_PLEX_REBUILDING || 794204886Slulf p->flags & GV_PLEX_GROWING) { 795204886Slulf G_VINUM_DEBUG(0, "plex %s is busy with " 796204886Slulf "syncing or parity build", p->name); 797204886Slulf break; 798204886Slulf } 799190507Slulf p->synced = 0; 800204886Slulf p->flags |= GV_PLEX_REBUILDING; 801190507Slulf g_topology_assert_not(); 802190507Slulf g_topology_lock(); 803190507Slulf err = gv_access(p->vol_sc->provider, 1, 1, 0); 804190507Slulf if (err) { 805190507Slulf G_VINUM_DEBUG(0, "unable to access " 806190507Slulf "provider"); 807190507Slulf break; 808190507Slulf } 809190507Slulf g_topology_unlock(); 810190507Slulf gv_parity_request(p, GV_BIO_CHECK | 811190507Slulf GV_BIO_PARITY, 0); 812190507Slulf break; 813190507Slulf 814190507Slulf case GV_EVENT_PARITY_CHECK: 815190507Slulf /* Start parity check. */ 816190507Slulf G_VINUM_DEBUG(2, "event 'check'"); 817190507Slulf p = ev->arg1; 818190507Slulf if (p->state != GV_PLEX_UP) { 819190507Slulf G_VINUM_DEBUG(0, "plex %s is not " 820190507Slulf "completely accessible", p->name); 821190507Slulf break; 822190507Slulf } 823204886Slulf if (p->flags & GV_PLEX_SYNCING || 824204886Slulf p->flags & GV_PLEX_REBUILDING || 825204886Slulf p->flags & GV_PLEX_GROWING) { 826204886Slulf G_VINUM_DEBUG(0, "plex %s is busy with " 827204886Slulf "syncing or parity build", p->name); 828204886Slulf break; 829204886Slulf } 830190507Slulf p->synced = 0; 831190507Slulf g_topology_assert_not(); 832190507Slulf g_topology_lock(); 833190507Slulf err = gv_access(p->vol_sc->provider, 1, 1, 0); 834190507Slulf if (err) { 835190507Slulf G_VINUM_DEBUG(0, "unable to access " 836190507Slulf "provider"); 837190507Slulf break; 838190507Slulf } 839190507Slulf g_topology_unlock(); 840190507Slulf gv_parity_request(p, GV_BIO_CHECK, 0); 841190507Slulf break; 842190507Slulf 843190507Slulf case GV_EVENT_START_PLEX: 844190507Slulf G_VINUM_DEBUG(2, "event 'start' plex"); 845190507Slulf p = ev->arg1; 846190507Slulf gv_start_plex(p); 847190507Slulf break; 848190507Slulf 849190507Slulf case GV_EVENT_START_VOLUME: 850190507Slulf G_VINUM_DEBUG(2, "event 'start' volume"); 851190507Slulf v = ev->arg1; 852190507Slulf gv_start_vol(v); 853190507Slulf break; 854190507Slulf 855190507Slulf case GV_EVENT_ATTACH_PLEX: 856190507Slulf G_VINUM_DEBUG(2, "event 'attach' plex"); 857190507Slulf p = ev->arg1; 858190507Slulf v = ev->arg2; 859190507Slulf rename = ev->arg4; 860190507Slulf err = gv_attach_plex(p, v, rename); 861190507Slulf if (err) 862190507Slulf G_VINUM_DEBUG(0, "error attaching %s to" 863190507Slulf " %s: error code %d", p->name, 864190507Slulf v->name, err); 865190507Slulf break; 866190507Slulf 867190507Slulf case GV_EVENT_ATTACH_SD: 868190507Slulf G_VINUM_DEBUG(2, "event 'attach' sd"); 869190507Slulf s = ev->arg1; 870190507Slulf p = ev->arg2; 871190507Slulf offset = ev->arg3; 872190507Slulf rename = ev->arg4; 873190507Slulf err = gv_attach_sd(s, p, offset, rename); 874190507Slulf if (err) 875190507Slulf G_VINUM_DEBUG(0, "error attaching %s to" 876190507Slulf " %s: error code %d", s->name, 877190507Slulf p->name, err); 878190507Slulf break; 879190507Slulf 880190507Slulf case GV_EVENT_DETACH_PLEX: 881190507Slulf G_VINUM_DEBUG(2, "event 'detach' plex"); 882190507Slulf p = ev->arg1; 883190507Slulf flags = ev->arg3; 884190507Slulf err = gv_detach_plex(p, flags); 885190507Slulf if (err) 886190507Slulf G_VINUM_DEBUG(0, "error detaching %s: " 887190507Slulf "error code %d", p->name, err); 888190507Slulf break; 889190507Slulf 890190507Slulf case GV_EVENT_DETACH_SD: 891190507Slulf G_VINUM_DEBUG(2, "event 'detach' sd"); 892190507Slulf s = ev->arg1; 893190507Slulf flags = ev->arg3; 894190507Slulf err = gv_detach_sd(s, flags); 895190507Slulf if (err) 896190507Slulf G_VINUM_DEBUG(0, "error detaching %s: " 897190507Slulf "error code %d", s->name, err); 898190507Slulf break; 899190507Slulf 900190507Slulf case GV_EVENT_RENAME_VOL: 901190507Slulf G_VINUM_DEBUG(2, "event 'rename' volume"); 902190507Slulf v = ev->arg1; 903190507Slulf newname = ev->arg2; 904190507Slulf flags = ev->arg3; 905190507Slulf err = gv_rename_vol(sc, v, newname, flags); 906190507Slulf if (err) 907190507Slulf G_VINUM_DEBUG(0, "error renaming %s to " 908190507Slulf "%s: error code %d", v->name, 909190507Slulf newname, err); 910190507Slulf g_free(newname); 911190507Slulf /* Destroy and recreate the provider if we can. */ 912190507Slulf if (gv_provider_is_open(v->provider)) { 913190507Slulf G_VINUM_DEBUG(0, "unable to rename " 914190507Slulf "provider to %s: provider in use", 915190507Slulf v->name); 916190507Slulf break; 917190507Slulf } 918191248Slulf g_topology_lock(); 919190507Slulf g_wither_provider(v->provider, ENOENT); 920191248Slulf g_topology_unlock(); 921190507Slulf v->provider = NULL; 922190507Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, 923190507Slulf NULL, 0, 0); 924190507Slulf break; 925190507Slulf 926190507Slulf case GV_EVENT_RENAME_PLEX: 927190507Slulf G_VINUM_DEBUG(2, "event 'rename' plex"); 928190507Slulf p = ev->arg1; 929190507Slulf newname = ev->arg2; 930190507Slulf flags = ev->arg3; 931190507Slulf err = gv_rename_plex(sc, p, newname, flags); 932190507Slulf if (err) 933190507Slulf G_VINUM_DEBUG(0, "error renaming %s to " 934190507Slulf "%s: error code %d", p->name, 935190507Slulf newname, err); 936190507Slulf g_free(newname); 937190507Slulf break; 938190507Slulf 939190507Slulf case GV_EVENT_RENAME_SD: 940190507Slulf G_VINUM_DEBUG(2, "event 'rename' sd"); 941190507Slulf s = ev->arg1; 942190507Slulf newname = ev->arg2; 943190507Slulf flags = ev->arg3; 944190507Slulf err = gv_rename_sd(sc, s, newname, flags); 945190507Slulf if (err) 946190507Slulf G_VINUM_DEBUG(0, "error renaming %s to " 947190507Slulf "%s: error code %d", s->name, 948190507Slulf newname, err); 949190507Slulf g_free(newname); 950190507Slulf break; 951190507Slulf 952190507Slulf case GV_EVENT_RENAME_DRIVE: 953190507Slulf G_VINUM_DEBUG(2, "event 'rename' drive"); 954190507Slulf d = ev->arg1; 955190507Slulf newname = ev->arg2; 956190507Slulf flags = ev->arg3; 957190507Slulf err = gv_rename_drive(sc, d, newname, flags); 958190507Slulf if (err) 959190507Slulf G_VINUM_DEBUG(0, "error renaming %s to " 960190507Slulf "%s: error code %d", d->name, 961190507Slulf newname, err); 962190507Slulf g_free(newname); 963190507Slulf break; 964190507Slulf 965190507Slulf case GV_EVENT_MOVE_SD: 966190507Slulf G_VINUM_DEBUG(2, "event 'move' sd"); 967190507Slulf s = ev->arg1; 968190507Slulf d = ev->arg2; 969190507Slulf flags = ev->arg3; 970190507Slulf err = gv_move_sd(sc, s, d, flags); 971190507Slulf if (err) 972190507Slulf G_VINUM_DEBUG(0, "error moving %s to " 973190507Slulf "%s: error code %d", s->name, 974190507Slulf d->name, err); 975190507Slulf break; 976190507Slulf 977190507Slulf case GV_EVENT_THREAD_EXIT: 978190507Slulf G_VINUM_DEBUG(2, "event 'thread exit'"); 979190507Slulf g_free(ev); 980191849Slulf mtx_lock(&sc->equeue_mtx); 981191849Slulf mtx_lock(&sc->bqueue_mtx); 982190507Slulf gv_cleanup(sc); 983191849Slulf mtx_destroy(&sc->bqueue_mtx); 984191849Slulf mtx_destroy(&sc->equeue_mtx); 985191856Slulf g_free(sc->bqueue_down); 986191856Slulf g_free(sc->bqueue_up); 987190507Slulf g_free(sc); 988207878Sjh kproc_exit(0); 989207878Sjh /* NOTREACHED */ 990190507Slulf 991190507Slulf default: 992190507Slulf G_VINUM_DEBUG(1, "unknown event %d", ev->type); 993190507Slulf } 994190507Slulf 995190507Slulf g_free(ev); 996190507Slulf continue; 997190507Slulf } 998190507Slulf 999190507Slulf /* ... then do I/O processing. */ 1000191849Slulf mtx_lock(&sc->bqueue_mtx); 1001191856Slulf /* First do new requests. */ 1002191856Slulf bp = bioq_takefirst(sc->bqueue_down); 1003191856Slulf if (bp != NULL) { 1004191856Slulf mtx_unlock(&sc->bqueue_mtx); 1005191856Slulf /* A bio that interfered with another bio. */ 1006191856Slulf if (bp->bio_pflags & GV_BIO_ONHOLD) { 1007191856Slulf s = bp->bio_caller1; 1008191856Slulf p = s->plex_sc; 1009191856Slulf /* Is it still locked out? */ 1010191856Slulf if (gv_stripe_active(p, bp)) { 1011191856Slulf /* Park the bio on the waiting queue. */ 1012191856Slulf bioq_disksort(p->wqueue, bp); 1013191856Slulf } else { 1014191856Slulf bp->bio_pflags &= ~GV_BIO_ONHOLD; 1015191856Slulf g_io_request(bp, s->drive_sc->consumer); 1016191856Slulf } 1017191856Slulf /* A special request requireing special handling. */ 1018191856Slulf } else if (bp->bio_pflags & GV_BIO_INTERNAL) { 1019191856Slulf p = bp->bio_caller1; 1020191856Slulf gv_plex_start(p, bp); 1021191856Slulf } else { 1022191856Slulf gv_volume_start(sc, bp); 1023191856Slulf } 1024191856Slulf mtx_lock(&sc->bqueue_mtx); 1025191856Slulf } 1026191856Slulf /* Then do completed requests. */ 1027191856Slulf bp = bioq_takefirst(sc->bqueue_up); 1028190507Slulf if (bp == NULL) { 1029191849Slulf msleep(sc, &sc->bqueue_mtx, PRIBIO, "-", hz/10); 1030191849Slulf mtx_unlock(&sc->bqueue_mtx); 1031190507Slulf continue; 1032190507Slulf } 1033191849Slulf mtx_unlock(&sc->bqueue_mtx); 1034191856Slulf gv_bio_done(sc, bp); 1035190507Slulf } 1036190507Slulf} 1037190507Slulf 1038130389Sle#define VINUM_CLASS_NAME "VINUM" 1039130389Sle 1040130389Slestatic struct g_class g_vinum_class = { 1041130389Sle .name = VINUM_CLASS_NAME, 1042133318Sphk .version = G_VERSION, 1043152773Sle .init = gv_init, 1044190507Slulf .taste = gv_taste, 1045130389Sle .ctlreq = gv_config, 1046190507Slulf .destroy_geom = gv_unload, 1047130389Sle}; 1048130389Sle 1049130389SleDECLARE_GEOM_CLASS(g_vinum_class, g_vinum); 1050