1 /*- 2 * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 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 THE AUTHORS 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 THE AUTHORS 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 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.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/concat/g_concat.h> 42 43 FEATURE(geom_concat, "GEOM concatenation support"); 44 45 static MALLOC_DEFINE(M_CONCAT, "concat_data", "GEOM_CONCAT Data"); 46 47 SYSCTL_DECL(_kern_geom); 48 static SYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW, 0, 49 "GEOM_CONCAT stuff"); 50 static u_int g_concat_debug = 0; 51 SYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RWTUN, &g_concat_debug, 0, 52 "Debug level"); 53 54 static int g_concat_destroy(struct g_concat_softc *sc, boolean_t force); 55 static int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp, 56 struct g_geom *gp); 57 58 static g_taste_t g_concat_taste; 59 static g_ctl_req_t g_concat_config; 60 static g_dumpconf_t g_concat_dumpconf; 61 62 struct g_class g_concat_class = { 63 .name = G_CONCAT_CLASS_NAME, 64 .version = G_VERSION, 65 .ctlreq = g_concat_config, 66 .taste = g_concat_taste, 67 .destroy_geom = g_concat_destroy_geom 68 }; 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 i, no; 104 105 no = 0; 106 for (i = 0; i < sc->sc_ndisks; i++) { 107 if (sc->sc_disks[i].d_consumer != NULL) 108 no++; 109 } 110 111 return (no); 112 } 113 114 static void 115 g_concat_remove_disk(struct g_concat_disk *disk) 116 { 117 struct g_consumer *cp; 118 struct g_concat_softc *sc; 119 120 g_topology_assert(); 121 KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); 122 sc = disk->d_softc; 123 cp = disk->d_consumer; 124 125 if (!disk->d_removed) { 126 G_CONCAT_DEBUG(0, "Disk %s removed from %s.", 127 cp->provider->name, sc->sc_name); 128 disk->d_removed = 1; 129 } 130 131 if (sc->sc_provider != NULL) { 132 G_CONCAT_DEBUG(0, "Device %s deactivated.", 133 sc->sc_provider->name); 134 g_wither_provider(sc->sc_provider, ENXIO); 135 sc->sc_provider = NULL; 136 } 137 138 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 139 return; 140 disk->d_consumer = NULL; 141 g_detach(cp); 142 g_destroy_consumer(cp); 143 /* If there are no valid disks anymore, remove device. */ 144 if (LIST_EMPTY(&sc->sc_geom->consumer)) 145 g_concat_destroy(sc, 1); 146 } 147 148 static void 149 g_concat_orphan(struct g_consumer *cp) 150 { 151 struct g_concat_softc *sc; 152 struct g_concat_disk *disk; 153 struct g_geom *gp; 154 155 g_topology_assert(); 156 gp = cp->geom; 157 sc = gp->softc; 158 if (sc == NULL) 159 return; 160 161 disk = cp->private; 162 if (disk == NULL) /* Possible? */ 163 return; 164 g_concat_remove_disk(disk); 165 } 166 167 static int 168 g_concat_access(struct g_provider *pp, int dr, int dw, int de) 169 { 170 struct g_consumer *cp1, *cp2, *tmp; 171 struct g_concat_disk *disk; 172 struct g_geom *gp; 173 int error; 174 175 g_topology_assert(); 176 gp = pp->geom; 177 178 /* On first open, grab an extra "exclusive" bit */ 179 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 180 de++; 181 /* ... and let go of it on last close */ 182 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 183 de--; 184 185 LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { 186 error = g_access(cp1, dr, dw, de); 187 if (error != 0) 188 goto fail; 189 disk = cp1->private; 190 if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && 191 disk->d_removed) { 192 g_concat_remove_disk(disk); /* May destroy geom. */ 193 } 194 } 195 return (0); 196 197 fail: 198 LIST_FOREACH(cp2, &gp->consumer, consumer) { 199 if (cp1 == cp2) 200 break; 201 g_access(cp2, -dr, -dw, -de); 202 } 203 return (error); 204 } 205 206 static void 207 g_concat_kernel_dump(struct bio *bp) 208 { 209 struct g_concat_softc *sc; 210 struct g_concat_disk *disk; 211 struct bio *cbp; 212 struct g_kerneldump *gkd; 213 u_int i; 214 215 sc = bp->bio_to->geom->softc; 216 gkd = (struct g_kerneldump *)bp->bio_data; 217 for (i = 0; i < sc->sc_ndisks; i++) { 218 if (sc->sc_disks[i].d_start <= gkd->offset && 219 sc->sc_disks[i].d_end > gkd->offset) 220 break; 221 } 222 if (i == sc->sc_ndisks) 223 g_io_deliver(bp, EOPNOTSUPP); 224 disk = &sc->sc_disks[i]; 225 gkd->offset -= disk->d_start; 226 if (gkd->length > disk->d_end - disk->d_start - gkd->offset) 227 gkd->length = disk->d_end - disk->d_start - gkd->offset; 228 cbp = g_clone_bio(bp); 229 if (cbp == NULL) { 230 g_io_deliver(bp, ENOMEM); 231 return; 232 } 233 cbp->bio_done = g_std_done; 234 g_io_request(cbp, disk->d_consumer); 235 G_CONCAT_DEBUG(1, "Kernel dump will go to %s.", 236 disk->d_consumer->provider->name); 237 } 238 239 static void 240 g_concat_done(struct bio *bp) 241 { 242 struct g_concat_softc *sc; 243 struct bio *pbp; 244 245 pbp = bp->bio_parent; 246 sc = pbp->bio_to->geom->softc; 247 mtx_lock(&sc->sc_lock); 248 if (pbp->bio_error == 0) 249 pbp->bio_error = bp->bio_error; 250 pbp->bio_completed += bp->bio_completed; 251 pbp->bio_inbed++; 252 if (pbp->bio_children == pbp->bio_inbed) { 253 mtx_unlock(&sc->sc_lock); 254 g_io_deliver(pbp, pbp->bio_error); 255 } else 256 mtx_unlock(&sc->sc_lock); 257 g_destroy_bio(bp); 258 } 259 260 static void 261 g_concat_flush(struct g_concat_softc *sc, struct bio *bp) 262 { 263 struct bio_queue_head queue; 264 struct g_consumer *cp; 265 struct bio *cbp; 266 u_int no; 267 268 bioq_init(&queue); 269 for (no = 0; no < sc->sc_ndisks; no++) { 270 cbp = g_clone_bio(bp); 271 if (cbp == NULL) { 272 while ((cbp = bioq_takefirst(&queue)) != NULL) 273 g_destroy_bio(cbp); 274 if (bp->bio_error == 0) 275 bp->bio_error = ENOMEM; 276 g_io_deliver(bp, bp->bio_error); 277 return; 278 } 279 bioq_insert_tail(&queue, cbp); 280 cbp->bio_done = g_concat_done; 281 cbp->bio_caller1 = sc->sc_disks[no].d_consumer; 282 cbp->bio_to = sc->sc_disks[no].d_consumer->provider; 283 } 284 while ((cbp = bioq_takefirst(&queue)) != NULL) { 285 G_CONCAT_LOGREQ(cbp, "Sending request."); 286 cp = cbp->bio_caller1; 287 cbp->bio_caller1 = NULL; 288 g_io_request(cbp, cp); 289 } 290 } 291 292 static void 293 g_concat_start(struct bio *bp) 294 { 295 struct bio_queue_head queue; 296 struct g_concat_softc *sc; 297 struct g_concat_disk *disk; 298 struct g_provider *pp; 299 off_t offset, end, length, off, len; 300 struct bio *cbp; 301 char *addr; 302 u_int no; 303 304 pp = bp->bio_to; 305 sc = pp->geom->softc; 306 /* 307 * If sc == NULL, provider's error should be set and g_concat_start() 308 * should not be called at all. 309 */ 310 KASSERT(sc != NULL, 311 ("Provider's error should be set (error=%d)(device=%s).", 312 bp->bio_to->error, bp->bio_to->name)); 313 314 G_CONCAT_LOGREQ(bp, "Request received."); 315 316 switch (bp->bio_cmd) { 317 case BIO_READ: 318 case BIO_WRITE: 319 case BIO_DELETE: 320 break; 321 case BIO_FLUSH: 322 g_concat_flush(sc, bp); 323 return; 324 case BIO_GETATTR: 325 if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 326 g_concat_kernel_dump(bp); 327 return; 328 } 329 /* To which provider it should be delivered? */ 330 /* FALLTHROUGH */ 331 default: 332 g_io_deliver(bp, EOPNOTSUPP); 333 return; 334 } 335 336 offset = bp->bio_offset; 337 length = bp->bio_length; 338 if ((bp->bio_flags & BIO_UNMAPPED) != 0) 339 addr = NULL; 340 else 341 addr = bp->bio_data; 342 end = offset + length; 343 344 bioq_init(&queue); 345 for (no = 0; no < sc->sc_ndisks; no++) { 346 disk = &sc->sc_disks[no]; 347 if (disk->d_end <= offset) 348 continue; 349 if (disk->d_start >= end) 350 break; 351 352 off = offset - disk->d_start; 353 len = MIN(length, disk->d_end - offset); 354 length -= len; 355 offset += len; 356 357 cbp = g_clone_bio(bp); 358 if (cbp == NULL) { 359 while ((cbp = bioq_takefirst(&queue)) != NULL) 360 g_destroy_bio(cbp); 361 if (bp->bio_error == 0) 362 bp->bio_error = ENOMEM; 363 g_io_deliver(bp, bp->bio_error); 364 return; 365 } 366 bioq_insert_tail(&queue, cbp); 367 /* 368 * Fill in the component buf structure. 369 */ 370 if (len == bp->bio_length) 371 cbp->bio_done = g_std_done; 372 else 373 cbp->bio_done = g_concat_done; 374 cbp->bio_offset = off; 375 cbp->bio_length = len; 376 if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 377 cbp->bio_ma_offset += (uintptr_t)addr; 378 cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; 379 cbp->bio_ma_offset %= PAGE_SIZE; 380 cbp->bio_ma_n = round_page(cbp->bio_ma_offset + 381 cbp->bio_length) / PAGE_SIZE; 382 } else 383 cbp->bio_data = addr; 384 addr += len; 385 cbp->bio_to = disk->d_consumer->provider; 386 cbp->bio_caller1 = disk; 387 388 if (length == 0) 389 break; 390 } 391 KASSERT(length == 0, 392 ("Length is still greater than 0 (class=%s, name=%s).", 393 bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 394 while ((cbp = bioq_takefirst(&queue)) != NULL) { 395 G_CONCAT_LOGREQ(cbp, "Sending request."); 396 disk = cbp->bio_caller1; 397 cbp->bio_caller1 = NULL; 398 g_io_request(cbp, disk->d_consumer); 399 } 400 } 401 402 static void 403 g_concat_check_and_run(struct g_concat_softc *sc) 404 { 405 struct g_concat_disk *disk; 406 struct g_provider *dp, *pp; 407 u_int no, sectorsize = 0; 408 off_t start; 409 410 g_topology_assert(); 411 if (g_concat_nvalid(sc) != sc->sc_ndisks) 412 return; 413 414 pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name); 415 pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | 416 G_PF_ACCEPT_UNMAPPED; 417 start = 0; 418 for (no = 0; no < sc->sc_ndisks; no++) { 419 disk = &sc->sc_disks[no]; 420 dp = disk->d_consumer->provider; 421 disk->d_start = start; 422 disk->d_end = disk->d_start + dp->mediasize; 423 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 424 disk->d_end -= dp->sectorsize; 425 start = disk->d_end; 426 if (no == 0) 427 sectorsize = dp->sectorsize; 428 else 429 sectorsize = lcm(sectorsize, dp->sectorsize); 430 431 /* A provider underneath us doesn't support unmapped */ 432 if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 433 G_CONCAT_DEBUG(1, "Cancelling unmapped " 434 "because of %s.", dp->name); 435 pp->flags &= ~G_PF_ACCEPT_UNMAPPED; 436 } 437 } 438 pp->sectorsize = sectorsize; 439 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 440 pp->mediasize = start; 441 pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; 442 pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; 443 sc->sc_provider = pp; 444 g_error_provider(pp, 0); 445 446 G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 447 } 448 449 static int 450 g_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 451 { 452 struct g_provider *pp; 453 u_char *buf; 454 int error; 455 456 g_topology_assert(); 457 458 error = g_access(cp, 1, 0, 0); 459 if (error != 0) 460 return (error); 461 pp = cp->provider; 462 g_topology_unlock(); 463 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 464 &error); 465 g_topology_lock(); 466 g_access(cp, -1, 0, 0); 467 if (buf == NULL) 468 return (error); 469 470 /* Decode metadata. */ 471 concat_metadata_decode(buf, md); 472 g_free(buf); 473 474 return (0); 475 } 476 477 /* 478 * Add disk to given device. 479 */ 480 static int 481 g_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 482 { 483 struct g_concat_disk *disk; 484 struct g_consumer *cp, *fcp; 485 struct g_geom *gp; 486 int error; 487 488 g_topology_assert(); 489 /* Metadata corrupted? */ 490 if (no >= sc->sc_ndisks) 491 return (EINVAL); 492 493 disk = &sc->sc_disks[no]; 494 /* Check if disk is not already attached. */ 495 if (disk->d_consumer != NULL) 496 return (EEXIST); 497 498 gp = sc->sc_geom; 499 fcp = LIST_FIRST(&gp->consumer); 500 501 cp = g_new_consumer(gp); 502 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 503 error = g_attach(cp, pp); 504 if (error != 0) { 505 g_destroy_consumer(cp); 506 return (error); 507 } 508 509 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 510 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 511 if (error != 0) { 512 g_detach(cp); 513 g_destroy_consumer(cp); 514 return (error); 515 } 516 } 517 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 518 struct g_concat_metadata md; 519 520 /* Re-read metadata. */ 521 error = g_concat_read_metadata(cp, &md); 522 if (error != 0) 523 goto fail; 524 525 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 526 strcmp(md.md_name, sc->sc_name) != 0 || 527 md.md_id != sc->sc_id) { 528 G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 529 goto fail; 530 } 531 } 532 533 cp->private = disk; 534 disk->d_consumer = cp; 535 disk->d_softc = sc; 536 disk->d_start = 0; /* not yet */ 537 disk->d_end = 0; /* not yet */ 538 disk->d_removed = 0; 539 540 G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 541 542 g_concat_check_and_run(sc); 543 544 return (0); 545 fail: 546 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 547 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 548 g_detach(cp); 549 g_destroy_consumer(cp); 550 return (error); 551 } 552 553 static struct g_geom * 554 g_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 555 u_int type) 556 { 557 struct g_concat_softc *sc; 558 struct g_geom *gp; 559 u_int no; 560 561 G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 562 md->md_id); 563 564 /* One disks is minimum. */ 565 if (md->md_all < 1) 566 return (NULL); 567 568 /* Check for duplicate unit */ 569 LIST_FOREACH(gp, &mp->geom, geom) { 570 sc = gp->softc; 571 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 572 G_CONCAT_DEBUG(0, "Device %s already configured.", 573 gp->name); 574 return (NULL); 575 } 576 } 577 gp = g_new_geomf(mp, "%s", md->md_name); 578 sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 579 gp->start = g_concat_start; 580 gp->spoiled = g_concat_orphan; 581 gp->orphan = g_concat_orphan; 582 gp->access = g_concat_access; 583 gp->dumpconf = g_concat_dumpconf; 584 585 sc->sc_id = md->md_id; 586 sc->sc_ndisks = md->md_all; 587 sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 588 M_CONCAT, M_WAITOK | M_ZERO); 589 for (no = 0; no < sc->sc_ndisks; no++) 590 sc->sc_disks[no].d_consumer = NULL; 591 sc->sc_type = type; 592 mtx_init(&sc->sc_lock, "gconcat lock", NULL, MTX_DEF); 593 594 gp->softc = sc; 595 sc->sc_geom = gp; 596 sc->sc_provider = NULL; 597 598 G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 599 600 return (gp); 601 } 602 603 static int 604 g_concat_destroy(struct g_concat_softc *sc, boolean_t force) 605 { 606 struct g_provider *pp; 607 struct g_consumer *cp, *cp1; 608 struct g_geom *gp; 609 610 g_topology_assert(); 611 612 if (sc == NULL) 613 return (ENXIO); 614 615 pp = sc->sc_provider; 616 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 617 if (force) { 618 G_CONCAT_DEBUG(0, "Device %s is still open, so it " 619 "can't be definitely removed.", pp->name); 620 } else { 621 G_CONCAT_DEBUG(1, 622 "Device %s is still open (r%dw%de%d).", pp->name, 623 pp->acr, pp->acw, pp->ace); 624 return (EBUSY); 625 } 626 } 627 628 gp = sc->sc_geom; 629 LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 630 g_concat_remove_disk(cp->private); 631 if (cp1 == NULL) 632 return (0); /* Recursion happened. */ 633 } 634 if (!LIST_EMPTY(&gp->consumer)) 635 return (EINPROGRESS); 636 637 gp->softc = NULL; 638 KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 639 gp->name)); 640 free(sc->sc_disks, M_CONCAT); 641 mtx_destroy(&sc->sc_lock); 642 free(sc, M_CONCAT); 643 644 G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 645 g_wither_geom(gp, ENXIO); 646 return (0); 647 } 648 649 static int 650 g_concat_destroy_geom(struct gctl_req *req __unused, 651 struct g_class *mp __unused, struct g_geom *gp) 652 { 653 struct g_concat_softc *sc; 654 655 sc = gp->softc; 656 return (g_concat_destroy(sc, 0)); 657 } 658 659 static struct g_geom * 660 g_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 661 { 662 struct g_concat_metadata md; 663 struct g_concat_softc *sc; 664 struct g_consumer *cp; 665 struct g_geom *gp; 666 int error; 667 668 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 669 g_topology_assert(); 670 671 /* Skip providers that are already open for writing. */ 672 if (pp->acw > 0) 673 return (NULL); 674 675 G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 676 677 gp = g_new_geomf(mp, "concat:taste"); 678 gp->start = g_concat_start; 679 gp->access = g_concat_access; 680 gp->orphan = g_concat_orphan; 681 cp = g_new_consumer(gp); 682 g_attach(cp, pp); 683 error = g_concat_read_metadata(cp, &md); 684 g_detach(cp); 685 g_destroy_consumer(cp); 686 g_destroy_geom(gp); 687 if (error != 0) 688 return (NULL); 689 gp = NULL; 690 691 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 692 return (NULL); 693 if (md.md_version > G_CONCAT_VERSION) { 694 printf("geom_concat.ko module is too old to handle %s.\n", 695 pp->name); 696 return (NULL); 697 } 698 /* 699 * Backward compatibility: 700 */ 701 /* There was no md_provider field in earlier versions of metadata. */ 702 if (md.md_version < 3) 703 bzero(md.md_provider, sizeof(md.md_provider)); 704 /* There was no md_provsize field in earlier versions of metadata. */ 705 if (md.md_version < 4) 706 md.md_provsize = pp->mediasize; 707 708 if (md.md_provider[0] != '\0' && 709 !g_compare_names(md.md_provider, pp->name)) 710 return (NULL); 711 if (md.md_provsize != pp->mediasize) 712 return (NULL); 713 714 /* 715 * Let's check if device already exists. 716 */ 717 sc = NULL; 718 LIST_FOREACH(gp, &mp->geom, geom) { 719 sc = gp->softc; 720 if (sc == NULL) 721 continue; 722 if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 723 continue; 724 if (strcmp(md.md_name, sc->sc_name) != 0) 725 continue; 726 if (md.md_id != sc->sc_id) 727 continue; 728 break; 729 } 730 if (gp != NULL) { 731 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 732 error = g_concat_add_disk(sc, pp, md.md_no); 733 if (error != 0) { 734 G_CONCAT_DEBUG(0, 735 "Cannot add disk %s to %s (error=%d).", pp->name, 736 gp->name, error); 737 return (NULL); 738 } 739 } else { 740 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 741 if (gp == NULL) { 742 G_CONCAT_DEBUG(0, "Cannot create device %s.", 743 md.md_name); 744 return (NULL); 745 } 746 sc = gp->softc; 747 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 748 error = g_concat_add_disk(sc, pp, md.md_no); 749 if (error != 0) { 750 G_CONCAT_DEBUG(0, 751 "Cannot add disk %s to %s (error=%d).", pp->name, 752 gp->name, error); 753 g_concat_destroy(sc, 1); 754 return (NULL); 755 } 756 } 757 758 return (gp); 759 } 760 761 static void 762 g_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 763 { 764 u_int attached, no; 765 struct g_concat_metadata md; 766 struct g_provider *pp; 767 struct g_concat_softc *sc; 768 struct g_geom *gp; 769 struct sbuf *sb; 770 const char *name; 771 char param[16]; 772 int *nargs; 773 774 g_topology_assert(); 775 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 776 if (nargs == NULL) { 777 gctl_error(req, "No '%s' argument.", "nargs"); 778 return; 779 } 780 if (*nargs < 2) { 781 gctl_error(req, "Too few arguments."); 782 return; 783 } 784 785 strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 786 md.md_version = G_CONCAT_VERSION; 787 name = gctl_get_asciiparam(req, "arg0"); 788 if (name == NULL) { 789 gctl_error(req, "No 'arg%u' argument.", 0); 790 return; 791 } 792 strlcpy(md.md_name, name, sizeof(md.md_name)); 793 md.md_id = arc4random(); 794 md.md_no = 0; 795 md.md_all = *nargs - 1; 796 bzero(md.md_provider, sizeof(md.md_provider)); 797 /* This field is not important here. */ 798 md.md_provsize = 0; 799 800 /* Check all providers are valid */ 801 for (no = 1; no < *nargs; no++) { 802 snprintf(param, sizeof(param), "arg%u", no); 803 name = gctl_get_asciiparam(req, param); 804 if (name == NULL) { 805 gctl_error(req, "No 'arg%u' argument.", no); 806 return; 807 } 808 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 809 name += strlen("/dev/"); 810 pp = g_provider_by_name(name); 811 if (pp == NULL) { 812 G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 813 gctl_error(req, "Disk %s is invalid.", name); 814 return; 815 } 816 } 817 818 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 819 if (gp == NULL) { 820 gctl_error(req, "Can't configure %s.", md.md_name); 821 return; 822 } 823 824 sc = gp->softc; 825 sb = sbuf_new_auto(); 826 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 827 for (attached = 0, no = 1; no < *nargs; no++) { 828 snprintf(param, sizeof(param), "arg%u", no); 829 name = gctl_get_asciiparam(req, param); 830 if (name == NULL) { 831 gctl_error(req, "No 'arg%d' argument.", no); 832 return; 833 } 834 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 835 name += strlen("/dev/"); 836 pp = g_provider_by_name(name); 837 KASSERT(pp != NULL, ("Provider %s disappear?!", name)); 838 if (g_concat_add_disk(sc, pp, no - 1) != 0) { 839 G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 840 no, pp->name, gp->name); 841 sbuf_printf(sb, " %s", pp->name); 842 continue; 843 } 844 attached++; 845 } 846 sbuf_finish(sb); 847 if (md.md_all != attached) { 848 g_concat_destroy(gp->softc, 1); 849 gctl_error(req, "%s", sbuf_data(sb)); 850 } 851 sbuf_delete(sb); 852 } 853 854 static struct g_concat_softc * 855 g_concat_find_device(struct g_class *mp, const char *name) 856 { 857 struct g_concat_softc *sc; 858 struct g_geom *gp; 859 860 LIST_FOREACH(gp, &mp->geom, geom) { 861 sc = gp->softc; 862 if (sc == NULL) 863 continue; 864 if (strcmp(sc->sc_name, name) == 0) 865 return (sc); 866 } 867 return (NULL); 868 } 869 870 static void 871 g_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 872 { 873 struct g_concat_softc *sc; 874 int *force, *nargs, error; 875 const char *name; 876 char param[16]; 877 u_int i; 878 879 g_topology_assert(); 880 881 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 882 if (nargs == NULL) { 883 gctl_error(req, "No '%s' argument.", "nargs"); 884 return; 885 } 886 if (*nargs <= 0) { 887 gctl_error(req, "Missing device(s)."); 888 return; 889 } 890 force = gctl_get_paraml(req, "force", sizeof(*force)); 891 if (force == NULL) { 892 gctl_error(req, "No '%s' argument.", "force"); 893 return; 894 } 895 896 for (i = 0; i < (u_int)*nargs; i++) { 897 snprintf(param, sizeof(param), "arg%u", i); 898 name = gctl_get_asciiparam(req, param); 899 if (name == NULL) { 900 gctl_error(req, "No 'arg%u' argument.", i); 901 return; 902 } 903 sc = g_concat_find_device(mp, name); 904 if (sc == NULL) { 905 gctl_error(req, "No such device: %s.", name); 906 return; 907 } 908 error = g_concat_destroy(sc, *force); 909 if (error != 0) { 910 gctl_error(req, "Cannot destroy device %s (error=%d).", 911 sc->sc_name, error); 912 return; 913 } 914 } 915 } 916 917 static void 918 g_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 919 { 920 uint32_t *version; 921 922 g_topology_assert(); 923 924 version = gctl_get_paraml(req, "version", sizeof(*version)); 925 if (version == NULL) { 926 gctl_error(req, "No '%s' argument.", "version"); 927 return; 928 } 929 if (*version != G_CONCAT_VERSION) { 930 gctl_error(req, "Userland and kernel parts are out of sync."); 931 return; 932 } 933 934 if (strcmp(verb, "create") == 0) { 935 g_concat_ctl_create(req, mp); 936 return; 937 } else if (strcmp(verb, "destroy") == 0 || 938 strcmp(verb, "stop") == 0) { 939 g_concat_ctl_destroy(req, mp); 940 return; 941 } 942 gctl_error(req, "Unknown verb."); 943 } 944 945 static void 946 g_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 947 struct g_consumer *cp, struct g_provider *pp) 948 { 949 struct g_concat_softc *sc; 950 951 g_topology_assert(); 952 sc = gp->softc; 953 if (sc == NULL) 954 return; 955 if (pp != NULL) { 956 /* Nothing here. */ 957 } else if (cp != NULL) { 958 struct g_concat_disk *disk; 959 960 disk = cp->private; 961 if (disk == NULL) 962 return; 963 sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 964 (intmax_t)disk->d_end); 965 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 966 (intmax_t)disk->d_start); 967 } else { 968 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 969 sbuf_printf(sb, "%s<Type>", indent); 970 switch (sc->sc_type) { 971 case G_CONCAT_TYPE_AUTOMATIC: 972 sbuf_printf(sb, "AUTOMATIC"); 973 break; 974 case G_CONCAT_TYPE_MANUAL: 975 sbuf_printf(sb, "MANUAL"); 976 break; 977 default: 978 sbuf_printf(sb, "UNKNOWN"); 979 break; 980 } 981 sbuf_printf(sb, "</Type>\n"); 982 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 983 indent, sc->sc_ndisks, g_concat_nvalid(sc)); 984 sbuf_printf(sb, "%s<State>", indent); 985 if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 986 sbuf_printf(sb, "UP"); 987 else 988 sbuf_printf(sb, "DOWN"); 989 sbuf_printf(sb, "</State>\n"); 990 } 991 } 992 993 DECLARE_GEOM_CLASS(g_concat_class, g_concat); 994