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