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$"); 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/sysctl.h> 39 #include <sys/systm.h> 40 41 #include <geom/geom.h> 42 #include <geom/vinum/geom_vinum_var.h> 43 #include <geom/vinum/geom_vinum.h> 44 #include <geom/vinum/geom_vinum_share.h> 45 46 SYSCTL_DECL(_kern_geom); 47 SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, "GEOM_VINUM stuff"); 48 u_int g_vinum_debug = 0; 49 TUNABLE_INT("kern.geom.vinum.debug", &g_vinum_debug); 50 SYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &g_vinum_debug, 0, 51 "Debug level"); 52 53 int gv_create(struct g_geom *, struct gctl_req *); 54 55 static void 56 gv_orphan(struct g_consumer *cp) 57 { 58 struct g_geom *gp; 59 struct gv_softc *sc; 60 int error; 61 62 g_topology_assert(); 63 64 KASSERT(cp != NULL, ("gv_orphan: null cp")); 65 gp = cp->geom; 66 KASSERT(gp != NULL, ("gv_orphan: null gp")); 67 sc = gp->softc; 68 69 g_trace(G_T_TOPOLOGY, "gv_orphan(%s)", gp->name); 70 71 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 72 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 73 error = cp->provider->error; 74 if (error == 0) 75 error = ENXIO; 76 g_detach(cp); 77 g_destroy_consumer(cp); 78 if (!LIST_EMPTY(&gp->consumer)) 79 return; 80 g_free(sc); 81 g_wither_geom(gp, error); 82 } 83 84 static void 85 gv_start(struct bio *bp) 86 { 87 struct bio *bp2; 88 struct g_geom *gp; 89 90 gp = bp->bio_to->geom; 91 switch(bp->bio_cmd) { 92 case BIO_READ: 93 case BIO_WRITE: 94 case BIO_DELETE: 95 bp2 = g_clone_bio(bp); 96 if (bp2 == NULL) 97 g_io_deliver(bp, ENOMEM); 98 else { 99 bp2->bio_done = g_std_done; 100 g_io_request(bp2, LIST_FIRST(&gp->consumer)); 101 } 102 return; 103 default: 104 g_io_deliver(bp, EOPNOTSUPP); 105 return; 106 } 107 } 108 109 static int 110 gv_access(struct g_provider *pp, int dr, int dw, int de) 111 { 112 struct g_geom *gp; 113 struct g_consumer *cp; 114 int error; 115 116 gp = pp->geom; 117 error = ENXIO; 118 cp = LIST_FIRST(&gp->consumer); 119 error = g_access(cp, dr, dw, de); 120 return (error); 121 } 122 123 static void 124 gv_init(struct g_class *mp) 125 { 126 struct g_geom *gp; 127 struct gv_softc *sc; 128 129 g_trace(G_T_TOPOLOGY, "gv_init(%p)", mp); 130 131 gp = g_new_geomf(mp, "VINUM"); 132 gp->spoiled = gv_orphan; 133 gp->orphan = gv_orphan; 134 gp->access = gv_access; 135 gp->start = gv_start; 136 gp->softc = g_malloc(sizeof(struct gv_softc), M_WAITOK | M_ZERO); 137 sc = gp->softc; 138 sc->geom = gp; 139 LIST_INIT(&sc->drives); 140 LIST_INIT(&sc->subdisks); 141 LIST_INIT(&sc->plexes); 142 LIST_INIT(&sc->volumes); 143 } 144 145 /* Handle userland requests for creating new objects. */ 146 int 147 gv_create(struct g_geom *gp, struct gctl_req *req) 148 { 149 struct gv_softc *sc; 150 struct gv_drive *d, *d2; 151 struct gv_plex *p, *p2; 152 struct gv_sd *s, *s2; 153 struct gv_volume *v, *v2; 154 struct g_consumer *cp; 155 struct g_provider *pp; 156 int error, i, *drives, *plexes, *subdisks, *volumes; 157 char buf[20], errstr[ERRBUFSIZ]; 158 159 g_topology_assert(); 160 161 sc = gp->softc; 162 163 /* Find out how many of each object have been passed in. */ 164 volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes)); 165 plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes)); 166 subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks)); 167 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 168 if (volumes == NULL || plexes == NULL || subdisks == NULL || 169 drives == NULL) { 170 gctl_error(req, "number of objects not given"); 171 return (-1); 172 } 173 174 /* First, handle drive definitions ... */ 175 for (i = 0; i < *drives; i++) { 176 snprintf(buf, sizeof(buf), "drive%d", i); 177 d2 = gctl_get_paraml(req, buf, sizeof(*d2)); 178 if (d2 == NULL) { 179 gctl_error(req, "no drive definition given"); 180 return (-1); 181 } 182 d = gv_find_drive(sc, d2->name); 183 if (d != NULL) { 184 gctl_error(req, "drive '%s' is already known", 185 d->name); 186 continue; 187 } 188 189 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 190 bcopy(d2, d, sizeof(*d)); 191 192 /* 193 * Make sure that the provider specified in the drive 194 * specification is an active GEOM provider. 195 */ 196 pp = g_provider_by_name(d->device); 197 if (pp == NULL) { 198 gctl_error(req, "%s: drive not found", d->device); 199 g_free(d); 200 return (-1); 201 } 202 d->size = pp->mediasize - GV_DATA_START; 203 d->avail = d->size; 204 205 gv_config_new_drive(d); 206 207 d->flags |= GV_DRIVE_NEWBORN; 208 LIST_INSERT_HEAD(&sc->drives, d, drive); 209 } 210 211 /* ... then volume definitions ... */ 212 for (i = 0; i < *volumes; i++) { 213 error = 0; 214 snprintf(buf, sizeof(buf), "volume%d", i); 215 v2 = gctl_get_paraml(req, buf, sizeof(*v2)); 216 if (v2 == NULL) { 217 gctl_error(req, "no volume definition given"); 218 return (-1); 219 } 220 v = gv_find_vol(sc, v2->name); 221 if (v != NULL) { 222 gctl_error(req, "volume '%s' is already known", 223 v->name); 224 return (-1); 225 } 226 227 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 228 bcopy(v2, v, sizeof(*v)); 229 230 v->vinumconf = sc; 231 LIST_INIT(&v->plexes); 232 LIST_INSERT_HEAD(&sc->volumes, v, volume); 233 } 234 235 /* ... then plex definitions ... */ 236 for (i = 0; i < *plexes; i++) { 237 error = 0; 238 snprintf(buf, sizeof(buf), "plex%d", i); 239 p2 = gctl_get_paraml(req, buf, sizeof(*p2)); 240 if (p2 == NULL) { 241 gctl_error(req, "no plex definition given"); 242 return (-1); 243 } 244 p = gv_find_plex(sc, p2->name); 245 if (p != NULL) { 246 gctl_error(req, "plex '%s' is already known", p->name); 247 return (-1); 248 } 249 250 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 251 bcopy(p2, p, sizeof(*p)); 252 253 /* Find the volume this plex should be attached to. */ 254 v = gv_find_vol(sc, p->volume); 255 if (v == NULL) { 256 gctl_error(req, "volume '%s' not found", p->volume); 257 g_free(p); 258 continue; 259 } 260 if (v->plexcount) 261 p->flags |= GV_PLEX_ADDED; 262 p->vol_sc = v; 263 v->plexcount++; 264 LIST_INSERT_HEAD(&v->plexes, p, in_volume); 265 266 p->vinumconf = sc; 267 p->flags |= GV_PLEX_NEWBORN; 268 LIST_INIT(&p->subdisks); 269 LIST_INSERT_HEAD(&sc->plexes, p, plex); 270 } 271 272 /* ... and finally, subdisk definitions. */ 273 for (i = 0; i < *subdisks; i++) { 274 error = 0; 275 snprintf(buf, sizeof(buf), "sd%d", i); 276 s2 = gctl_get_paraml(req, buf, sizeof(*s2)); 277 if (s2 == NULL) { 278 gctl_error(req, "no subdisk definition given"); 279 return (-1); 280 } 281 s = gv_find_sd(sc, s2->name); 282 if (s != NULL) { 283 gctl_error(req, "subdisk '%s' is already known", 284 s->name); 285 return (-1); 286 } 287 288 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 289 bcopy(s2, s, sizeof(*s)); 290 291 /* Find the drive where this subdisk should be put on. */ 292 d = gv_find_drive(sc, s->drive); 293 294 /* drive not found - XXX */ 295 if (d == NULL) { 296 gctl_error(req, "drive '%s' not found", s->drive); 297 g_free(s); 298 continue; 299 } 300 301 /* Find the plex where this subdisk belongs to. */ 302 p = gv_find_plex(sc, s->plex); 303 304 /* plex not found - XXX */ 305 if (p == NULL) { 306 gctl_error(req, "plex '%s' not found\n", s->plex); 307 g_free(s); 308 continue; 309 } 310 311 /* 312 * First we give the subdisk to the drive, to handle autosized 313 * values ... 314 */ 315 error = gv_sd_to_drive(sc, d, s, errstr, sizeof(errstr)); 316 if (error) { 317 gctl_error(req, errstr); 318 g_free(s); 319 continue; 320 } 321 322 /* 323 * Then, we give the subdisk to the plex; we check if the 324 * given values are correct and maybe adjust them. 325 */ 326 error = gv_sd_to_plex(p, s, 1); 327 if (error) { 328 gctl_error(req, "GEOM_VINUM: couldn't give sd '%s' " 329 "to plex '%s'\n", s->name, p->name); 330 if (s->drive_sc) 331 LIST_REMOVE(s, from_drive); 332 gv_free_sd(s); 333 g_free(s); 334 /* 335 * If this subdisk can't be created, we won't create 336 * the attached plex either, if it is also a new one. 337 */ 338 if (!(p->flags & GV_PLEX_NEWBORN)) 339 continue; 340 LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) { 341 if (s->drive_sc) 342 LIST_REMOVE(s, from_drive); 343 p->sdcount--; 344 LIST_REMOVE(s, in_plex); 345 LIST_REMOVE(s, sd); 346 gv_free_sd(s); 347 g_free(s); 348 } 349 if (p->vol_sc != NULL) { 350 LIST_REMOVE(p, in_volume); 351 p->vol_sc->plexcount--; 352 } 353 LIST_REMOVE(p, plex); 354 g_free(p); 355 continue; 356 } 357 s->flags |= GV_SD_NEWBORN; 358 359 s->vinumconf = sc; 360 LIST_INSERT_HEAD(&sc->subdisks, s, sd); 361 } 362 363 LIST_FOREACH(s, &sc->subdisks, sd) 364 gv_update_sd_state(s); 365 LIST_FOREACH(p, &sc->plexes, plex) 366 gv_update_plex_config(p); 367 LIST_FOREACH(v, &sc->volumes, volume) 368 gv_update_vol_state(v); 369 370 /* 371 * Write out the configuration to each drive. If the drive doesn't 372 * have a valid geom_slice geom yet, attach it temporarily to our VINUM 373 * geom. 374 */ 375 LIST_FOREACH(d, &sc->drives, drive) { 376 if (d->geom == NULL) { 377 /* 378 * XXX if the provider disapears before we get a chance 379 * to write the config out to the drive, should this 380 * be handled any differently? 381 */ 382 pp = g_provider_by_name(d->device); 383 if (pp == NULL) { 384 G_VINUM_DEBUG(0, "%s: drive disappeared?", 385 d->device); 386 continue; 387 } 388 cp = g_new_consumer(gp); 389 g_attach(cp, pp); 390 gv_save_config(cp, d, sc); 391 g_detach(cp); 392 g_destroy_consumer(cp); 393 } else 394 gv_save_config(NULL, d, sc); 395 d->flags &= ~GV_DRIVE_NEWBORN; 396 } 397 398 return (0); 399 } 400 401 static void 402 gv_config(struct gctl_req *req, struct g_class *mp, char const *verb) 403 { 404 struct g_geom *gp; 405 struct gv_softc *sc; 406 struct sbuf *sb; 407 char *comment; 408 409 g_topology_assert(); 410 411 gp = LIST_FIRST(&mp->geom); 412 sc = gp->softc; 413 414 if (!strcmp(verb, "list")) { 415 gv_list(gp, req); 416 417 /* Save our configuration back to disk. */ 418 } else if (!strcmp(verb, "saveconfig")) { 419 420 gv_save_config_all(sc); 421 422 /* Return configuration in string form. */ 423 } else if (!strcmp(verb, "getconfig")) { 424 comment = gctl_get_param(req, "comment", NULL); 425 if (comment == NULL) { 426 gctl_error(req, "no comment parameter given"); 427 return; 428 } 429 sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 430 gv_format_config(sc, sb, 0, comment); 431 sbuf_finish(sb); 432 gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); 433 sbuf_delete(sb); 434 435 } else if (!strcmp(verb, "create")) { 436 gv_create(gp, req); 437 438 } else if (!strcmp(verb, "move")) { 439 gv_move(gp, req); 440 441 } else if (!strcmp(verb, "parityop")) { 442 gv_parityop(gp, req); 443 444 } else if (!strcmp(verb, "remove")) { 445 gv_remove(gp, req); 446 447 } else if (!strcmp(verb, "rename")) { 448 gv_rename(gp, req); 449 450 } else if (!strcmp(verb, "resetconfig")) { 451 gv_resetconfig(gp, req); 452 453 } else if (!strcmp(verb, "start")) { 454 gv_start_obj(gp, req); 455 456 } else if (!strcmp(verb, "setstate")) { 457 gv_setstate(gp, req); 458 459 } else 460 gctl_error(req, "Unknown verb parameter"); 461 } 462 463 #if 0 464 static int 465 gv_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 466 { 467 struct g_geom *gp2; 468 struct g_consumer *cp; 469 struct gv_softc *sc; 470 struct gv_drive *d, *d2; 471 struct gv_plex *p, *p2; 472 struct gv_sd *s, *s2; 473 struct gv_volume *v, *v2; 474 struct gv_freelist *fl, *fl2; 475 476 g_trace(G_T_TOPOLOGY, "gv_destroy_geom: %s", gp->name); 477 g_topology_assert(); 478 479 KASSERT(gp != NULL, ("gv_destroy_geom: null gp")); 480 KASSERT(gp->softc != NULL, ("gv_destroy_geom: null sc")); 481 482 sc = gp->softc; 483 484 /* 485 * Check if any of our drives is still open; if so, refuse destruction. 486 */ 487 LIST_FOREACH(d, &sc->drives, drive) { 488 gp2 = d->geom; 489 cp = LIST_FIRST(&gp2->consumer); 490 if (cp != NULL) 491 g_access(cp, -1, -1, -1); 492 if (gv_is_open(gp2)) 493 return (EBUSY); 494 } 495 496 /* Clean up and deallocate what we allocated. */ 497 LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) { 498 LIST_REMOVE(d, drive); 499 g_free(d->hdr); 500 d->hdr = NULL; 501 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 502 d->freelist_entries--; 503 LIST_REMOVE(fl, freelist); 504 g_free(fl); 505 fl = NULL; 506 } 507 d->geom->softc = NULL; 508 g_free(d); 509 } 510 511 LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) { 512 LIST_REMOVE(s, sd); 513 s->drive_sc = NULL; 514 s->plex_sc = NULL; 515 s->provider = NULL; 516 s->consumer = NULL; 517 g_free(s); 518 } 519 520 LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) { 521 LIST_REMOVE(p, plex); 522 gv_kill_thread(p); 523 p->vol_sc = NULL; 524 p->geom->softc = NULL; 525 p->provider = NULL; 526 p->consumer = NULL; 527 if (p->org == GV_PLEX_RAID5) { 528 mtx_destroy(&p->worklist_mtx); 529 } 530 g_free(p); 531 } 532 533 LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) { 534 LIST_REMOVE(v, volume); 535 v->geom->softc = NULL; 536 g_free(v); 537 } 538 539 gp->softc = NULL; 540 g_free(sc); 541 g_wither_geom(gp, ENXIO); 542 return (0); 543 } 544 #endif 545 546 #define VINUM_CLASS_NAME "VINUM" 547 548 static struct g_class g_vinum_class = { 549 .name = VINUM_CLASS_NAME, 550 .version = G_VERSION, 551 .init = gv_init, 552 /*.destroy_geom = gv_destroy_geom,*/ 553 .ctlreq = gv_config, 554 }; 555 556 DECLARE_GEOM_CLASS(g_vinum_class, g_vinum); 557