1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/module.h> 33 #include <sys/lock.h> 34 #include <sys/mutex.h> 35 #include <sys/sx.h> 36 #include <sys/bio.h> 37 #include <sys/sbuf.h> 38 #include <sys/sysctl.h> 39 #include <sys/malloc.h> 40 #include <geom/geom.h> 41 #include <geom/geom_dbg.h> 42 #include <geom/concat/g_concat.h> 43 44 FEATURE(geom_concat, "GEOM concatenation support"); 45 46 static MALLOC_DEFINE(M_CONCAT, "concat_data", "GEOM_CONCAT Data"); 47 48 SYSCTL_DECL(_kern_geom); 49 static SYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 50 "GEOM_CONCAT stuff"); 51 static u_int g_concat_debug = 0; 52 SYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RWTUN, &g_concat_debug, 0, 53 "Debug level"); 54 55 static int g_concat_destroy(struct g_concat_softc *sc, boolean_t force); 56 static int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp, 57 struct g_geom *gp); 58 59 static g_taste_t g_concat_taste; 60 static g_ctl_req_t g_concat_config; 61 static g_dumpconf_t g_concat_dumpconf; 62 63 struct g_class g_concat_class = { 64 .name = G_CONCAT_CLASS_NAME, 65 .version = G_VERSION, 66 .ctlreq = g_concat_config, 67 .taste = g_concat_taste, 68 .destroy_geom = g_concat_destroy_geom 69 }; 70 71 /* 72 * Greatest Common Divisor. 73 */ 74 static u_int 75 gcd(u_int a, u_int b) 76 { 77 u_int c; 78 79 while (b != 0) { 80 c = a; 81 a = b; 82 b = (c % b); 83 } 84 return (a); 85 } 86 87 /* 88 * Least Common Multiple. 89 */ 90 static u_int 91 lcm(u_int a, u_int b) 92 { 93 94 return ((a * b) / gcd(a, b)); 95 } 96 97 /* 98 * Return the number of valid disks. 99 */ 100 static u_int 101 g_concat_nvalid(struct g_concat_softc *sc) 102 { 103 u_int no; 104 struct g_concat_disk *disk; 105 106 sx_assert(&sc->sc_disks_lock, SA_LOCKED); 107 108 no = 0; 109 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 110 if (disk->d_consumer != NULL) 111 no++; 112 } 113 114 return (no); 115 } 116 117 static void 118 g_concat_remove_disk(struct g_concat_disk *disk) 119 { 120 struct g_consumer *cp; 121 struct g_concat_softc *sc; 122 123 g_topology_assert(); 124 KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); 125 sc = disk->d_softc; 126 cp = disk->d_consumer; 127 128 if (!disk->d_removed) { 129 G_CONCAT_DEBUG(0, "Disk %s removed from %s.", 130 cp->provider->name, sc->sc_name); 131 disk->d_removed = 1; 132 } 133 134 if (sc->sc_provider != NULL) { 135 G_CONCAT_DEBUG(0, "Device %s deactivated.", 136 sc->sc_provider->name); 137 g_wither_provider(sc->sc_provider, ENXIO); 138 sc->sc_provider = NULL; 139 } 140 141 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 142 return; 143 disk->d_consumer = NULL; 144 g_detach(cp); 145 g_destroy_consumer(cp); 146 /* If there are no valid disks anymore, remove device. */ 147 if (LIST_EMPTY(&sc->sc_geom->consumer)) 148 g_concat_destroy(sc, 1); 149 } 150 151 static void 152 g_concat_orphan(struct g_consumer *cp) 153 { 154 struct g_concat_softc *sc; 155 struct g_concat_disk *disk; 156 struct g_geom *gp; 157 158 g_topology_assert(); 159 gp = cp->geom; 160 sc = gp->softc; 161 if (sc == NULL) 162 return; 163 164 disk = cp->private; 165 if (disk == NULL) /* Possible? */ 166 return; 167 g_concat_remove_disk(disk); 168 } 169 170 static int 171 g_concat_access(struct g_provider *pp, int dr, int dw, int de) 172 { 173 struct g_consumer *cp1, *cp2, *tmp; 174 struct g_concat_disk *disk; 175 struct g_geom *gp; 176 struct g_concat_softc *sc; 177 int error; 178 179 g_topology_assert(); 180 gp = pp->geom; 181 sc = gp->softc; 182 183 /* On first open, grab an extra "exclusive" bit */ 184 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 185 de++; 186 /* ... and let go of it on last close */ 187 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 188 de--; 189 190 sx_slock(&sc->sc_disks_lock); 191 LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { 192 error = g_access(cp1, dr, dw, de); 193 if (error != 0) 194 goto fail; 195 disk = cp1->private; 196 if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && 197 disk->d_removed) { 198 g_concat_remove_disk(disk); /* May destroy geom. */ 199 } 200 } 201 sx_sunlock(&sc->sc_disks_lock); 202 return (0); 203 204 fail: 205 sx_sunlock(&sc->sc_disks_lock); 206 LIST_FOREACH(cp2, &gp->consumer, consumer) { 207 if (cp1 == cp2) 208 break; 209 g_access(cp2, -dr, -dw, -de); 210 } 211 return (error); 212 } 213 214 static void 215 g_concat_candelete(struct bio *bp) 216 { 217 struct g_concat_softc *sc; 218 struct g_concat_disk *disk; 219 int val; 220 221 sc = bp->bio_to->geom->softc; 222 sx_assert(&sc->sc_disks_lock, SX_LOCKED); 223 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 224 if (!disk->d_removed && disk->d_candelete) 225 break; 226 } 227 val = disk != NULL; 228 g_handleattr(bp, "GEOM::candelete", &val, sizeof(val)); 229 } 230 231 static void 232 g_concat_kernel_dump(struct bio *bp) 233 { 234 struct g_concat_softc *sc; 235 struct g_concat_disk *disk; 236 struct bio *cbp; 237 struct g_kerneldump *gkd; 238 239 sc = bp->bio_to->geom->softc; 240 gkd = (struct g_kerneldump *)bp->bio_data; 241 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 242 if (disk->d_start <= gkd->offset && 243 disk->d_end > gkd->offset) 244 break; 245 } 246 if (disk == NULL) { 247 g_io_deliver(bp, EOPNOTSUPP); 248 return; 249 } 250 251 gkd->offset -= disk->d_start; 252 if (gkd->length > disk->d_end - disk->d_start - gkd->offset) 253 gkd->length = disk->d_end - disk->d_start - gkd->offset; 254 cbp = g_clone_bio(bp); 255 if (cbp == NULL) { 256 g_io_deliver(bp, ENOMEM); 257 return; 258 } 259 cbp->bio_done = g_std_done; 260 g_io_request(cbp, disk->d_consumer); 261 G_CONCAT_DEBUG(1, "Kernel dump will go to %s.", 262 disk->d_consumer->provider->name); 263 } 264 265 static void 266 g_concat_done(struct bio *bp) 267 { 268 struct g_concat_softc *sc; 269 struct bio *pbp; 270 271 pbp = bp->bio_parent; 272 sc = pbp->bio_to->geom->softc; 273 mtx_lock(&sc->sc_completion_lock); 274 if (pbp->bio_error == 0) 275 pbp->bio_error = bp->bio_error; 276 pbp->bio_completed += bp->bio_completed; 277 pbp->bio_inbed++; 278 if (pbp->bio_children == pbp->bio_inbed) { 279 mtx_unlock(&sc->sc_completion_lock); 280 g_io_deliver(pbp, pbp->bio_error); 281 } else 282 mtx_unlock(&sc->sc_completion_lock); 283 g_destroy_bio(bp); 284 } 285 286 /* 287 * Called for both BIO_FLUSH and BIO_SPEEDUP. Just pass the call down 288 */ 289 static void 290 g_concat_passdown(struct g_concat_softc *sc, struct bio *bp) 291 { 292 struct bio_queue_head queue; 293 struct g_consumer *cp; 294 struct bio *cbp; 295 struct g_concat_disk *disk; 296 297 sx_assert(&sc->sc_disks_lock, SX_LOCKED); 298 299 bioq_init(&queue); 300 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 301 cbp = g_clone_bio(bp); 302 if (cbp == NULL) { 303 while ((cbp = bioq_takefirst(&queue)) != NULL) 304 g_destroy_bio(cbp); 305 if (bp->bio_error == 0) 306 bp->bio_error = ENOMEM; 307 g_io_deliver(bp, bp->bio_error); 308 return; 309 } 310 bioq_insert_tail(&queue, cbp); 311 cbp->bio_done = g_concat_done; 312 cbp->bio_caller1 = disk->d_consumer; 313 cbp->bio_to = disk->d_consumer->provider; 314 } 315 while ((cbp = bioq_takefirst(&queue)) != NULL) { 316 G_CONCAT_LOGREQ(cbp, "Sending request."); 317 cp = cbp->bio_caller1; 318 cbp->bio_caller1 = NULL; 319 g_io_request(cbp, cp); 320 } 321 } 322 323 static void 324 g_concat_start(struct bio *bp) 325 { 326 struct bio_queue_head queue; 327 struct g_concat_softc *sc; 328 struct g_concat_disk *disk; 329 struct g_provider *pp; 330 off_t offset, end, length, off, len; 331 struct bio *cbp; 332 char *addr; 333 334 pp = bp->bio_to; 335 sc = pp->geom->softc; 336 /* 337 * If sc == NULL, provider's error should be set and g_concat_start() 338 * should not be called at all. 339 */ 340 KASSERT(sc != NULL, 341 ("Provider's error should be set (error=%d)(device=%s).", 342 bp->bio_to->error, bp->bio_to->name)); 343 344 G_CONCAT_LOGREQ(bp, "Request received."); 345 sx_slock(&sc->sc_disks_lock); 346 347 switch (bp->bio_cmd) { 348 case BIO_READ: 349 case BIO_WRITE: 350 case BIO_DELETE: 351 break; 352 case BIO_SPEEDUP: 353 case BIO_FLUSH: 354 g_concat_passdown(sc, bp); 355 goto end; 356 case BIO_GETATTR: 357 if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 358 g_concat_kernel_dump(bp); 359 goto end; 360 } else if (strcmp("GEOM::candelete", bp->bio_attribute) == 0) { 361 g_concat_candelete(bp); 362 goto end; 363 } 364 /* To which provider it should be delivered? */ 365 /* FALLTHROUGH */ 366 default: 367 g_io_deliver(bp, EOPNOTSUPP); 368 goto end; 369 } 370 371 offset = bp->bio_offset; 372 length = bp->bio_length; 373 if ((bp->bio_flags & BIO_UNMAPPED) != 0) 374 addr = NULL; 375 else 376 addr = bp->bio_data; 377 end = offset + length; 378 379 bioq_init(&queue); 380 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 381 if (disk->d_end <= offset) 382 continue; 383 if (disk->d_start >= end) 384 break; 385 386 off = offset - disk->d_start; 387 len = MIN(length, disk->d_end - offset); 388 length -= len; 389 offset += len; 390 391 cbp = g_clone_bio(bp); 392 if (cbp == NULL) { 393 while ((cbp = bioq_takefirst(&queue)) != NULL) 394 g_destroy_bio(cbp); 395 if (bp->bio_error == 0) 396 bp->bio_error = ENOMEM; 397 g_io_deliver(bp, bp->bio_error); 398 goto end; 399 } 400 bioq_insert_tail(&queue, cbp); 401 /* 402 * Fill in the component buf structure. 403 */ 404 if (len == bp->bio_length) 405 cbp->bio_done = g_std_done; 406 else 407 cbp->bio_done = g_concat_done; 408 cbp->bio_offset = off; 409 cbp->bio_length = len; 410 if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 411 cbp->bio_ma_offset += (uintptr_t)addr; 412 cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; 413 cbp->bio_ma_offset %= PAGE_SIZE; 414 cbp->bio_ma_n = round_page(cbp->bio_ma_offset + 415 cbp->bio_length) / PAGE_SIZE; 416 } else 417 cbp->bio_data = addr; 418 addr += len; 419 cbp->bio_to = disk->d_consumer->provider; 420 cbp->bio_caller1 = disk; 421 422 if (length == 0) 423 break; 424 } 425 KASSERT(length == 0, 426 ("Length is still greater than 0 (class=%s, name=%s).", 427 bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 428 while ((cbp = bioq_takefirst(&queue)) != NULL) { 429 G_CONCAT_LOGREQ(cbp, "Sending request."); 430 disk = cbp->bio_caller1; 431 cbp->bio_caller1 = NULL; 432 g_io_request(cbp, disk->d_consumer); 433 } 434 end: 435 sx_sunlock(&sc->sc_disks_lock); 436 } 437 438 static void 439 g_concat_check_and_run(struct g_concat_softc *sc) 440 { 441 struct g_concat_disk *disk; 442 struct g_provider *dp, *pp; 443 u_int sectorsize = 0; 444 off_t start; 445 int error; 446 447 g_topology_assert(); 448 if (g_concat_nvalid(sc) != sc->sc_ndisks) 449 return; 450 451 pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name); 452 pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | 453 G_PF_ACCEPT_UNMAPPED; 454 start = 0; 455 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 456 dp = disk->d_consumer->provider; 457 disk->d_start = start; 458 disk->d_end = disk->d_start + dp->mediasize; 459 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 460 disk->d_end -= dp->sectorsize; 461 start = disk->d_end; 462 error = g_access(disk->d_consumer, 1, 0, 0); 463 if (error == 0) { 464 error = g_getattr("GEOM::candelete", disk->d_consumer, 465 &disk->d_candelete); 466 if (error != 0) 467 disk->d_candelete = 0; 468 (void)g_access(disk->d_consumer, -1, 0, 0); 469 } else 470 G_CONCAT_DEBUG(1, "Failed to access disk %s, error %d.", 471 dp->name, error); 472 if (disk == TAILQ_FIRST(&sc->sc_disks)) 473 sectorsize = dp->sectorsize; 474 else 475 sectorsize = lcm(sectorsize, dp->sectorsize); 476 477 /* A provider underneath us doesn't support unmapped */ 478 if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 479 G_CONCAT_DEBUG(1, "Cancelling unmapped " 480 "because of %s.", dp->name); 481 pp->flags &= ~G_PF_ACCEPT_UNMAPPED; 482 } 483 } 484 pp->sectorsize = sectorsize; 485 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 486 pp->mediasize = start; 487 dp = TAILQ_FIRST(&sc->sc_disks)->d_consumer->provider; 488 pp->stripesize = dp->stripesize; 489 pp->stripeoffset = dp->stripeoffset; 490 sc->sc_provider = pp; 491 g_error_provider(pp, 0); 492 493 G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 494 } 495 496 static int 497 g_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 498 { 499 struct g_provider *pp; 500 u_char *buf; 501 int error; 502 503 g_topology_assert(); 504 505 error = g_access(cp, 1, 0, 0); 506 if (error != 0) 507 return (error); 508 pp = cp->provider; 509 g_topology_unlock(); 510 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 511 &error); 512 g_topology_lock(); 513 g_access(cp, -1, 0, 0); 514 if (buf == NULL) 515 return (error); 516 517 /* Decode metadata. */ 518 concat_metadata_decode(buf, md); 519 g_free(buf); 520 521 return (0); 522 } 523 524 /* 525 * Add disk to given device. 526 */ 527 static int 528 g_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 529 { 530 struct g_concat_disk *disk; 531 struct g_consumer *cp, *fcp; 532 struct g_geom *gp; 533 int error; 534 535 g_topology_assert(); 536 537 sx_slock(&sc->sc_disks_lock); 538 539 /* Metadata corrupted? */ 540 if (no >= sc->sc_ndisks) { 541 sx_sunlock(&sc->sc_disks_lock); 542 return (EINVAL); 543 } 544 545 for (disk = TAILQ_FIRST(&sc->sc_disks); no > 0; no--) { 546 disk = TAILQ_NEXT(disk, d_next); 547 } 548 549 /* Check if disk is not already attached. */ 550 if (disk->d_consumer != NULL) { 551 sx_sunlock(&sc->sc_disks_lock); 552 return (EEXIST); 553 } 554 555 gp = sc->sc_geom; 556 fcp = LIST_FIRST(&gp->consumer); 557 558 cp = g_new_consumer(gp); 559 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 560 error = g_attach(cp, pp); 561 if (error != 0) { 562 sx_sunlock(&sc->sc_disks_lock); 563 g_destroy_consumer(cp); 564 return (error); 565 } 566 567 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 568 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 569 if (error != 0) { 570 sx_sunlock(&sc->sc_disks_lock); 571 g_detach(cp); 572 g_destroy_consumer(cp); 573 return (error); 574 } 575 } 576 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 577 struct g_concat_metadata md; 578 579 // temporarily give up the lock to avoid lock order violation 580 // due to topology unlock in g_concat_read_metadata 581 sx_sunlock(&sc->sc_disks_lock); 582 /* Re-read metadata. */ 583 error = g_concat_read_metadata(cp, &md); 584 sx_slock(&sc->sc_disks_lock); 585 586 if (error != 0) 587 goto fail; 588 589 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 590 strcmp(md.md_name, sc->sc_name) != 0 || 591 md.md_id != sc->sc_id) { 592 G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 593 goto fail; 594 } 595 596 disk->d_hardcoded = md.md_provider[0] != '\0'; 597 } else { 598 disk->d_hardcoded = false; 599 } 600 601 cp->private = disk; 602 disk->d_consumer = cp; 603 disk->d_softc = sc; 604 disk->d_start = 0; /* not yet */ 605 disk->d_end = 0; /* not yet */ 606 disk->d_removed = 0; 607 608 G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 609 610 g_concat_check_and_run(sc); 611 sx_sunlock(&sc->sc_disks_lock); // need lock for check_and_run 612 613 return (0); 614 fail: 615 sx_sunlock(&sc->sc_disks_lock); 616 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 617 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 618 g_detach(cp); 619 g_destroy_consumer(cp); 620 return (error); 621 } 622 623 static struct g_geom * 624 g_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 625 u_int type) 626 { 627 struct g_concat_softc *sc; 628 struct g_concat_disk *disk; 629 struct g_geom *gp; 630 u_int no; 631 632 G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 633 md->md_id); 634 635 /* One disks is minimum. */ 636 if (md->md_all < 1) 637 return (NULL); 638 639 /* Check for duplicate unit */ 640 LIST_FOREACH(gp, &mp->geom, geom) { 641 sc = gp->softc; 642 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 643 G_CONCAT_DEBUG(0, "Device %s already configured.", 644 gp->name); 645 return (NULL); 646 } 647 } 648 gp = g_new_geomf(mp, "%s", md->md_name); 649 sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 650 gp->start = g_concat_start; 651 gp->spoiled = g_concat_orphan; 652 gp->orphan = g_concat_orphan; 653 gp->access = g_concat_access; 654 gp->dumpconf = g_concat_dumpconf; 655 656 sc->sc_id = md->md_id; 657 sc->sc_ndisks = md->md_all; 658 TAILQ_INIT(&sc->sc_disks); 659 for (no = 0; no < sc->sc_ndisks; no++) { 660 disk = malloc(sizeof(*disk), M_CONCAT, M_WAITOK | M_ZERO); 661 TAILQ_INSERT_TAIL(&sc->sc_disks, disk, d_next); 662 } 663 sc->sc_type = type; 664 mtx_init(&sc->sc_completion_lock, "gconcat lock", NULL, MTX_DEF); 665 sx_init(&sc->sc_disks_lock, "gconcat append lock"); 666 667 gp->softc = sc; 668 sc->sc_geom = gp; 669 sc->sc_provider = NULL; 670 671 G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 672 673 return (gp); 674 } 675 676 static int 677 g_concat_destroy(struct g_concat_softc *sc, boolean_t force) 678 { 679 struct g_provider *pp; 680 struct g_consumer *cp, *cp1; 681 struct g_geom *gp; 682 struct g_concat_disk *disk; 683 684 g_topology_assert(); 685 686 if (sc == NULL) 687 return (ENXIO); 688 689 pp = sc->sc_provider; 690 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 691 if (force) { 692 G_CONCAT_DEBUG(0, "Device %s is still open, so it " 693 "can't be definitely removed.", pp->name); 694 } else { 695 G_CONCAT_DEBUG(1, 696 "Device %s is still open (r%dw%de%d).", pp->name, 697 pp->acr, pp->acw, pp->ace); 698 return (EBUSY); 699 } 700 } 701 702 gp = sc->sc_geom; 703 LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 704 g_concat_remove_disk(cp->private); 705 if (cp1 == NULL) 706 return (0); /* Recursion happened. */ 707 } 708 if (!LIST_EMPTY(&gp->consumer)) 709 return (EINPROGRESS); 710 711 gp->softc = NULL; 712 KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 713 gp->name)); 714 while ((disk = TAILQ_FIRST(&sc->sc_disks)) != NULL) { 715 TAILQ_REMOVE(&sc->sc_disks, disk, d_next); 716 free(disk, M_CONCAT); 717 } 718 mtx_destroy(&sc->sc_completion_lock); 719 sx_destroy(&sc->sc_disks_lock); 720 free(sc, M_CONCAT); 721 722 G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 723 g_wither_geom(gp, ENXIO); 724 return (0); 725 } 726 727 static int 728 g_concat_destroy_geom(struct gctl_req *req __unused, 729 struct g_class *mp __unused, struct g_geom *gp) 730 { 731 struct g_concat_softc *sc; 732 733 sc = gp->softc; 734 return (g_concat_destroy(sc, 0)); 735 } 736 737 static struct g_geom * 738 g_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 739 { 740 struct g_concat_metadata md; 741 struct g_concat_softc *sc; 742 struct g_consumer *cp; 743 struct g_geom *gp; 744 int error; 745 746 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 747 g_topology_assert(); 748 749 /* Skip providers that are already open for writing. */ 750 if (pp->acw > 0) 751 return (NULL); 752 753 G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 754 755 gp = g_new_geomf(mp, "concat:taste"); 756 gp->start = g_concat_start; 757 gp->access = g_concat_access; 758 gp->orphan = g_concat_orphan; 759 cp = g_new_consumer(gp); 760 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 761 error = g_attach(cp, pp); 762 if (error == 0) { 763 error = g_concat_read_metadata(cp, &md); 764 g_detach(cp); 765 } 766 g_destroy_consumer(cp); 767 g_destroy_geom(gp); 768 if (error != 0) 769 return (NULL); 770 gp = NULL; 771 772 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 773 return (NULL); 774 if (md.md_version > G_CONCAT_VERSION) { 775 printf("geom_concat.ko module is too old to handle %s.\n", 776 pp->name); 777 return (NULL); 778 } 779 /* 780 * Backward compatibility: 781 */ 782 /* There was no md_provider field in earlier versions of metadata. */ 783 if (md.md_version < 3) 784 bzero(md.md_provider, sizeof(md.md_provider)); 785 /* There was no md_provsize field in earlier versions of metadata. */ 786 if (md.md_version < 4) 787 md.md_provsize = pp->mediasize; 788 789 if (md.md_provider[0] != '\0' && 790 !g_compare_names(md.md_provider, pp->name)) 791 return (NULL); 792 if (md.md_provsize != pp->mediasize) 793 return (NULL); 794 795 /* 796 * Let's check if device already exists. 797 */ 798 sc = NULL; 799 LIST_FOREACH(gp, &mp->geom, geom) { 800 sc = gp->softc; 801 if (sc == NULL) 802 continue; 803 if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 804 continue; 805 if (strcmp(md.md_name, sc->sc_name) != 0) 806 continue; 807 if (md.md_id != sc->sc_id) 808 continue; 809 break; 810 } 811 if (gp != NULL) { 812 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 813 error = g_concat_add_disk(sc, pp, md.md_no); 814 if (error != 0) { 815 G_CONCAT_DEBUG(0, 816 "Cannot add disk %s to %s (error=%d).", pp->name, 817 gp->name, error); 818 return (NULL); 819 } 820 } else { 821 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 822 if (gp == NULL) { 823 G_CONCAT_DEBUG(0, "Cannot create device %s.", 824 md.md_name); 825 return (NULL); 826 } 827 sc = gp->softc; 828 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 829 error = g_concat_add_disk(sc, pp, md.md_no); 830 if (error != 0) { 831 G_CONCAT_DEBUG(0, 832 "Cannot add disk %s to %s (error=%d).", pp->name, 833 gp->name, error); 834 g_concat_destroy(sc, 1); 835 return (NULL); 836 } 837 } 838 839 return (gp); 840 } 841 842 static void 843 g_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 844 { 845 u_int attached, no; 846 struct g_concat_metadata md; 847 struct g_provider *pp; 848 struct g_concat_softc *sc; 849 struct g_geom *gp; 850 struct sbuf *sb; 851 const char *name; 852 char param[16]; 853 int *nargs; 854 855 g_topology_assert(); 856 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 857 if (nargs == NULL) { 858 gctl_error(req, "No '%s' argument.", "nargs"); 859 return; 860 } 861 if (*nargs < 2) { 862 gctl_error(req, "Too few arguments."); 863 return; 864 } 865 866 bzero(&md, sizeof(md)); 867 strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 868 md.md_version = G_CONCAT_VERSION; 869 name = gctl_get_asciiparam(req, "arg0"); 870 if (name == NULL) { 871 gctl_error(req, "No 'arg%u' argument.", 0); 872 return; 873 } 874 strlcpy(md.md_name, name, sizeof(md.md_name)); 875 md.md_id = arc4random(); 876 md.md_no = 0; 877 md.md_all = *nargs - 1; 878 /* This field is not important here. */ 879 md.md_provsize = 0; 880 881 /* Check all providers are valid */ 882 for (no = 1; no < *nargs; no++) { 883 snprintf(param, sizeof(param), "arg%u", no); 884 pp = gctl_get_provider(req, param); 885 if (pp == NULL) 886 return; 887 } 888 889 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 890 if (gp == NULL) { 891 gctl_error(req, "Can't configure %s.", md.md_name); 892 return; 893 } 894 895 sc = gp->softc; 896 sb = sbuf_new_auto(); 897 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 898 for (attached = 0, no = 1; no < *nargs; no++) { 899 snprintf(param, sizeof(param), "arg%u", no); 900 pp = gctl_get_provider(req, param); 901 if (pp == NULL) { 902 name = gctl_get_asciiparam(req, param); 903 MPASS(name != NULL); 904 sbuf_printf(sb, " %s", name); 905 continue; 906 } 907 if (g_concat_add_disk(sc, pp, no - 1) != 0) { 908 G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 909 no, pp->name, gp->name); 910 sbuf_printf(sb, " %s", pp->name); 911 continue; 912 } 913 attached++; 914 } 915 sbuf_finish(sb); 916 if (md.md_all != attached) { 917 g_concat_destroy(gp->softc, 1); 918 gctl_error(req, "%s", sbuf_data(sb)); 919 } 920 sbuf_delete(sb); 921 } 922 923 static struct g_concat_softc * 924 g_concat_find_device(struct g_class *mp, const char *name) 925 { 926 struct g_concat_softc *sc; 927 struct g_geom *gp; 928 929 if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0) 930 name += strlen(_PATH_DEV); 931 932 LIST_FOREACH(gp, &mp->geom, geom) { 933 sc = gp->softc; 934 if (sc == NULL) 935 continue; 936 if (strcmp(sc->sc_name, name) == 0) 937 return (sc); 938 } 939 return (NULL); 940 } 941 942 static void 943 g_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 944 { 945 struct g_concat_softc *sc; 946 int *force, *nargs, error; 947 const char *name; 948 char param[16]; 949 u_int i; 950 951 g_topology_assert(); 952 953 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 954 if (nargs == NULL) { 955 gctl_error(req, "No '%s' argument.", "nargs"); 956 return; 957 } 958 if (*nargs <= 0) { 959 gctl_error(req, "Missing device(s)."); 960 return; 961 } 962 force = gctl_get_paraml(req, "force", sizeof(*force)); 963 if (force == NULL) { 964 gctl_error(req, "No '%s' argument.", "force"); 965 return; 966 } 967 968 for (i = 0; i < (u_int)*nargs; i++) { 969 snprintf(param, sizeof(param), "arg%u", i); 970 name = gctl_get_asciiparam(req, param); 971 if (name == NULL) { 972 gctl_error(req, "No 'arg%u' argument.", i); 973 return; 974 } 975 sc = g_concat_find_device(mp, name); 976 if (sc == NULL) { 977 gctl_error(req, "No such device: %s.", name); 978 return; 979 } 980 error = g_concat_destroy(sc, *force); 981 if (error != 0) { 982 gctl_error(req, "Cannot destroy device %s (error=%d).", 983 sc->sc_name, error); 984 return; 985 } 986 } 987 } 988 989 static struct g_concat_disk * 990 g_concat_find_disk(struct g_concat_softc *sc, const char *name) 991 { 992 struct g_concat_disk *disk; 993 994 sx_assert(&sc->sc_disks_lock, SX_LOCKED); 995 if (strncmp(name, "/dev/", 5) == 0) 996 name += 5; 997 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 998 if (disk->d_consumer == NULL) 999 continue; 1000 if (disk->d_consumer->provider == NULL) 1001 continue; 1002 if (strcmp(disk->d_consumer->provider->name, name) == 0) 1003 return (disk); 1004 } 1005 return (NULL); 1006 } 1007 1008 static void 1009 g_concat_write_metadata(struct gctl_req *req, struct g_concat_softc *sc) 1010 { 1011 u_int no = 0; 1012 struct g_concat_disk *disk; 1013 struct g_concat_metadata md; 1014 struct g_provider *pp; 1015 u_char *sector; 1016 int error; 1017 1018 bzero(&md, sizeof(md)); 1019 strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 1020 md.md_version = G_CONCAT_VERSION; 1021 strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name)); 1022 md.md_id = sc->sc_id; 1023 md.md_all = sc->sc_ndisks; 1024 TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1025 pp = disk->d_consumer->provider; 1026 1027 md.md_no = no; 1028 if (disk->d_hardcoded) 1029 strlcpy(md.md_provider, pp->name, 1030 sizeof(md.md_provider)); 1031 md.md_provsize = disk->d_consumer->provider->mediasize; 1032 1033 sector = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 1034 concat_metadata_encode(&md, sector); 1035 error = g_access(disk->d_consumer, 0, 1, 0); 1036 if (error == 0) { 1037 error = g_write_data(disk->d_consumer, 1038 pp->mediasize - pp->sectorsize, sector, 1039 pp->sectorsize); 1040 (void)g_access(disk->d_consumer, 0, -1, 0); 1041 } 1042 g_free(sector); 1043 if (error != 0) 1044 gctl_error(req, "Cannot store metadata on %s: %d", 1045 pp->name, error); 1046 1047 no++; 1048 } 1049 } 1050 1051 static void 1052 g_concat_ctl_append(struct gctl_req *req, struct g_class *mp) 1053 { 1054 struct g_concat_softc *sc; 1055 struct g_consumer *cp, *fcp; 1056 struct g_provider *pp; 1057 struct g_geom *gp; 1058 const char *name, *cname; 1059 struct g_concat_disk *disk; 1060 int *nargs, *hardcode; 1061 int error; 1062 int disk_candelete; 1063 1064 g_topology_assert(); 1065 1066 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1067 if (nargs == NULL) { 1068 gctl_error(req, "No '%s' argument.", "nargs"); 1069 return; 1070 } 1071 if (*nargs != 2) { 1072 gctl_error(req, "Invalid number of arguments."); 1073 return; 1074 } 1075 hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); 1076 if (hardcode == NULL) { 1077 gctl_error(req, "No '%s' argument.", "hardcode"); 1078 return; 1079 } 1080 1081 cname = gctl_get_asciiparam(req, "arg0"); 1082 if (cname == NULL) { 1083 gctl_error(req, "No 'arg%u' argument.", 0); 1084 return; 1085 } 1086 sc = g_concat_find_device(mp, cname); 1087 if (sc == NULL) { 1088 gctl_error(req, "No such device: %s.", cname); 1089 return; 1090 } 1091 if (sc->sc_provider == NULL) { 1092 /* 1093 * this won't race with g_concat_remove_disk as both 1094 * are holding the topology lock 1095 */ 1096 gctl_error(req, "Device not active, can't append: %s.", cname); 1097 return; 1098 } 1099 G_CONCAT_DEBUG(1, "Appending to %s:", cname); 1100 sx_xlock(&sc->sc_disks_lock); 1101 gp = sc->sc_geom; 1102 fcp = LIST_FIRST(&gp->consumer); 1103 1104 name = gctl_get_asciiparam(req, "arg1"); 1105 if (name == NULL) { 1106 gctl_error(req, "No 'arg%u' argument.", 1); 1107 goto fail; 1108 } 1109 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 1110 name += strlen("/dev/"); 1111 pp = g_provider_by_name(name); 1112 if (pp == NULL) { 1113 G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 1114 gctl_error(req, "Disk %s is invalid.", name); 1115 goto fail; 1116 } 1117 G_CONCAT_DEBUG(1, "Appending %s to this", name); 1118 1119 if (g_concat_find_disk(sc, name) != NULL) { 1120 gctl_error(req, "Disk %s already appended.", name); 1121 goto fail; 1122 } 1123 1124 if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) { 1125 gctl_error(req, "Providers sectorsize mismatch: %u vs %u", 1126 sc->sc_provider->sectorsize, pp->sectorsize); 1127 goto fail; 1128 } 1129 1130 cp = g_new_consumer(gp); 1131 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 1132 error = g_attach(cp, pp); 1133 if (error != 0) { 1134 g_destroy_consumer(cp); 1135 gctl_error(req, "Cannot open device %s (error=%d).", 1136 name, error); 1137 goto fail; 1138 } 1139 1140 error = g_access(cp, 1, 0, 0); 1141 if (error == 0) { 1142 error = g_getattr("GEOM::candelete", cp, &disk_candelete); 1143 if (error != 0) 1144 disk_candelete = 0; 1145 (void)g_access(cp, -1, 0, 0); 1146 } else 1147 G_CONCAT_DEBUG(1, "Failed to access disk %s, error %d.", name, error); 1148 1149 /* invoke g_access exactly as deep as all the other members currently are */ 1150 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 1151 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 1152 if (error != 0) { 1153 g_detach(cp); 1154 g_destroy_consumer(cp); 1155 gctl_error(req, "Failed to access disk %s (error=%d).", name, error); 1156 goto fail; 1157 } 1158 } 1159 1160 disk = malloc(sizeof(*disk), M_CONCAT, M_WAITOK | M_ZERO); 1161 disk->d_consumer = cp; 1162 disk->d_softc = sc; 1163 disk->d_start = TAILQ_LAST(&sc->sc_disks, g_concat_disks)->d_end; 1164 disk->d_end = disk->d_start + cp->provider->mediasize; 1165 disk->d_candelete = disk_candelete; 1166 disk->d_removed = 0; 1167 disk->d_hardcoded = *hardcode; 1168 cp->private = disk; 1169 TAILQ_INSERT_TAIL(&sc->sc_disks, disk, d_next); 1170 sc->sc_ndisks++; 1171 1172 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 1173 /* last sector is for metadata */ 1174 disk->d_end -= cp->provider->sectorsize; 1175 1176 /* update metadata on all parts */ 1177 g_concat_write_metadata(req, sc); 1178 } 1179 1180 g_resize_provider(sc->sc_provider, disk->d_end); 1181 1182 fail: 1183 sx_xunlock(&sc->sc_disks_lock); 1184 } 1185 1186 static void 1187 g_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 1188 { 1189 uint32_t *version; 1190 1191 g_topology_assert(); 1192 1193 version = gctl_get_paraml(req, "version", sizeof(*version)); 1194 if (version == NULL) { 1195 gctl_error(req, "No '%s' argument.", "version"); 1196 return; 1197 } 1198 if (*version != G_CONCAT_VERSION) { 1199 gctl_error(req, "Userland and kernel parts are out of sync."); 1200 return; 1201 } 1202 1203 if (strcmp(verb, "create") == 0) { 1204 g_concat_ctl_create(req, mp); 1205 return; 1206 } else if (strcmp(verb, "destroy") == 0 || 1207 strcmp(verb, "stop") == 0) { 1208 g_concat_ctl_destroy(req, mp); 1209 return; 1210 } else if (strcmp(verb, "append") == 0) { 1211 g_concat_ctl_append(req, mp); 1212 return; 1213 } 1214 gctl_error(req, "Unknown verb."); 1215 } 1216 1217 static void 1218 g_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 1219 struct g_consumer *cp, struct g_provider *pp) 1220 { 1221 struct g_concat_softc *sc; 1222 1223 g_topology_assert(); 1224 sc = gp->softc; 1225 if (sc == NULL) 1226 return; 1227 1228 sx_slock(&sc->sc_disks_lock); 1229 if (pp != NULL) { 1230 /* Nothing here. */ 1231 } else if (cp != NULL) { 1232 struct g_concat_disk *disk; 1233 1234 disk = cp->private; 1235 if (disk == NULL) 1236 goto end; 1237 sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 1238 (intmax_t)disk->d_end); 1239 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 1240 (intmax_t)disk->d_start); 1241 } else { 1242 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 1243 sbuf_printf(sb, "%s<Type>", indent); 1244 switch (sc->sc_type) { 1245 case G_CONCAT_TYPE_AUTOMATIC: 1246 sbuf_cat(sb, "AUTOMATIC"); 1247 break; 1248 case G_CONCAT_TYPE_MANUAL: 1249 sbuf_cat(sb, "MANUAL"); 1250 break; 1251 default: 1252 sbuf_cat(sb, "UNKNOWN"); 1253 break; 1254 } 1255 sbuf_cat(sb, "</Type>\n"); 1256 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 1257 indent, sc->sc_ndisks, g_concat_nvalid(sc)); 1258 sbuf_printf(sb, "%s<State>", indent); 1259 if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 1260 sbuf_cat(sb, "UP"); 1261 else 1262 sbuf_cat(sb, "DOWN"); 1263 sbuf_cat(sb, "</State>\n"); 1264 } 1265 end: 1266 sx_sunlock(&sc->sc_disks_lock); 1267 } 1268 1269 DECLARE_GEOM_CLASS(g_concat_class, g_concat); 1270 MODULE_VERSION(geom_concat, 0); 1271