1190513Slulf/*- 2190513Slulf * Copyright (c) 2007 Lukas Ertl 3190513Slulf * Copyright (c) 2007, 2009 Ulf Lilleengen 4190513Slulf * All rights reserved. 5190513Slulf * 6190513Slulf * Redistribution and use in source and binary forms, with or without 7190513Slulf * modification, are permitted provided that the following conditions 8190513Slulf * are met: 9190513Slulf * 1. Redistributions of source code must retain the above copyright 10190513Slulf * notice, this list of conditions and the following disclaimer. 11190513Slulf * 2. Redistributions in binary form must reproduce the above copyright 12190513Slulf * notice, this list of conditions and the following disclaimer in the 13190513Slulf * documentation and/or other materials provided with the distribution. 14190513Slulf * 15190513Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16190513Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17190513Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18190513Slulf * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19190513Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20190513Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21190513Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22190513Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23190513Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24190513Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25190513Slulf * SUCH DAMAGE. 26190513Slulf */ 27190513Slulf 28190513Slulf#include <sys/cdefs.h> 29190513Slulf__FBSDID("$FreeBSD$"); 30190513Slulf 31190513Slulf#include <sys/param.h> 32190513Slulf#include <sys/bio.h> 33190513Slulf#include <sys/conf.h> 34193066Sjamie#include <sys/jail.h> 35190513Slulf#include <sys/kernel.h> 36190513Slulf#include <sys/malloc.h> 37190513Slulf#include <sys/systm.h> 38190513Slulf 39190513Slulf#include <geom/geom.h> 40190513Slulf#include <geom/vinum/geom_vinum_var.h> 41190513Slulf#include <geom/vinum/geom_vinum.h> 42190513Slulf 43190513Slulf#define DEFAULT_STRIPESIZE 262144 44190513Slulf 45190513Slulf/* 46190513Slulf * Create a new drive object, either by user request, during taste of the drive 47190513Slulf * itself, or because it was referenced by a subdisk during taste. 48190513Slulf */ 49190513Slulfint 50190513Slulfgv_create_drive(struct gv_softc *sc, struct gv_drive *d) 51190513Slulf{ 52190513Slulf struct g_geom *gp; 53190513Slulf struct g_provider *pp; 54190513Slulf struct g_consumer *cp, *cp2; 55190513Slulf struct gv_drive *d2; 56190513Slulf struct gv_hdr *hdr; 57190513Slulf struct gv_freelist *fl; 58190513Slulf 59190513Slulf KASSERT(d != NULL, ("gv_create_drive: NULL d")); 60190513Slulf 61190513Slulf gp = sc->geom; 62190513Slulf 63190513Slulf pp = NULL; 64190513Slulf cp = cp2 = NULL; 65190513Slulf 66190513Slulf /* The drive already has a consumer if it was tasted before. */ 67190513Slulf if (d->consumer != NULL) { 68190513Slulf cp = d->consumer; 69190513Slulf cp->private = d; 70190513Slulf pp = cp->provider; 71190513Slulf } else if (!(d->flags & GV_DRIVE_REFERENCED)) { 72190513Slulf if (gv_find_drive(sc, d->name) != NULL) { 73190513Slulf G_VINUM_DEBUG(0, "drive '%s' already exists", d->name); 74190513Slulf g_free(d); 75190513Slulf return (GV_ERR_CREATE); 76190513Slulf } 77190513Slulf 78190513Slulf if (gv_find_drive_device(sc, d->device) != NULL) { 79190513Slulf G_VINUM_DEBUG(0, "provider '%s' already in use by " 80190513Slulf "gvinum", d->device); 81190513Slulf return (GV_ERR_CREATE); 82190513Slulf } 83190513Slulf 84190513Slulf pp = g_provider_by_name(d->device); 85190513Slulf if (pp == NULL) { 86190513Slulf G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared", 87190513Slulf d->name, d->device); 88190513Slulf g_free(d); 89190513Slulf return (GV_ERR_CREATE); 90190513Slulf } 91190513Slulf 92190513Slulf g_topology_lock(); 93190513Slulf cp = g_new_consumer(gp); 94190513Slulf if (g_attach(cp, pp) != 0) { 95190513Slulf g_destroy_consumer(cp); 96190513Slulf g_topology_unlock(); 97197767Slulf G_VINUM_DEBUG(0, "create drive '%s': unable to attach", 98190513Slulf d->name); 99190513Slulf g_free(d); 100190513Slulf return (GV_ERR_CREATE); 101190513Slulf } 102190513Slulf g_topology_unlock(); 103190513Slulf 104190513Slulf d->consumer = cp; 105190513Slulf cp->private = d; 106190513Slulf } 107190513Slulf 108190513Slulf /* 109190513Slulf * If this was just a "referenced" drive, we're almost finished, but 110190513Slulf * insert this drive not on the head of the drives list, as 111190513Slulf * gv_drive_is_newer() expects a "real" drive from LIST_FIRST(). 112190513Slulf */ 113190513Slulf if (d->flags & GV_DRIVE_REFERENCED) { 114190513Slulf snprintf(d->device, sizeof(d->device), "???"); 115190513Slulf d2 = LIST_FIRST(&sc->drives); 116190513Slulf if (d2 == NULL) 117190513Slulf LIST_INSERT_HEAD(&sc->drives, d, drive); 118190513Slulf else 119190513Slulf LIST_INSERT_AFTER(d2, d, drive); 120190513Slulf return (0); 121190513Slulf } 122190513Slulf 123190513Slulf /* 124190513Slulf * Update access counts of the new drive to those of an already 125190513Slulf * existing drive. 126190513Slulf */ 127190513Slulf LIST_FOREACH(d2, &sc->drives, drive) { 128190513Slulf if ((d == d2) || (d2->consumer == NULL)) 129190513Slulf continue; 130190513Slulf 131190513Slulf cp2 = d2->consumer; 132190513Slulf g_topology_lock(); 133190513Slulf if ((cp2->acr || cp2->acw || cp2->ace) && 134190513Slulf (g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) { 135190513Slulf g_detach(cp); 136190513Slulf g_destroy_consumer(cp); 137190513Slulf g_topology_unlock(); 138197767Slulf G_VINUM_DEBUG(0, "create drive '%s': unable to update " 139190513Slulf "access counts", d->name); 140190513Slulf if (d->hdr != NULL) 141190513Slulf g_free(d->hdr); 142190513Slulf g_free(d); 143190513Slulf return (GV_ERR_CREATE); 144190513Slulf } 145190513Slulf g_topology_unlock(); 146190513Slulf break; 147190513Slulf } 148190513Slulf 149190513Slulf d->size = pp->mediasize - GV_DATA_START; 150190513Slulf d->avail = d->size; 151190513Slulf d->vinumconf = sc; 152190513Slulf LIST_INIT(&d->subdisks); 153190513Slulf LIST_INIT(&d->freelist); 154190513Slulf 155190513Slulf /* The header might have been set during taste. */ 156190513Slulf if (d->hdr == NULL) { 157190513Slulf hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO); 158190513Slulf hdr->magic = GV_MAGIC; 159190513Slulf hdr->config_length = GV_CFG_LEN; 160193066Sjamie getcredhostname(NULL, hdr->label.sysname, GV_HOSTNAME_LEN); 161190513Slulf strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name)); 162190513Slulf microtime(&hdr->label.date_of_birth); 163190513Slulf d->hdr = hdr; 164190513Slulf } 165190513Slulf 166190513Slulf /* We also need a freelist entry. */ 167190513Slulf fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO); 168190513Slulf fl->offset = GV_DATA_START; 169190513Slulf fl->size = d->avail; 170190513Slulf LIST_INSERT_HEAD(&d->freelist, fl, freelist); 171190513Slulf d->freelist_entries = 1; 172190513Slulf 173190513Slulf if (gv_find_drive(sc, d->name) == NULL) 174190513Slulf LIST_INSERT_HEAD(&sc->drives, d, drive); 175190513Slulf 176190513Slulf gv_set_drive_state(d, GV_DRIVE_UP, 0); 177190513Slulf return (0); 178190513Slulf} 179190513Slulf 180190513Slulfint 181190513Slulfgv_create_volume(struct gv_softc *sc, struct gv_volume *v) 182190513Slulf{ 183190513Slulf KASSERT(v != NULL, ("gv_create_volume: NULL v")); 184190513Slulf 185190513Slulf v->vinumconf = sc; 186190513Slulf v->flags |= GV_VOL_NEWBORN; 187190513Slulf LIST_INIT(&v->plexes); 188190513Slulf LIST_INSERT_HEAD(&sc->volumes, v, volume); 189190513Slulf v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 190190513Slulf bioq_init(v->wqueue); 191190513Slulf return (0); 192190513Slulf} 193190513Slulf 194190513Slulfint 195190513Slulfgv_create_plex(struct gv_softc *sc, struct gv_plex *p) 196190513Slulf{ 197190513Slulf struct gv_volume *v; 198190513Slulf 199190513Slulf KASSERT(p != NULL, ("gv_create_plex: NULL p")); 200190513Slulf 201190513Slulf /* Find the volume this plex should be attached to. */ 202190513Slulf v = gv_find_vol(sc, p->volume); 203190513Slulf if (v == NULL) { 204190513Slulf G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found", 205190513Slulf p->name, p->volume); 206190513Slulf g_free(p); 207190513Slulf return (GV_ERR_CREATE); 208190513Slulf } 209190513Slulf if (!(v->flags & GV_VOL_NEWBORN)) 210190513Slulf p->flags |= GV_PLEX_ADDED; 211190513Slulf p->vol_sc = v; 212190513Slulf v->plexcount++; 213190513Slulf p->vinumconf = sc; 214190513Slulf p->synced = 0; 215190513Slulf p->flags |= GV_PLEX_NEWBORN; 216190513Slulf LIST_INSERT_HEAD(&v->plexes, p, in_volume); 217190513Slulf LIST_INIT(&p->subdisks); 218190513Slulf TAILQ_INIT(&p->packets); 219190513Slulf LIST_INSERT_HEAD(&sc->plexes, p, plex); 220190513Slulf p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 221190513Slulf bioq_init(p->bqueue); 222190513Slulf p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 223190513Slulf bioq_init(p->wqueue); 224190513Slulf p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 225190513Slulf bioq_init(p->rqueue); 226190513Slulf return (0); 227190513Slulf} 228190513Slulf 229190513Slulfint 230190513Slulfgv_create_sd(struct gv_softc *sc, struct gv_sd *s) 231190513Slulf{ 232190513Slulf struct gv_plex *p; 233190513Slulf struct gv_drive *d; 234190513Slulf 235190513Slulf KASSERT(s != NULL, ("gv_create_sd: NULL s")); 236190513Slulf 237190513Slulf /* Find the drive where this subdisk should be put on. */ 238190513Slulf d = gv_find_drive(sc, s->drive); 239190513Slulf if (d == NULL) { 240190513Slulf /* 241190513Slulf * It's possible that the subdisk references a drive that 242190513Slulf * doesn't exist yet (during the taste process), so create a 243190513Slulf * practically empty "referenced" drive. 244190513Slulf */ 245190513Slulf if (s->flags & GV_SD_TASTED) { 246190513Slulf d = g_malloc(sizeof(struct gv_drive), 247190513Slulf M_WAITOK | M_ZERO); 248190513Slulf d->flags |= GV_DRIVE_REFERENCED; 249190513Slulf strlcpy(d->name, s->drive, sizeof(d->name)); 250190513Slulf gv_create_drive(sc, d); 251190513Slulf } else { 252190513Slulf G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found", 253190513Slulf s->name, s->drive); 254190513Slulf g_free(s); 255190513Slulf return (GV_ERR_CREATE); 256190513Slulf } 257190513Slulf } 258190513Slulf 259190513Slulf /* Find the plex where this subdisk belongs to. */ 260190513Slulf p = gv_find_plex(sc, s->plex); 261190513Slulf if (p == NULL) { 262190513Slulf G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found", 263190513Slulf s->name, s->plex); 264190513Slulf g_free(s); 265190513Slulf return (GV_ERR_CREATE); 266190513Slulf } 267190513Slulf 268190513Slulf /* 269190513Slulf * First we give the subdisk to the drive, to handle autosized 270190513Slulf * values ... 271190513Slulf */ 272190513Slulf if (gv_sd_to_drive(s, d) != 0) { 273190513Slulf g_free(s); 274190513Slulf return (GV_ERR_CREATE); 275190513Slulf } 276190513Slulf 277190513Slulf /* 278190513Slulf * Then, we give the subdisk to the plex; we check if the 279190513Slulf * given values are correct and maybe adjust them. 280190513Slulf */ 281190513Slulf if (gv_sd_to_plex(s, p) != 0) { 282190513Slulf G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'", 283190513Slulf s->name, p->name); 284190513Slulf if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED)) 285190513Slulf LIST_REMOVE(s, from_drive); 286190513Slulf gv_free_sd(s); 287190513Slulf g_free(s); 288190513Slulf /* 289190513Slulf * If this subdisk can't be created, we won't create 290190513Slulf * the attached plex either, if it is also a new one. 291190513Slulf */ 292190513Slulf if (!(p->flags & GV_PLEX_NEWBORN)) 293190513Slulf return (GV_ERR_CREATE); 294190513Slulf gv_rm_plex(sc, p); 295190513Slulf return (GV_ERR_CREATE); 296190513Slulf } 297190513Slulf s->flags |= GV_SD_NEWBORN; 298190513Slulf 299190513Slulf s->vinumconf = sc; 300190513Slulf LIST_INSERT_HEAD(&sc->subdisks, s, sd); 301190513Slulf 302190513Slulf return (0); 303190513Slulf} 304190513Slulf 305190513Slulf/* 306190513Slulf * Create a concatenated volume from specified drives or drivegroups. 307190513Slulf */ 308190513Slulfvoid 309190513Slulfgv_concat(struct g_geom *gp, struct gctl_req *req) 310190513Slulf{ 311190513Slulf struct gv_drive *d; 312190513Slulf struct gv_sd *s; 313190513Slulf struct gv_volume *v; 314190513Slulf struct gv_plex *p; 315190513Slulf struct gv_softc *sc; 316190513Slulf char *drive, buf[30], *vol; 317202974Strasz int *drives, dcount; 318190513Slulf 319190513Slulf sc = gp->softc; 320190513Slulf dcount = 0; 321190513Slulf vol = gctl_get_param(req, "name", NULL); 322190513Slulf if (vol == NULL) { 323197767Slulf gctl_error(req, "volume name not given"); 324190513Slulf return; 325190513Slulf } 326190513Slulf 327190513Slulf drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 328190513Slulf 329190513Slulf if (drives == NULL) { 330190513Slulf gctl_error(req, "drive names not given"); 331190513Slulf return; 332190513Slulf } 333190513Slulf 334190513Slulf /* First we create the volume. */ 335190513Slulf v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 336190513Slulf strlcpy(v->name, vol, sizeof(v->name)); 337190513Slulf v->state = GV_VOL_UP; 338190513Slulf gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 339190513Slulf 340190513Slulf /* Then we create the plex. */ 341190513Slulf p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 342190513Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 343190513Slulf strlcpy(p->volume, v->name, sizeof(p->volume)); 344190513Slulf p->org = GV_PLEX_CONCAT; 345190513Slulf p->stripesize = 0; 346190513Slulf gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 347190513Slulf 348190513Slulf /* Drives are first (right now) priority */ 349190513Slulf for (dcount = 0; dcount < *drives; dcount++) { 350190513Slulf snprintf(buf, sizeof(buf), "drive%d", dcount); 351190513Slulf drive = gctl_get_param(req, buf, NULL); 352190513Slulf d = gv_find_drive(sc, drive); 353190513Slulf if (d == NULL) { 354190513Slulf gctl_error(req, "No such drive '%s'", drive); 355190513Slulf continue; 356190513Slulf } 357190513Slulf s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 358190513Slulf snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 359190513Slulf strlcpy(s->plex, p->name, sizeof(s->plex)); 360190513Slulf strlcpy(s->drive, drive, sizeof(s->drive)); 361190513Slulf s->plex_offset = -1; 362190513Slulf s->drive_offset = -1; 363190513Slulf s->size = -1; 364190513Slulf gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 365190513Slulf } 366190513Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 367190513Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 368190513Slulf} 369190513Slulf 370190513Slulf/* 371190513Slulf * Create a mirrored volume from specified drives or drivegroups. 372190513Slulf */ 373190513Slulfvoid 374190513Slulfgv_mirror(struct g_geom *gp, struct gctl_req *req) 375190513Slulf{ 376190513Slulf struct gv_drive *d; 377190513Slulf struct gv_sd *s; 378190513Slulf struct gv_volume *v; 379190513Slulf struct gv_plex *p; 380190513Slulf struct gv_softc *sc; 381190513Slulf char *drive, buf[30], *vol; 382190513Slulf int *drives, *flags, dcount, pcount, scount; 383190513Slulf 384190513Slulf sc = gp->softc; 385190513Slulf dcount = 0; 386190513Slulf scount = 0; 387190513Slulf pcount = 0; 388190513Slulf vol = gctl_get_param(req, "name", NULL); 389190513Slulf if (vol == NULL) { 390197767Slulf gctl_error(req, "volume name not given"); 391190513Slulf return; 392190513Slulf } 393190513Slulf 394190513Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 395190513Slulf drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 396190513Slulf 397190513Slulf if (drives == NULL) { 398197767Slulf gctl_error(req, "drive names not given"); 399190513Slulf return; 400190513Slulf } 401190513Slulf 402190513Slulf /* We must have an even number of drives. */ 403190513Slulf if (*drives % 2 != 0) { 404190513Slulf gctl_error(req, "mirror organization must have an even number " 405190513Slulf "of drives"); 406190513Slulf return; 407190513Slulf } 408190513Slulf if (*flags & GV_FLAG_S && *drives < 4) { 409190513Slulf gctl_error(req, "must have at least 4 drives for striped plex"); 410190513Slulf return; 411190513Slulf } 412190513Slulf 413190513Slulf /* First we create the volume. */ 414190513Slulf v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 415190513Slulf strlcpy(v->name, vol, sizeof(v->name)); 416190513Slulf v->state = GV_VOL_UP; 417190513Slulf gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 418190513Slulf 419190513Slulf /* Then we create the plexes. */ 420190513Slulf for (pcount = 0; pcount < 2; pcount++) { 421190513Slulf p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 422190513Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, 423190513Slulf pcount); 424190513Slulf strlcpy(p->volume, v->name, sizeof(p->volume)); 425190513Slulf if (*flags & GV_FLAG_S) { 426190513Slulf p->org = GV_PLEX_STRIPED; 427190513Slulf p->stripesize = DEFAULT_STRIPESIZE; 428190513Slulf } else { 429190513Slulf p->org = GV_PLEX_CONCAT; 430190513Slulf p->stripesize = -1; 431190513Slulf } 432190513Slulf gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 433190513Slulf 434190513Slulf /* 435190513Slulf * We just gives each even drive to plex one, and each odd to 436190513Slulf * plex two. 437190513Slulf */ 438190513Slulf scount = 0; 439190513Slulf for (dcount = pcount; dcount < *drives; dcount += 2) { 440190513Slulf snprintf(buf, sizeof(buf), "drive%d", dcount); 441190513Slulf drive = gctl_get_param(req, buf, NULL); 442190513Slulf d = gv_find_drive(sc, drive); 443190513Slulf if (d == NULL) { 444190513Slulf gctl_error(req, "No such drive '%s', aborting", 445190513Slulf drive); 446190513Slulf scount++; 447190513Slulf break; 448190513Slulf } 449190513Slulf s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 450190513Slulf snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, 451190513Slulf scount); 452190513Slulf strlcpy(s->plex, p->name, sizeof(s->plex)); 453190513Slulf strlcpy(s->drive, drive, sizeof(s->drive)); 454190513Slulf s->plex_offset = -1; 455190513Slulf s->drive_offset = -1; 456190513Slulf s->size = -1; 457190513Slulf gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 458190513Slulf scount++; 459190513Slulf } 460190513Slulf } 461190513Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 462190513Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 463190513Slulf} 464190513Slulf 465190513Slulfvoid 466190513Slulfgv_raid5(struct g_geom *gp, struct gctl_req *req) 467190513Slulf{ 468190513Slulf struct gv_softc *sc; 469190513Slulf struct gv_drive *d; 470190513Slulf struct gv_volume *v; 471190513Slulf struct gv_plex *p; 472190513Slulf struct gv_sd *s; 473190513Slulf int *drives, *flags, dcount; 474190513Slulf char *vol, *drive, buf[30]; 475190513Slulf off_t *stripesize; 476190513Slulf 477190513Slulf sc = gp->softc; 478190513Slulf 479190513Slulf vol = gctl_get_param(req, "name", NULL); 480190513Slulf if (vol == NULL) { 481197767Slulf gctl_error(req, "volume name not given"); 482190513Slulf return; 483190513Slulf } 484190513Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 485190513Slulf drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 486190513Slulf stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize)); 487190513Slulf 488190513Slulf if (stripesize == NULL) { 489190513Slulf gctl_error(req, "no stripesize given"); 490190513Slulf return; 491190513Slulf } 492190513Slulf 493190513Slulf if (drives == NULL) { 494197767Slulf gctl_error(req, "drive names not given"); 495190513Slulf return; 496190513Slulf } 497190513Slulf 498190513Slulf /* We must have at least three drives. */ 499190513Slulf if (*drives < 3) { 500190513Slulf gctl_error(req, "must have at least three drives for this " 501190513Slulf "plex organisation"); 502190513Slulf return; 503190513Slulf } 504190513Slulf /* First we create the volume. */ 505190513Slulf v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 506190513Slulf strlcpy(v->name, vol, sizeof(v->name)); 507190513Slulf v->state = GV_VOL_UP; 508190513Slulf gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 509190513Slulf 510190513Slulf /* Then we create the plex. */ 511190513Slulf p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 512190513Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 513190513Slulf strlcpy(p->volume, v->name, sizeof(p->volume)); 514190513Slulf p->org = GV_PLEX_RAID5; 515190513Slulf p->stripesize = *stripesize; 516190513Slulf gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 517190513Slulf 518190513Slulf /* Create subdisks on drives. */ 519190513Slulf for (dcount = 0; dcount < *drives; dcount++) { 520190513Slulf snprintf(buf, sizeof(buf), "drive%d", dcount); 521190513Slulf drive = gctl_get_param(req, buf, NULL); 522190513Slulf d = gv_find_drive(sc, drive); 523190513Slulf if (d == NULL) { 524190513Slulf gctl_error(req, "No such drive '%s'", drive); 525190513Slulf continue; 526190513Slulf } 527190513Slulf s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 528190513Slulf snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 529190513Slulf strlcpy(s->plex, p->name, sizeof(s->plex)); 530190513Slulf strlcpy(s->drive, drive, sizeof(s->drive)); 531190513Slulf s->plex_offset = -1; 532190513Slulf s->drive_offset = -1; 533190513Slulf s->size = -1; 534190513Slulf gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 535190513Slulf } 536190513Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 537190513Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 538190513Slulf} 539190513Slulf 540190513Slulf/* 541190513Slulf * Create a striped volume from specified drives or drivegroups. 542190513Slulf */ 543190513Slulfvoid 544190513Slulfgv_stripe(struct g_geom *gp, struct gctl_req *req) 545190513Slulf{ 546190513Slulf struct gv_drive *d; 547190513Slulf struct gv_sd *s; 548190513Slulf struct gv_volume *v; 549190513Slulf struct gv_plex *p; 550190513Slulf struct gv_softc *sc; 551190513Slulf char *drive, buf[30], *vol; 552190513Slulf int *drives, *flags, dcount, pcount; 553190513Slulf 554190513Slulf sc = gp->softc; 555190513Slulf dcount = 0; 556190513Slulf pcount = 0; 557190513Slulf vol = gctl_get_param(req, "name", NULL); 558190513Slulf if (vol == NULL) { 559197767Slulf gctl_error(req, "volume name not given"); 560190513Slulf return; 561190513Slulf } 562190513Slulf flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 563190513Slulf drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 564190513Slulf 565190513Slulf if (drives == NULL) { 566197767Slulf gctl_error(req, "drive names not given"); 567190513Slulf return; 568190513Slulf } 569190513Slulf 570190513Slulf /* We must have at least two drives. */ 571190513Slulf if (*drives < 2) { 572190513Slulf gctl_error(req, "must have at least 2 drives"); 573190513Slulf return; 574190513Slulf } 575190513Slulf 576190513Slulf /* First we create the volume. */ 577190513Slulf v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 578190513Slulf strlcpy(v->name, vol, sizeof(v->name)); 579190513Slulf v->state = GV_VOL_UP; 580190513Slulf gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 581190513Slulf 582190513Slulf /* Then we create the plex. */ 583190513Slulf p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 584190513Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 585190513Slulf strlcpy(p->volume, v->name, sizeof(p->volume)); 586190513Slulf p->org = GV_PLEX_STRIPED; 587190513Slulf p->stripesize = 262144; 588190513Slulf gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 589190513Slulf 590190513Slulf /* Create subdisks on drives. */ 591190513Slulf for (dcount = 0; dcount < *drives; dcount++) { 592190513Slulf snprintf(buf, sizeof(buf), "drive%d", dcount); 593190513Slulf drive = gctl_get_param(req, buf, NULL); 594190513Slulf d = gv_find_drive(sc, drive); 595190513Slulf if (d == NULL) { 596190513Slulf gctl_error(req, "No such drive '%s'", drive); 597190513Slulf continue; 598190513Slulf } 599190513Slulf s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 600190513Slulf snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 601190513Slulf strlcpy(s->plex, p->name, sizeof(s->plex)); 602190513Slulf strlcpy(s->drive, drive, sizeof(s->drive)); 603190513Slulf s->plex_offset = -1; 604190513Slulf s->drive_offset = -1; 605190513Slulf s->size = -1; 606190513Slulf gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 607190513Slulf } 608190513Slulf gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 609190513Slulf gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 610190513Slulf} 611