1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2007 Lukas Ertl 5 * Copyright (c) 2007, 2009 Ulf Lilleengen 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/bio.h> 35 #include <sys/conf.h> 36 #include <sys/jail.h> 37 #include <sys/kernel.h> 38 #include <sys/malloc.h> 39 #include <sys/systm.h> 40 41 #include <geom/geom.h> 42 #include <geom/geom_dbg.h> 43 #include <geom/vinum/geom_vinum_var.h> 44 #include <geom/vinum/geom_vinum.h> 45 46 #define DEFAULT_STRIPESIZE 262144 47 48 /* 49 * Create a new drive object, either by user request, during taste of the drive 50 * itself, or because it was referenced by a subdisk during taste. 51 */ 52 int 53 gv_create_drive(struct gv_softc *sc, struct gv_drive *d) 54 { 55 struct g_geom *gp; 56 struct g_provider *pp; 57 struct g_consumer *cp, *cp2; 58 struct gv_drive *d2; 59 struct gv_hdr *hdr; 60 struct gv_freelist *fl; 61 62 KASSERT(d != NULL, ("gv_create_drive: NULL d")); 63 64 gp = sc->geom; 65 66 pp = NULL; 67 cp = cp2 = NULL; 68 69 /* The drive already has a consumer if it was tasted before. */ 70 if (d->consumer != NULL) { 71 cp = d->consumer; 72 cp->private = d; 73 pp = cp->provider; 74 } else if (!(d->flags & GV_DRIVE_REFERENCED)) { 75 if (gv_find_drive(sc, d->name) != NULL) { 76 G_VINUM_DEBUG(0, "drive '%s' already exists", d->name); 77 g_free(d); 78 return (GV_ERR_CREATE); 79 } 80 81 if (gv_find_drive_device(sc, d->device) != NULL) { 82 G_VINUM_DEBUG(0, "provider '%s' already in use by " 83 "gvinum", d->device); 84 return (GV_ERR_CREATE); 85 } 86 87 pp = g_provider_by_name(d->device); 88 if (pp == NULL) { 89 G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared", 90 d->name, d->device); 91 g_free(d); 92 return (GV_ERR_CREATE); 93 } 94 95 g_topology_lock(); 96 cp = g_new_consumer(gp); 97 if (g_attach(cp, pp) != 0) { 98 g_destroy_consumer(cp); 99 g_topology_unlock(); 100 G_VINUM_DEBUG(0, "create drive '%s': unable to attach", 101 d->name); 102 g_free(d); 103 return (GV_ERR_CREATE); 104 } 105 g_topology_unlock(); 106 107 d->consumer = cp; 108 cp->private = d; 109 } 110 111 /* 112 * If this was just a "referenced" drive, we're almost finished, but 113 * insert this drive not on the head of the drives list, as 114 * gv_drive_is_newer() expects a "real" drive from LIST_FIRST(). 115 */ 116 if (d->flags & GV_DRIVE_REFERENCED) { 117 snprintf(d->device, sizeof(d->device), "???"); 118 d2 = LIST_FIRST(&sc->drives); 119 if (d2 == NULL) 120 LIST_INSERT_HEAD(&sc->drives, d, drive); 121 else 122 LIST_INSERT_AFTER(d2, d, drive); 123 return (0); 124 } 125 126 /* 127 * Update access counts of the new drive to those of an already 128 * existing drive. 129 */ 130 LIST_FOREACH(d2, &sc->drives, drive) { 131 if ((d == d2) || (d2->consumer == NULL)) 132 continue; 133 134 cp2 = d2->consumer; 135 g_topology_lock(); 136 if ((cp2->acr || cp2->acw || cp2->ace) && 137 (g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) { 138 g_detach(cp); 139 g_destroy_consumer(cp); 140 g_topology_unlock(); 141 G_VINUM_DEBUG(0, "create drive '%s': unable to update " 142 "access counts", d->name); 143 g_free(d->hdr); 144 g_free(d); 145 return (GV_ERR_CREATE); 146 } 147 g_topology_unlock(); 148 break; 149 } 150 151 d->size = pp->mediasize - GV_DATA_START; 152 d->avail = d->size; 153 d->vinumconf = sc; 154 LIST_INIT(&d->subdisks); 155 LIST_INIT(&d->freelist); 156 157 /* The header might have been set during taste. */ 158 if (d->hdr == NULL) { 159 hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO); 160 hdr->magic = GV_MAGIC; 161 hdr->config_length = GV_CFG_LEN; 162 getcredhostname(NULL, hdr->label.sysname, GV_HOSTNAME_LEN); 163 strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name)); 164 microtime(&hdr->label.date_of_birth); 165 d->hdr = hdr; 166 } 167 168 /* We also need a freelist entry. */ 169 fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO); 170 fl->offset = GV_DATA_START; 171 fl->size = d->avail; 172 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 173 d->freelist_entries = 1; 174 175 if (gv_find_drive(sc, d->name) == NULL) 176 LIST_INSERT_HEAD(&sc->drives, d, drive); 177 178 gv_set_drive_state(d, GV_DRIVE_UP, 0); 179 return (0); 180 } 181 182 int 183 gv_create_volume(struct gv_softc *sc, struct gv_volume *v) 184 { 185 KASSERT(v != NULL, ("gv_create_volume: NULL v")); 186 187 v->vinumconf = sc; 188 v->flags |= GV_VOL_NEWBORN; 189 LIST_INIT(&v->plexes); 190 LIST_INSERT_HEAD(&sc->volumes, v, volume); 191 v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 192 bioq_init(v->wqueue); 193 return (0); 194 } 195 196 int 197 gv_create_plex(struct gv_softc *sc, struct gv_plex *p) 198 { 199 struct gv_volume *v; 200 201 KASSERT(p != NULL, ("gv_create_plex: NULL p")); 202 203 /* Find the volume this plex should be attached to. */ 204 v = gv_find_vol(sc, p->volume); 205 if (v == NULL) { 206 G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found", 207 p->name, p->volume); 208 g_free(p); 209 return (GV_ERR_CREATE); 210 } 211 if (!(v->flags & GV_VOL_NEWBORN)) 212 p->flags |= GV_PLEX_ADDED; 213 p->vol_sc = v; 214 v->plexcount++; 215 p->vinumconf = sc; 216 p->synced = 0; 217 p->flags |= GV_PLEX_NEWBORN; 218 LIST_INSERT_HEAD(&v->plexes, p, in_volume); 219 LIST_INIT(&p->subdisks); 220 TAILQ_INIT(&p->packets); 221 LIST_INSERT_HEAD(&sc->plexes, p, plex); 222 p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 223 bioq_init(p->bqueue); 224 p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 225 bioq_init(p->wqueue); 226 p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 227 bioq_init(p->rqueue); 228 return (0); 229 } 230 231 int 232 gv_create_sd(struct gv_softc *sc, struct gv_sd *s) 233 { 234 struct gv_plex *p; 235 struct gv_drive *d; 236 237 KASSERT(s != NULL, ("gv_create_sd: NULL s")); 238 239 /* Find the drive where this subdisk should be put on. */ 240 d = gv_find_drive(sc, s->drive); 241 if (d == NULL) { 242 /* 243 * It's possible that the subdisk references a drive that 244 * doesn't exist yet (during the taste process), so create a 245 * practically empty "referenced" drive. 246 */ 247 if (s->flags & GV_SD_TASTED) { 248 d = g_malloc(sizeof(struct gv_drive), 249 M_WAITOK | M_ZERO); 250 d->flags |= GV_DRIVE_REFERENCED; 251 strlcpy(d->name, s->drive, sizeof(d->name)); 252 gv_create_drive(sc, d); 253 } else { 254 G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found", 255 s->name, s->drive); 256 g_free(s); 257 return (GV_ERR_CREATE); 258 } 259 } 260 261 /* Find the plex where this subdisk belongs to. */ 262 p = gv_find_plex(sc, s->plex); 263 if (p == NULL) { 264 G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found", 265 s->name, s->plex); 266 g_free(s); 267 return (GV_ERR_CREATE); 268 } 269 270 /* 271 * First we give the subdisk to the drive, to handle autosized 272 * values ... 273 */ 274 if (gv_sd_to_drive(s, d) != 0) { 275 g_free(s); 276 return (GV_ERR_CREATE); 277 } 278 279 /* 280 * Then, we give the subdisk to the plex; we check if the 281 * given values are correct and maybe adjust them. 282 */ 283 if (gv_sd_to_plex(s, p) != 0) { 284 G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'", 285 s->name, p->name); 286 if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED)) 287 LIST_REMOVE(s, from_drive); 288 gv_free_sd(s); 289 g_free(s); 290 /* 291 * If this subdisk can't be created, we won't create 292 * the attached plex either, if it is also a new one. 293 */ 294 if (!(p->flags & GV_PLEX_NEWBORN)) 295 return (GV_ERR_CREATE); 296 gv_rm_plex(sc, p); 297 return (GV_ERR_CREATE); 298 } 299 s->flags |= GV_SD_NEWBORN; 300 301 s->vinumconf = sc; 302 LIST_INSERT_HEAD(&sc->subdisks, s, sd); 303 304 return (0); 305 } 306 307 /* 308 * Create a concatenated volume from specified drives or drivegroups. 309 */ 310 void 311 gv_concat(struct g_geom *gp, struct gctl_req *req) 312 { 313 struct gv_drive *d; 314 struct gv_sd *s; 315 struct gv_volume *v; 316 struct gv_plex *p; 317 struct gv_softc *sc; 318 char *drive, buf[30], *vol; 319 int *drives, dcount; 320 321 sc = gp->softc; 322 dcount = 0; 323 vol = gctl_get_param(req, "name", NULL); 324 if (vol == NULL) { 325 gctl_error(req, "volume name not given"); 326 return; 327 } 328 329 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 330 331 if (drives == NULL) { 332 gctl_error(req, "drive names not given"); 333 return; 334 } 335 336 /* First we create the volume. */ 337 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 338 strlcpy(v->name, vol, sizeof(v->name)); 339 v->state = GV_VOL_UP; 340 gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 341 342 /* Then we create the plex. */ 343 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 344 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 345 strlcpy(p->volume, v->name, sizeof(p->volume)); 346 p->org = GV_PLEX_CONCAT; 347 p->stripesize = 0; 348 gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 349 350 /* Drives are first (right now) priority */ 351 for (dcount = 0; dcount < *drives; dcount++) { 352 snprintf(buf, sizeof(buf), "drive%d", dcount); 353 drive = gctl_get_param(req, buf, NULL); 354 d = gv_find_drive(sc, drive); 355 if (d == NULL) { 356 gctl_error(req, "No such drive '%s'", drive); 357 continue; 358 } 359 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 360 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 361 strlcpy(s->plex, p->name, sizeof(s->plex)); 362 strlcpy(s->drive, drive, sizeof(s->drive)); 363 s->plex_offset = -1; 364 s->drive_offset = -1; 365 s->size = -1; 366 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 367 } 368 gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 369 gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 370 } 371 372 /* 373 * Create a mirrored volume from specified drives or drivegroups. 374 */ 375 void 376 gv_mirror(struct g_geom *gp, struct gctl_req *req) 377 { 378 struct gv_drive *d; 379 struct gv_sd *s; 380 struct gv_volume *v; 381 struct gv_plex *p; 382 struct gv_softc *sc; 383 char *drive, buf[30], *vol; 384 int *drives, *flags, dcount, pcount, scount; 385 386 sc = gp->softc; 387 dcount = 0; 388 scount = 0; 389 pcount = 0; 390 vol = gctl_get_param(req, "name", NULL); 391 if (vol == NULL) { 392 gctl_error(req, "volume name not given"); 393 return; 394 } 395 396 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 397 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 398 399 if (drives == NULL) { 400 gctl_error(req, "drive names not given"); 401 return; 402 } 403 404 /* We must have an even number of drives. */ 405 if (*drives % 2 != 0) { 406 gctl_error(req, "mirror organization must have an even number " 407 "of drives"); 408 return; 409 } 410 if (*flags & GV_FLAG_S && *drives < 4) { 411 gctl_error(req, "must have at least 4 drives for striped plex"); 412 return; 413 } 414 415 /* First we create the volume. */ 416 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 417 strlcpy(v->name, vol, sizeof(v->name)); 418 v->state = GV_VOL_UP; 419 gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 420 421 /* Then we create the plexes. */ 422 for (pcount = 0; pcount < 2; pcount++) { 423 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 424 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, 425 pcount); 426 strlcpy(p->volume, v->name, sizeof(p->volume)); 427 if (*flags & GV_FLAG_S) { 428 p->org = GV_PLEX_STRIPED; 429 p->stripesize = DEFAULT_STRIPESIZE; 430 } else { 431 p->org = GV_PLEX_CONCAT; 432 p->stripesize = -1; 433 } 434 gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 435 436 /* 437 * We just gives each even drive to plex one, and each odd to 438 * plex two. 439 */ 440 scount = 0; 441 for (dcount = pcount; dcount < *drives; dcount += 2) { 442 snprintf(buf, sizeof(buf), "drive%d", dcount); 443 drive = gctl_get_param(req, buf, NULL); 444 d = gv_find_drive(sc, drive); 445 if (d == NULL) { 446 gctl_error(req, "No such drive '%s', aborting", 447 drive); 448 scount++; 449 break; 450 } 451 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 452 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, 453 scount); 454 strlcpy(s->plex, p->name, sizeof(s->plex)); 455 strlcpy(s->drive, drive, sizeof(s->drive)); 456 s->plex_offset = -1; 457 s->drive_offset = -1; 458 s->size = -1; 459 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 460 scount++; 461 } 462 } 463 gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 464 gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 465 } 466 467 void 468 gv_raid5(struct g_geom *gp, struct gctl_req *req) 469 { 470 struct gv_softc *sc; 471 struct gv_drive *d; 472 struct gv_volume *v; 473 struct gv_plex *p; 474 struct gv_sd *s; 475 int *drives, *flags, dcount; 476 char *vol, *drive, buf[30]; 477 off_t *stripesize; 478 479 sc = gp->softc; 480 481 vol = gctl_get_param(req, "name", NULL); 482 if (vol == NULL) { 483 gctl_error(req, "volume name not given"); 484 return; 485 } 486 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 487 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 488 stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize)); 489 490 if (stripesize == NULL) { 491 gctl_error(req, "no stripesize given"); 492 return; 493 } 494 495 if (drives == NULL) { 496 gctl_error(req, "drive names not given"); 497 return; 498 } 499 500 /* We must have at least three drives. */ 501 if (*drives < 3) { 502 gctl_error(req, "must have at least three drives for this " 503 "plex organisation"); 504 return; 505 } 506 /* First we create the volume. */ 507 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 508 strlcpy(v->name, vol, sizeof(v->name)); 509 v->state = GV_VOL_UP; 510 gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 511 512 /* Then we create the plex. */ 513 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 514 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 515 strlcpy(p->volume, v->name, sizeof(p->volume)); 516 p->org = GV_PLEX_RAID5; 517 p->stripesize = *stripesize; 518 gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 519 520 /* Create subdisks on drives. */ 521 for (dcount = 0; dcount < *drives; dcount++) { 522 snprintf(buf, sizeof(buf), "drive%d", dcount); 523 drive = gctl_get_param(req, buf, NULL); 524 d = gv_find_drive(sc, drive); 525 if (d == NULL) { 526 gctl_error(req, "No such drive '%s'", drive); 527 continue; 528 } 529 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 530 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 531 strlcpy(s->plex, p->name, sizeof(s->plex)); 532 strlcpy(s->drive, drive, sizeof(s->drive)); 533 s->plex_offset = -1; 534 s->drive_offset = -1; 535 s->size = -1; 536 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 537 } 538 gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 539 gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 540 } 541 542 /* 543 * Create a striped volume from specified drives or drivegroups. 544 */ 545 void 546 gv_stripe(struct g_geom *gp, struct gctl_req *req) 547 { 548 struct gv_drive *d; 549 struct gv_sd *s; 550 struct gv_volume *v; 551 struct gv_plex *p; 552 struct gv_softc *sc; 553 char *drive, buf[30], *vol; 554 int *drives, *flags, dcount; 555 556 sc = gp->softc; 557 dcount = 0; 558 vol = gctl_get_param(req, "name", NULL); 559 if (vol == NULL) { 560 gctl_error(req, "volume name not given"); 561 return; 562 } 563 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 564 drives = gctl_get_paraml(req, "drives", sizeof(*drives)); 565 566 if (drives == NULL) { 567 gctl_error(req, "drive names not given"); 568 return; 569 } 570 571 /* We must have at least two drives. */ 572 if (*drives < 2) { 573 gctl_error(req, "must have at least 2 drives"); 574 return; 575 } 576 577 /* First we create the volume. */ 578 v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); 579 strlcpy(v->name, vol, sizeof(v->name)); 580 v->state = GV_VOL_UP; 581 gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0); 582 583 /* Then we create the plex. */ 584 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); 585 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount); 586 strlcpy(p->volume, v->name, sizeof(p->volume)); 587 p->org = GV_PLEX_STRIPED; 588 p->stripesize = 262144; 589 gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0); 590 591 /* Create subdisks on drives. */ 592 for (dcount = 0; dcount < *drives; dcount++) { 593 snprintf(buf, sizeof(buf), "drive%d", dcount); 594 drive = gctl_get_param(req, buf, NULL); 595 d = gv_find_drive(sc, drive); 596 if (d == NULL) { 597 gctl_error(req, "No such drive '%s'", drive); 598 continue; 599 } 600 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); 601 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount); 602 strlcpy(s->plex, p->name, sizeof(s->plex)); 603 strlcpy(s->drive, drive, sizeof(s->drive)); 604 s->plex_offset = -1; 605 s->drive_offset = -1; 606 s->size = -1; 607 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0); 608 } 609 gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0); 610 gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0); 611 } 612