geom_vinum.c revision 136065
1/* 2 * Copyright (c) 2004 Lukas Ertl 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum.c 136065 2004-10-02 20:50:21Z le $"); 30 31#include <sys/param.h> 32#include <sys/bio.h> 33#include <sys/kernel.h> 34#include <sys/lock.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/mutex.h> 38#include <sys/systm.h> 39 40#include <geom/geom.h> 41#include <geom/vinum/geom_vinum_var.h> 42#include <geom/vinum/geom_vinum.h> 43#include <geom/vinum/geom_vinum_share.h> 44 45#if 0 46SYSCTL_DECL(_kern_geom); 47SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, "GEOM_VINUM stuff"); 48SYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &gv_debug, 0, 49 "Debug level"); 50#endif 51 52int gv_create(struct g_geom *, struct gctl_req *); 53 54static void 55gv_orphan(struct g_consumer *cp) 56{ 57 struct g_geom *gp; 58 struct gv_softc *sc; 59 int error; 60 61 g_topology_assert(); 62 63 KASSERT(cp != NULL, ("gv_orphan: null cp")); 64 gp = cp->geom; 65 KASSERT(gp != NULL, ("gv_orphan: null gp")); 66 sc = gp->softc; 67 68 g_trace(G_T_TOPOLOGY, "gv_orphan(%s)", gp->name); 69 70 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 71 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 72 error = cp->provider->error; 73 if (error == 0) 74 error = ENXIO; 75 g_detach(cp); 76 g_destroy_consumer(cp); 77 if (!LIST_EMPTY(&gp->consumer)) 78 return; 79 g_free(sc); 80 g_wither_geom(gp, error); 81} 82 83static void 84gv_start(struct bio *bp) 85{ 86 struct bio *bp2; 87 struct g_geom *gp; 88 89 gp = bp->bio_to->geom; 90 switch(bp->bio_cmd) { 91 case BIO_READ: 92 case BIO_WRITE: 93 case BIO_DELETE: 94 bp2 = g_clone_bio(bp); 95 if (bp2 == NULL) 96 g_io_deliver(bp, ENOMEM); 97 else { 98 bp2->bio_done = g_std_done; 99 g_io_request(bp2, LIST_FIRST(&gp->consumer)); 100 } 101 return; 102 default: 103 g_io_deliver(bp, EOPNOTSUPP); 104 return; 105 } 106} 107 108static int 109gv_access(struct g_provider *pp, int dr, int dw, int de) 110{ 111 struct g_geom *gp; 112 struct g_consumer *cp; 113 int error; 114 115 gp = pp->geom; 116 error = ENXIO; 117 cp = LIST_FIRST(&gp->consumer); 118 error = g_access(cp, dr, dw, de); 119 return (error); 120} 121 122static struct g_geom * 123gv_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 124{ 125 struct g_geom *gp; 126 struct g_consumer *cp; 127 struct gv_softc *sc; 128 struct gv_hdr *vhdr; 129 int error, first; 130 char *buf; 131 132 vhdr = NULL; 133 buf = NULL; 134 first = 0; 135 136 g_trace(G_T_TOPOLOGY, "gv_taste(%s, %s)", mp->name, pp->name); 137 g_topology_assert(); 138 139 if (pp->sectorsize == 0) 140 return (NULL); 141 142 /* Check if we already have a VINUM geom, or create a new one. */ 143 if (LIST_EMPTY(&mp->geom)) { 144 gp = g_new_geomf(mp, "VINUM"); 145 gp->spoiled = gv_orphan; 146 gp->orphan = gv_orphan; 147 gp->access = gv_access; 148 gp->start = gv_start; 149 gp->softc = g_malloc(sizeof(struct gv_softc), 150 M_WAITOK | M_ZERO); 151 sc = gp->softc; 152 sc->geom = gp; 153 LIST_INIT(&sc->drives); 154 LIST_INIT(&sc->subdisks); 155 LIST_INIT(&sc->plexes); 156 LIST_INIT(&sc->volumes); 157 first++; 158 } else { 159 gp = LIST_FIRST(&mp->geom); 160 sc = gp->softc; 161 } 162 163 164 /* We need a temporary consumer to read the config from. */ 165 cp = g_new_consumer(gp); 166 error = g_attach(cp, pp); 167 if (error) { 168 g_destroy_consumer(cp); 169 if (first) { 170 g_free(sc); 171 g_destroy_geom(gp); 172 } 173 return (NULL); 174 } 175 error = g_access(cp, 1, 0, 0); 176 if (error) { 177 g_detach(cp); 178 g_destroy_consumer(cp); 179 if (first) { 180 g_free(gp->softc); 181 g_destroy_geom(gp); 182 } 183 return (NULL); 184 } 185 186 g_topology_unlock(); 187 188 /* Check if the provided slice is a valid vinum drive. */ 189 vhdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, &error); 190 if (vhdr == NULL || error != 0) { 191 g_topology_lock(); 192 g_access(cp, -1, 0, 0); 193 g_detach(cp); 194 g_destroy_consumer(cp); 195 if (first) { 196 g_free(sc); 197 g_destroy_geom(gp); 198 } 199 return (NULL); 200 } 201 202 /* This provider has no vinum magic on board. */ 203 if (vhdr->magic != GV_MAGIC) { 204 /* Release the temporary consumer, we don't need it anymore. */ 205 g_topology_lock(); 206 g_access(cp, -1, 0, 0); 207 g_detach(cp); 208 g_destroy_consumer(cp); 209 210 g_free(vhdr); 211 212 /* 213 * If there is no other VINUM geom yet just take this one; the 214 * configuration is still empty, but it can be filled by other 215 * valid vinum drives later. 216 */ 217 if (first) 218 return (gp); 219 else 220 return (NULL); 221 222 /* 223 * We have found a valid vinum drive, now read the on-disk 224 * configuration. 225 */ 226 } else { 227 g_free(vhdr); 228 229 buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, 230 &error); 231 if (buf == NULL || error != 0) { 232 g_topology_lock(); 233 g_access(cp, -1, 0, 0); 234 g_detach(cp); 235 g_destroy_consumer(cp); 236 if (first) { 237 g_free(sc); 238 g_destroy_geom(gp); 239 } 240 return (NULL); 241 } 242 243 /* Release the temporary consumer, we don't need it anymore. */ 244 g_topology_lock(); 245 g_access(cp, -1, 0, 0); 246 g_detach(cp); 247 g_destroy_consumer(cp); 248 249 /* We are the first VINUM geom. */ 250 if (first) { 251 gv_parse_config(sc, buf, 0); 252 g_free(buf); 253 return (gp); 254 255 /* Just merge the configs. */ 256 } else { 257 gv_parse_config(sc, buf, 1); 258 g_free(buf); 259 return (NULL); 260 } 261 } 262} 263 264/* Handle userland requests for creating new objects. */ 265int 266gv_create(struct g_geom *gp, struct gctl_req *req) 267{ 268 struct gv_softc *sc; 269 struct gv_drive *d, *d2; 270 struct gv_plex *p, *p2; 271 struct gv_sd *s, *s2; 272 struct gv_volume *v, *v2; 273 struct g_consumer *cp; 274 struct g_provider *pp; 275 int error, i, *drives, *plexes, *subdisks, *volumes; 276 char buf[20], errstr[ERRBUFSIZ]; 277 278 g_topology_assert(); 279 280 sc = gp->softc; 281 282 /* Find out how many of each object have been passed in. */ 283 volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes)); 284 plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes)); 285 subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks)); 286 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 287 288 /* First, handle drive definitions ... */ 289 for (i = 0; i < *drives; i++) { 290 snprintf(buf, sizeof(buf), "drive%d", i); 291 d2 = gctl_get_paraml(req, buf, sizeof(*d2)); 292 293 d = gv_find_drive(sc, d2->name); 294 if (d != NULL) { 295 gctl_error(req, "drive '%s' is already known", 296 d->name); 297 continue; 298 } 299 300 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 301 bcopy(d2, d, sizeof(*d)); 302 303 /* 304 * Make sure that the provider specified in the drive 305 * specification is an active GEOM provider. 306 */ 307 pp = g_provider_by_name(d->device); 308 if (pp == NULL) { 309 gctl_error(req, "%s: drive not found", d->device); 310 g_free(d); 311 return (-1); 312 } 313 d->size = pp->mediasize - GV_DATA_START; 314 d->avail = d->size; 315 316 gv_config_new_drive(d); 317 318 LIST_INSERT_HEAD(&sc->drives, d, drive); 319 } 320 321 /* ... then volume definitions ... */ 322 for (i = 0; i < *volumes; i++) { 323 error = 0; 324 snprintf(buf, sizeof(buf), "volume%d", i); 325 v2 = gctl_get_paraml(req, buf, sizeof(*v2)); 326 327 v = gv_find_vol(sc, v2->name); 328 if (v != NULL) { 329 gctl_error(req, "volume '%s' is already known", 330 v->name); 331 return (-1); 332 } 333 334 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 335 bcopy(v2, v, sizeof(*v)); 336 337 v->vinumconf = sc; 338 LIST_INIT(&v->plexes); 339 LIST_INSERT_HEAD(&sc->volumes, v, volume); 340 } 341 342 /* ... then plex definitions ... */ 343 for (i = 0; i < *plexes; i++) { 344 error = 0; 345 snprintf(buf, sizeof(buf), "plex%d", i); 346 p2 = gctl_get_paraml(req, buf, sizeof(*p2)); 347 348 p = gv_find_plex(sc, p2->name); 349 if (p != NULL) { 350 gctl_error(req, "plex '%s' is already known", p->name); 351 return (-1); 352 } 353 354 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 355 bcopy(p2, p, sizeof(*p)); 356 357 /* Find the volume this plex should be attached to. */ 358 v = gv_find_vol(sc, p->volume); 359 if (v != NULL) { 360 if (v->plexcount) 361 p->flags |= GV_PLEX_ADDED; 362 p->vol_sc = v; 363 v->plexcount++; 364 LIST_INSERT_HEAD(&v->plexes, p, in_volume); 365 } 366 367 p->vinumconf = sc; 368 p->flags |= GV_PLEX_NEWBORN; 369 LIST_INIT(&p->subdisks); 370 LIST_INSERT_HEAD(&sc->plexes, p, plex); 371 } 372 373 /* ... and finally, subdisk definitions. */ 374 for (i = 0; i < *subdisks; i++) { 375 error = 0; 376 snprintf(buf, sizeof(buf), "sd%d", i); 377 s2 = gctl_get_paraml(req, buf, sizeof(*s2)); 378 379 s = gv_find_sd(sc, s2->name); 380 if (s != NULL) { 381 gctl_error(req, "subdisk '%s' is already known", 382 s->name); 383 return (-1); 384 } 385 386 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 387 bcopy(s2, s, sizeof(*s)); 388 389 /* Find the drive where this subdisk should be put on. */ 390 d = gv_find_drive(sc, s->drive); 391 392 /* drive not found - XXX */ 393 if (d == NULL) { 394 printf("FOO: drive '%s' not found\n", s->drive); 395 g_free(s); 396 continue; 397 } 398 399 /* Find the plex where this subdisk belongs to. */ 400 p = gv_find_plex(sc, s->plex); 401 402 /* plex not found - XXX */ 403 if (p == NULL) { 404 printf("FOO: plex '%s' not found\n", s->plex); 405 g_free(s); 406 continue; 407 } 408 409 /* 410 * First we give the subdisk to the drive, to handle autosized 411 * values ... 412 */ 413 error = gv_sd_to_drive(sc, d, s, errstr, sizeof(errstr)); 414 if (error) { 415 gctl_error(req, errstr); 416 g_free(s); 417 continue; 418 } 419 420 /* 421 * Then, we give the subdisk to the plex; we check if the 422 * given values are correct and maybe adjust them. 423 */ 424 error = gv_sd_to_plex(p, s, 1); 425 if (error) { 426 printf("FOO: couldn't give sd '%s' to plex '%s'\n", 427 s->name, p->name); 428 } 429 s->flags |= GV_SD_NEWBORN; 430 431 s->vinumconf = sc; 432 LIST_INSERT_HEAD(&sc->subdisks, s, sd); 433 } 434 435 LIST_FOREACH(s, &sc->subdisks, sd) 436 gv_update_sd_state(s); 437 LIST_FOREACH(p, &sc->plexes, plex) 438 gv_update_plex_config(p); 439 LIST_FOREACH(v, &sc->volumes, volume) 440 gv_update_vol_state(v); 441 442 /* 443 * Write out the configuration to each drive. If the drive doesn't 444 * have a valid geom_slice geom yet, attach it temporarily to our VINUM 445 * geom. 446 */ 447 LIST_FOREACH(d, &sc->drives, drive) { 448 if (d->geom == NULL) { 449 /* 450 * XXX if the provider disapears before we get a chance 451 * to write the config out to the drive, should this 452 * be handled any differently? 453 */ 454 pp = g_provider_by_name(d->device); 455 if (pp == NULL) { 456 printf("geom_vinum: %s: drive disapeared?\n", 457 d->device); 458 continue; 459 } 460 cp = g_new_consumer(gp); 461 g_attach(cp, pp); 462 gv_save_config(cp, d, sc); 463 g_detach(cp); 464 g_destroy_consumer(cp); 465 } else 466 gv_save_config(NULL, d, sc); 467 } 468 469 return (0); 470} 471 472static void 473gv_config(struct gctl_req *req, struct g_class *mp, char const *verb) 474{ 475 struct g_geom *gp; 476 struct gv_softc *sc; 477 struct sbuf *sb; 478 char *comment; 479 480 g_topology_assert(); 481 482 gp = LIST_FIRST(&mp->geom); 483 sc = gp->softc; 484 485 if (!strcmp(verb, "list")) { 486 gv_list(gp, req); 487 488 /* Save our configuration back to disk. */ 489 } else if (!strcmp(verb, "saveconfig")) { 490 491 gv_save_config_all(sc); 492 493 /* Return configuration in string form. */ 494 } else if (!strcmp(verb, "getconfig")) { 495 comment = gctl_get_param(req, "comment", NULL); 496 497 sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 498 gv_format_config(sc, sb, 0, comment); 499 sbuf_finish(sb); 500 gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); 501 sbuf_delete(sb); 502 503 } else if (!strcmp(verb, "create")) { 504 gv_create(gp, req); 505 506 } else if (!strcmp(verb, "remove")) { 507 gv_remove(gp, req); 508 509 } else if (!strcmp(verb, "start")) { 510 gv_start_obj(gp, req); 511 512 } else 513 gctl_error(req, "Unknown verb parameter"); 514} 515 516#if 0 517static int 518gv_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 519{ 520 struct g_geom *gp2; 521 struct g_consumer *cp; 522 struct gv_softc *sc; 523 struct gv_drive *d, *d2; 524 struct gv_plex *p, *p2; 525 struct gv_sd *s, *s2; 526 struct gv_volume *v, *v2; 527 struct gv_freelist *fl, *fl2; 528 529 g_trace(G_T_TOPOLOGY, "gv_destroy_geom: %s", gp->name); 530 g_topology_assert(); 531 532 KASSERT(gp != NULL, ("gv_destroy_geom: null gp")); 533 KASSERT(gp->softc != NULL, ("gv_destroy_geom: null sc")); 534 535 sc = gp->softc; 536 537 /* 538 * Check if any of our drives is still open; if so, refuse destruction. 539 */ 540 LIST_FOREACH(d, &sc->drives, drive) { 541 gp2 = d->geom; 542 cp = LIST_FIRST(&gp2->consumer); 543 if (cp != NULL) 544 g_access(cp, -1, -1, -1); 545 if (gv_is_open(gp2)) 546 return (EBUSY); 547 } 548 549 /* Clean up and deallocate what we allocated. */ 550 LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) { 551 LIST_REMOVE(d, drive); 552 g_free(d->hdr); 553 d->hdr = NULL; 554 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 555 d->freelist_entries--; 556 LIST_REMOVE(fl, freelist); 557 g_free(fl); 558 fl = NULL; 559 } 560 d->geom->softc = NULL; 561 g_free(d); 562 } 563 564 LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) { 565 LIST_REMOVE(s, sd); 566 s->drive_sc = NULL; 567 s->plex_sc = NULL; 568 s->provider = NULL; 569 s->consumer = NULL; 570 g_free(s); 571 } 572 573 LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) { 574 LIST_REMOVE(p, plex); 575 gv_kill_thread(p); 576 p->vol_sc = NULL; 577 p->geom->softc = NULL; 578 p->provider = NULL; 579 p->consumer = NULL; 580 if (p->org == GV_PLEX_RAID5) { 581 mtx_destroy(&p->worklist_mtx); 582 } 583 g_free(p); 584 } 585 586 LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) { 587 LIST_REMOVE(v, volume); 588 v->geom->softc = NULL; 589 g_free(v); 590 } 591 592 gp->softc = NULL; 593 g_free(sc); 594 g_wither_geom(gp, ENXIO); 595 return (0); 596} 597#endif 598 599#define VINUM_CLASS_NAME "VINUM" 600 601static struct g_class g_vinum_class = { 602 .name = VINUM_CLASS_NAME, 603 .version = G_VERSION, 604 .taste = gv_taste, 605 /*.destroy_geom = gv_destroy_geom,*/ 606 .ctlreq = gv_config, 607}; 608 609DECLARE_GEOM_CLASS(g_vinum_class, g_vinum); 610