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