1126007Spjd/*- 2142727Spjd * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3126007Spjd * All rights reserved. 4126007Spjd * 5126007Spjd * Redistribution and use in source and binary forms, with or without 6126007Spjd * modification, are permitted provided that the following conditions 7126007Spjd * are met: 8126007Spjd * 1. Redistributions of source code must retain the above copyright 9126007Spjd * notice, this list of conditions and the following disclaimer. 10126007Spjd * 2. Redistributions in binary form must reproduce the above copyright 11126007Spjd * notice, this list of conditions and the following disclaimer in the 12126007Spjd * documentation and/or other materials provided with the distribution. 13155174Spjd * 14126007Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15126007Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16126007Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17126007Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18126007Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19126007Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20126007Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21126007Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22126007Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23126007Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24126007Spjd * SUCH DAMAGE. 25126007Spjd */ 26126007Spjd 27126007Spjd#include <sys/cdefs.h> 28126007Spjd__FBSDID("$FreeBSD$"); 29126007Spjd 30126007Spjd#include <sys/param.h> 31126007Spjd#include <sys/systm.h> 32126007Spjd#include <sys/kernel.h> 33126007Spjd#include <sys/module.h> 34126007Spjd#include <sys/lock.h> 35126007Spjd#include <sys/mutex.h> 36126007Spjd#include <sys/bio.h> 37223921Sae#include <sys/sbuf.h> 38126007Spjd#include <sys/sysctl.h> 39126007Spjd#include <sys/malloc.h> 40126007Spjd#include <geom/geom.h> 41126007Spjd#include <geom/concat/g_concat.h> 42126007Spjd 43219029SnetchildFEATURE(geom_concat, "GEOM concatenation support"); 44126007Spjd 45151897Srwatsonstatic MALLOC_DEFINE(M_CONCAT, "concat_data", "GEOM_CONCAT Data"); 46126007Spjd 47126565SpjdSYSCTL_DECL(_kern_geom); 48227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW, 0, 49227309Sed "GEOM_CONCAT stuff"); 50126007Spjdstatic u_int g_concat_debug = 0; 51134528SpjdTUNABLE_INT("kern.geom.concat.debug", &g_concat_debug); 52126007SpjdSYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RW, &g_concat_debug, 0, 53126007Spjd "Debug level"); 54126007Spjd 55126007Spjdstatic int g_concat_destroy(struct g_concat_softc *sc, boolean_t force); 56126007Spjdstatic int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp, 57126007Spjd struct g_geom *gp); 58126007Spjd 59126007Spjdstatic g_taste_t g_concat_taste; 60126007Spjdstatic g_ctl_req_t g_concat_config; 61126007Spjdstatic g_dumpconf_t g_concat_dumpconf; 62126007Spjd 63126007Spjdstruct g_class g_concat_class = { 64126007Spjd .name = G_CONCAT_CLASS_NAME, 65133318Sphk .version = G_VERSION, 66126007Spjd .ctlreq = g_concat_config, 67126007Spjd .taste = g_concat_taste, 68126007Spjd .destroy_geom = g_concat_destroy_geom 69126007Spjd}; 70126007Spjd 71126007Spjd 72126007Spjd/* 73126773Spjd * Greatest Common Divisor. 74126773Spjd */ 75126773Spjdstatic u_int 76126773Spjdgcd(u_int a, u_int b) 77126773Spjd{ 78126773Spjd u_int c; 79126773Spjd 80126773Spjd while (b != 0) { 81126773Spjd c = a; 82126773Spjd a = b; 83126773Spjd b = (c % b); 84126773Spjd } 85126773Spjd return (a); 86126773Spjd} 87126773Spjd 88126773Spjd/* 89126773Spjd * Least Common Multiple. 90126773Spjd */ 91126773Spjdstatic u_int 92126773Spjdlcm(u_int a, u_int b) 93126773Spjd{ 94126773Spjd 95126773Spjd return ((a * b) / gcd(a, b)); 96126773Spjd} 97126773Spjd 98126773Spjd/* 99126007Spjd * Return the number of valid disks. 100126007Spjd */ 101126007Spjdstatic u_int 102126007Spjdg_concat_nvalid(struct g_concat_softc *sc) 103126007Spjd{ 104126007Spjd u_int i, no; 105126007Spjd 106126007Spjd no = 0; 107126007Spjd for (i = 0; i < sc->sc_ndisks; i++) { 108126565Spjd if (sc->sc_disks[i].d_consumer != NULL) 109126007Spjd no++; 110126007Spjd } 111126007Spjd 112126007Spjd return (no); 113126007Spjd} 114126007Spjd 115126007Spjdstatic void 116126007Spjdg_concat_remove_disk(struct g_concat_disk *disk) 117126007Spjd{ 118126007Spjd struct g_consumer *cp; 119126007Spjd struct g_concat_softc *sc; 120126007Spjd 121227004Smav g_topology_assert(); 122126565Spjd KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); 123126007Spjd sc = disk->d_softc; 124126007Spjd cp = disk->d_consumer; 125126007Spjd 126227004Smav if (!disk->d_removed) { 127227004Smav G_CONCAT_DEBUG(0, "Disk %s removed from %s.", 128227004Smav cp->provider->name, sc->sc_name); 129227004Smav disk->d_removed = 1; 130227004Smav } 131126007Spjd 132129478Spjd if (sc->sc_provider != NULL) { 133148092Spjd sc->sc_provider->flags |= G_PF_WITHER; 134227004Smav G_CONCAT_DEBUG(0, "Device %s deactivated.", 135227004Smav sc->sc_provider->name); 136129478Spjd g_orphan_provider(sc->sc_provider, ENXIO); 137129478Spjd sc->sc_provider = NULL; 138129478Spjd } 139126007Spjd 140126007Spjd if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 141227004Smav return; 142227004Smav disk->d_consumer = NULL; 143126007Spjd g_detach(cp); 144126007Spjd g_destroy_consumer(cp); 145227004Smav /* If there are no valid disks anymore, remove device. */ 146227004Smav if (LIST_EMPTY(&sc->sc_geom->consumer)) 147227004Smav g_concat_destroy(sc, 1); 148126007Spjd} 149126007Spjd 150126007Spjdstatic void 151126007Spjdg_concat_orphan(struct g_consumer *cp) 152126007Spjd{ 153126007Spjd struct g_concat_softc *sc; 154126007Spjd struct g_concat_disk *disk; 155126007Spjd struct g_geom *gp; 156126007Spjd 157126007Spjd g_topology_assert(); 158126007Spjd gp = cp->geom; 159126007Spjd sc = gp->softc; 160126007Spjd if (sc == NULL) 161126007Spjd return; 162126007Spjd 163126007Spjd disk = cp->private; 164126007Spjd if (disk == NULL) /* Possible? */ 165126007Spjd return; 166126007Spjd g_concat_remove_disk(disk); 167126007Spjd} 168126007Spjd 169126007Spjdstatic int 170126007Spjdg_concat_access(struct g_provider *pp, int dr, int dw, int de) 171126007Spjd{ 172227004Smav struct g_consumer *cp1, *cp2, *tmp; 173227004Smav struct g_concat_disk *disk; 174126007Spjd struct g_geom *gp; 175126007Spjd int error; 176126007Spjd 177227004Smav g_topology_assert(); 178126007Spjd gp = pp->geom; 179126007Spjd 180126007Spjd /* On first open, grab an extra "exclusive" bit */ 181126007Spjd if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 182126007Spjd de++; 183126007Spjd /* ... and let go of it on last close */ 184132342Spjd if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 185126007Spjd de--; 186126007Spjd 187227004Smav LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { 188126007Spjd error = g_access(cp1, dr, dw, de); 189227004Smav if (error != 0) 190227004Smav goto fail; 191227004Smav disk = cp1->private; 192227004Smav if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && 193227004Smav disk->d_removed) { 194227004Smav g_concat_remove_disk(disk); /* May destroy geom. */ 195126007Spjd } 196126007Spjd } 197227004Smav return (0); 198126007Spjd 199227004Smavfail: 200227004Smav LIST_FOREACH(cp2, &gp->consumer, consumer) { 201227004Smav if (cp1 == cp2) 202227004Smav break; 203227004Smav g_access(cp2, -dr, -dw, -de); 204227004Smav } 205126007Spjd return (error); 206126007Spjd} 207126007Spjd 208126007Spjdstatic void 209212754Smavg_concat_kernel_dump(struct bio *bp) 210212754Smav{ 211212754Smav struct g_concat_softc *sc; 212212754Smav struct g_concat_disk *disk; 213212754Smav struct bio *cbp; 214212754Smav struct g_kerneldump *gkd; 215212754Smav u_int i; 216212754Smav 217212754Smav sc = bp->bio_to->geom->softc; 218212754Smav gkd = (struct g_kerneldump *)bp->bio_data; 219212754Smav for (i = 0; i < sc->sc_ndisks; i++) { 220212754Smav if (sc->sc_disks[i].d_start <= gkd->offset && 221212754Smav sc->sc_disks[i].d_end > gkd->offset) 222212754Smav break; 223212754Smav } 224212754Smav if (i == sc->sc_ndisks) 225212754Smav g_io_deliver(bp, EOPNOTSUPP); 226212754Smav disk = &sc->sc_disks[i]; 227212754Smav gkd->offset -= disk->d_start; 228212754Smav if (gkd->length > disk->d_end - disk->d_start - gkd->offset) 229212754Smav gkd->length = disk->d_end - disk->d_start - gkd->offset; 230212754Smav cbp = g_clone_bio(bp); 231212754Smav if (cbp == NULL) { 232212754Smav g_io_deliver(bp, ENOMEM); 233212754Smav return; 234212754Smav } 235212754Smav cbp->bio_done = g_std_done; 236212754Smav g_io_request(cbp, disk->d_consumer); 237212754Smav G_CONCAT_DEBUG(1, "Kernel dump will go to %s.", 238212754Smav disk->d_consumer->provider->name); 239212754Smav} 240212754Smav 241212754Smavstatic void 242163836Spjdg_concat_flush(struct g_concat_softc *sc, struct bio *bp) 243163836Spjd{ 244163836Spjd struct bio_queue_head queue; 245163836Spjd struct g_consumer *cp; 246163836Spjd struct bio *cbp; 247163836Spjd u_int no; 248163836Spjd 249163836Spjd bioq_init(&queue); 250163836Spjd for (no = 0; no < sc->sc_ndisks; no++) { 251163836Spjd cbp = g_clone_bio(bp); 252163836Spjd if (cbp == NULL) { 253163836Spjd for (cbp = bioq_first(&queue); cbp != NULL; 254163836Spjd cbp = bioq_first(&queue)) { 255163836Spjd bioq_remove(&queue, cbp); 256163836Spjd g_destroy_bio(cbp); 257163836Spjd } 258163836Spjd if (bp->bio_error == 0) 259163836Spjd bp->bio_error = ENOMEM; 260163836Spjd g_io_deliver(bp, bp->bio_error); 261163836Spjd return; 262163836Spjd } 263163836Spjd bioq_insert_tail(&queue, cbp); 264163836Spjd cbp->bio_done = g_std_done; 265163836Spjd cbp->bio_caller1 = sc->sc_disks[no].d_consumer; 266163836Spjd cbp->bio_to = sc->sc_disks[no].d_consumer->provider; 267163836Spjd } 268163836Spjd for (cbp = bioq_first(&queue); cbp != NULL; cbp = bioq_first(&queue)) { 269163836Spjd bioq_remove(&queue, cbp); 270163836Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 271163836Spjd cp = cbp->bio_caller1; 272163836Spjd cbp->bio_caller1 = NULL; 273163836Spjd g_io_request(cbp, cp); 274163836Spjd } 275163836Spjd} 276163836Spjd 277163836Spjdstatic void 278126007Spjdg_concat_start(struct bio *bp) 279126007Spjd{ 280144592Spjd struct bio_queue_head queue; 281126007Spjd struct g_concat_softc *sc; 282126007Spjd struct g_concat_disk *disk; 283126007Spjd struct g_provider *pp; 284126007Spjd off_t offset, end, length, off, len; 285126007Spjd struct bio *cbp; 286126007Spjd char *addr; 287126007Spjd u_int no; 288126007Spjd 289126007Spjd pp = bp->bio_to; 290126007Spjd sc = pp->geom->softc; 291126007Spjd /* 292126007Spjd * If sc == NULL, provider's error should be set and g_concat_start() 293126007Spjd * should not be called at all. 294126007Spjd */ 295126007Spjd KASSERT(sc != NULL, 296126007Spjd ("Provider's error should be set (error=%d)(device=%s).", 297126007Spjd bp->bio_to->error, bp->bio_to->name)); 298126007Spjd 299126007Spjd G_CONCAT_LOGREQ(bp, "Request received."); 300126007Spjd 301126007Spjd switch (bp->bio_cmd) { 302126007Spjd case BIO_READ: 303126007Spjd case BIO_WRITE: 304126007Spjd case BIO_DELETE: 305126007Spjd break; 306163836Spjd case BIO_FLUSH: 307163836Spjd g_concat_flush(sc, bp); 308163836Spjd return; 309126007Spjd case BIO_GETATTR: 310212754Smav if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 311212754Smav g_concat_kernel_dump(bp); 312212754Smav return; 313212754Smav } 314126007Spjd /* To which provider it should be delivered? */ 315212754Smav /* FALLTHROUGH */ 316126007Spjd default: 317126007Spjd g_io_deliver(bp, EOPNOTSUPP); 318126007Spjd return; 319126007Spjd } 320126007Spjd 321126007Spjd offset = bp->bio_offset; 322126007Spjd length = bp->bio_length; 323126007Spjd addr = bp->bio_data; 324126007Spjd end = offset + length; 325126007Spjd 326144592Spjd bioq_init(&queue); 327126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 328126007Spjd disk = &sc->sc_disks[no]; 329126007Spjd if (disk->d_end <= offset) 330126007Spjd continue; 331126007Spjd if (disk->d_start >= end) 332126007Spjd break; 333126007Spjd 334126007Spjd off = offset - disk->d_start; 335126007Spjd len = MIN(length, disk->d_end - offset); 336126007Spjd length -= len; 337126007Spjd offset += len; 338126007Spjd 339126007Spjd cbp = g_clone_bio(bp); 340126007Spjd if (cbp == NULL) { 341144592Spjd for (cbp = bioq_first(&queue); cbp != NULL; 342144592Spjd cbp = bioq_first(&queue)) { 343144592Spjd bioq_remove(&queue, cbp); 344144592Spjd g_destroy_bio(cbp); 345144592Spjd } 346126007Spjd if (bp->bio_error == 0) 347126007Spjd bp->bio_error = ENOMEM; 348144592Spjd g_io_deliver(bp, bp->bio_error); 349126007Spjd return; 350126007Spjd } 351144592Spjd bioq_insert_tail(&queue, cbp); 352126007Spjd /* 353126007Spjd * Fill in the component buf structure. 354126007Spjd */ 355126007Spjd cbp->bio_done = g_std_done; 356126007Spjd cbp->bio_offset = off; 357126007Spjd cbp->bio_data = addr; 358126007Spjd addr += len; 359126007Spjd cbp->bio_length = len; 360126007Spjd cbp->bio_to = disk->d_consumer->provider; 361144592Spjd cbp->bio_caller1 = disk; 362126007Spjd 363126007Spjd if (length == 0) 364126007Spjd break; 365126007Spjd } 366126007Spjd KASSERT(length == 0, 367126007Spjd ("Length is still greater than 0 (class=%s, name=%s).", 368126007Spjd bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 369144592Spjd for (cbp = bioq_first(&queue); cbp != NULL; cbp = bioq_first(&queue)) { 370144592Spjd bioq_remove(&queue, cbp); 371144592Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 372144592Spjd disk = cbp->bio_caller1; 373144592Spjd cbp->bio_caller1 = NULL; 374144592Spjd g_io_request(cbp, disk->d_consumer); 375144592Spjd } 376126007Spjd} 377126007Spjd 378126007Spjdstatic void 379126007Spjdg_concat_check_and_run(struct g_concat_softc *sc) 380126007Spjd{ 381126007Spjd struct g_concat_disk *disk; 382200942Smav struct g_provider *pp; 383126773Spjd u_int no, sectorsize = 0; 384126007Spjd off_t start; 385126007Spjd 386227004Smav g_topology_assert(); 387126007Spjd if (g_concat_nvalid(sc) != sc->sc_ndisks) 388126007Spjd return; 389126007Spjd 390200942Smav pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name); 391126007Spjd start = 0; 392126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 393126007Spjd disk = &sc->sc_disks[no]; 394126007Spjd disk->d_start = start; 395126007Spjd disk->d_end = disk->d_start + 396126007Spjd disk->d_consumer->provider->mediasize; 397126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 398126007Spjd disk->d_end -= disk->d_consumer->provider->sectorsize; 399126007Spjd start = disk->d_end; 400126773Spjd if (no == 0) 401126773Spjd sectorsize = disk->d_consumer->provider->sectorsize; 402126773Spjd else { 403126773Spjd sectorsize = lcm(sectorsize, 404126773Spjd disk->d_consumer->provider->sectorsize); 405126773Spjd } 406126007Spjd } 407200942Smav pp->sectorsize = sectorsize; 408126007Spjd /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 409200942Smav pp->mediasize = start; 410200942Smav pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; 411200942Smav pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; 412200942Smav sc->sc_provider = pp; 413200942Smav g_error_provider(pp, 0); 414126007Spjd 415227004Smav G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 416126007Spjd} 417126007Spjd 418126007Spjdstatic int 419126007Spjdg_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 420126007Spjd{ 421126007Spjd struct g_provider *pp; 422126007Spjd u_char *buf; 423126007Spjd int error; 424126007Spjd 425126007Spjd g_topology_assert(); 426126007Spjd 427126007Spjd error = g_access(cp, 1, 0, 0); 428126007Spjd if (error != 0) 429126007Spjd return (error); 430126007Spjd pp = cp->provider; 431126007Spjd g_topology_unlock(); 432126007Spjd buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 433126007Spjd &error); 434126007Spjd g_topology_lock(); 435126007Spjd g_access(cp, -1, 0, 0); 436126007Spjd if (buf == NULL) 437126007Spjd return (error); 438126007Spjd 439126007Spjd /* Decode metadata. */ 440126007Spjd concat_metadata_decode(buf, md); 441126007Spjd g_free(buf); 442126007Spjd 443126007Spjd return (0); 444126007Spjd} 445126007Spjd 446126007Spjd/* 447126007Spjd * Add disk to given device. 448126007Spjd */ 449126007Spjdstatic int 450126007Spjdg_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 451126007Spjd{ 452126007Spjd struct g_concat_disk *disk; 453129478Spjd struct g_consumer *cp, *fcp; 454126007Spjd struct g_geom *gp; 455126007Spjd int error; 456126007Spjd 457227004Smav g_topology_assert(); 458126007Spjd /* Metadata corrupted? */ 459126007Spjd if (no >= sc->sc_ndisks) 460126007Spjd return (EINVAL); 461126007Spjd 462126007Spjd disk = &sc->sc_disks[no]; 463126007Spjd /* Check if disk is not already attached. */ 464126565Spjd if (disk->d_consumer != NULL) 465126007Spjd return (EEXIST); 466126007Spjd 467129478Spjd gp = sc->sc_geom; 468129478Spjd fcp = LIST_FIRST(&gp->consumer); 469126007Spjd 470126007Spjd cp = g_new_consumer(gp); 471126007Spjd error = g_attach(cp, pp); 472126007Spjd if (error != 0) { 473126007Spjd g_destroy_consumer(cp); 474126007Spjd return (error); 475126007Spjd } 476126007Spjd 477129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 478129478Spjd error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 479126007Spjd if (error != 0) { 480126007Spjd g_detach(cp); 481126007Spjd g_destroy_consumer(cp); 482126007Spjd return (error); 483126007Spjd } 484126007Spjd } 485126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 486126007Spjd struct g_concat_metadata md; 487126007Spjd 488126773Spjd /* Re-read metadata. */ 489126007Spjd error = g_concat_read_metadata(cp, &md); 490126007Spjd if (error != 0) 491126007Spjd goto fail; 492126007Spjd 493126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 494126007Spjd strcmp(md.md_name, sc->sc_name) != 0 || 495126007Spjd md.md_id != sc->sc_id) { 496126007Spjd G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 497126007Spjd goto fail; 498126007Spjd } 499126007Spjd } 500126007Spjd 501126007Spjd cp->private = disk; 502126007Spjd disk->d_consumer = cp; 503126007Spjd disk->d_softc = sc; 504126007Spjd disk->d_start = 0; /* not yet */ 505126007Spjd disk->d_end = 0; /* not yet */ 506227004Smav disk->d_removed = 0; 507126007Spjd 508132663Spjd G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 509126007Spjd 510126007Spjd g_concat_check_and_run(sc); 511126007Spjd 512126007Spjd return (0); 513126007Spjdfail: 514129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 515129478Spjd g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 516126007Spjd g_detach(cp); 517126007Spjd g_destroy_consumer(cp); 518126007Spjd return (error); 519126007Spjd} 520126007Spjd 521126007Spjdstatic struct g_geom * 522126007Spjdg_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 523126773Spjd u_int type) 524126007Spjd{ 525126007Spjd struct g_concat_softc *sc; 526126007Spjd struct g_geom *gp; 527126007Spjd u_int no; 528126007Spjd 529132663Spjd G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 530126007Spjd md->md_id); 531126007Spjd 532155071Spjd /* One disks is minimum. */ 533155071Spjd if (md->md_all < 1) 534126007Spjd return (NULL); 535126007Spjd 536126007Spjd /* Check for duplicate unit */ 537126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 538126007Spjd sc = gp->softc; 539126007Spjd if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 540129478Spjd G_CONCAT_DEBUG(0, "Device %s already configured.", 541126007Spjd gp->name); 542126007Spjd return (NULL); 543126007Spjd } 544126007Spjd } 545132663Spjd gp = g_new_geomf(mp, "%s", md->md_name); 546132661Spjd sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 547126007Spjd gp->start = g_concat_start; 548126007Spjd gp->spoiled = g_concat_orphan; 549126007Spjd gp->orphan = g_concat_orphan; 550126007Spjd gp->access = g_concat_access; 551126007Spjd gp->dumpconf = g_concat_dumpconf; 552126007Spjd 553126007Spjd sc->sc_id = md->md_id; 554126007Spjd sc->sc_ndisks = md->md_all; 555126007Spjd sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 556126007Spjd M_CONCAT, M_WAITOK | M_ZERO); 557126007Spjd for (no = 0; no < sc->sc_ndisks; no++) 558126565Spjd sc->sc_disks[no].d_consumer = NULL; 559126007Spjd sc->sc_type = type; 560126007Spjd 561126007Spjd gp->softc = sc; 562129478Spjd sc->sc_geom = gp; 563129478Spjd sc->sc_provider = NULL; 564126007Spjd 565132663Spjd G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 566126007Spjd 567126007Spjd return (gp); 568126007Spjd} 569126007Spjd 570126007Spjdstatic int 571126007Spjdg_concat_destroy(struct g_concat_softc *sc, boolean_t force) 572126007Spjd{ 573126007Spjd struct g_provider *pp; 574227004Smav struct g_consumer *cp, *cp1; 575126007Spjd struct g_geom *gp; 576126007Spjd 577126007Spjd g_topology_assert(); 578126007Spjd 579126007Spjd if (sc == NULL) 580126007Spjd return (ENXIO); 581126007Spjd 582126007Spjd pp = sc->sc_provider; 583129478Spjd if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 584126007Spjd if (force) { 585126007Spjd G_CONCAT_DEBUG(0, "Device %s is still open, so it " 586129478Spjd "can't be definitely removed.", pp->name); 587126007Spjd } else { 588126007Spjd G_CONCAT_DEBUG(1, 589129478Spjd "Device %s is still open (r%dw%de%d).", pp->name, 590129478Spjd pp->acr, pp->acw, pp->ace); 591126007Spjd return (EBUSY); 592126007Spjd } 593126007Spjd } 594126007Spjd 595227004Smav gp = sc->sc_geom; 596227004Smav LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 597227004Smav g_concat_remove_disk(cp->private); 598227004Smav if (cp1 == NULL) 599227004Smav return (0); /* Recursion happened. */ 600126007Spjd } 601227004Smav if (!LIST_EMPTY(&gp->consumer)) 602227004Smav return (EINPROGRESS); 603126007Spjd 604126007Spjd gp->softc = NULL; 605129478Spjd KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 606129478Spjd gp->name)); 607126007Spjd free(sc->sc_disks, M_CONCAT); 608126007Spjd free(sc, M_CONCAT); 609126007Spjd 610227004Smav G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 611126007Spjd g_wither_geom(gp, ENXIO); 612126007Spjd return (0); 613126007Spjd} 614126007Spjd 615126007Spjdstatic int 616126773Spjdg_concat_destroy_geom(struct gctl_req *req __unused, 617126773Spjd struct g_class *mp __unused, struct g_geom *gp) 618126007Spjd{ 619126007Spjd struct g_concat_softc *sc; 620126007Spjd 621126007Spjd sc = gp->softc; 622126007Spjd return (g_concat_destroy(sc, 0)); 623126007Spjd} 624126007Spjd 625126007Spjdstatic struct g_geom * 626126007Spjdg_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 627126007Spjd{ 628126007Spjd struct g_concat_metadata md; 629126007Spjd struct g_concat_softc *sc; 630126007Spjd struct g_consumer *cp; 631126007Spjd struct g_geom *gp; 632126007Spjd int error; 633126007Spjd 634129478Spjd g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 635126007Spjd g_topology_assert(); 636126007Spjd 637197898Spjd /* Skip providers that are already open for writing. */ 638197898Spjd if (pp->acw > 0) 639197898Spjd return (NULL); 640197898Spjd 641126007Spjd G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 642126007Spjd 643126007Spjd gp = g_new_geomf(mp, "concat:taste"); 644126007Spjd gp->start = g_concat_start; 645126315Spjd gp->access = g_concat_access; 646126315Spjd gp->orphan = g_concat_orphan; 647126007Spjd cp = g_new_consumer(gp); 648126007Spjd g_attach(cp, pp); 649126007Spjd error = g_concat_read_metadata(cp, &md); 650133371Spjd g_detach(cp); 651133371Spjd g_destroy_consumer(cp); 652133371Spjd g_destroy_geom(gp); 653129478Spjd if (error != 0) 654126007Spjd return (NULL); 655126007Spjd gp = NULL; 656126007Spjd 657126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 658126007Spjd return (NULL); 659126007Spjd if (md.md_version > G_CONCAT_VERSION) { 660126007Spjd printf("geom_concat.ko module is too old to handle %s.\n", 661126007Spjd pp->name); 662126007Spjd return (NULL); 663126007Spjd } 664133373Spjd /* 665133373Spjd * Backward compatibility: 666133373Spjd */ 667142727Spjd /* There was no md_provider field in earlier versions of metadata. */ 668133373Spjd if (md.md_version < 3) 669133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 670142727Spjd /* There was no md_provsize field in earlier versions of metadata. */ 671142727Spjd if (md.md_version < 4) 672142727Spjd md.md_provsize = pp->mediasize; 673126007Spjd 674221101Smav if (md.md_provider[0] != '\0' && 675221101Smav !g_compare_names(md.md_provider, pp->name)) 676133373Spjd return (NULL); 677142727Spjd if (md.md_provsize != pp->mediasize) 678142727Spjd return (NULL); 679133373Spjd 680126007Spjd /* 681126007Spjd * Let's check if device already exists. 682126007Spjd */ 683128913Spjd sc = NULL; 684126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 685126007Spjd sc = gp->softc; 686126007Spjd if (sc == NULL) 687126007Spjd continue; 688126007Spjd if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 689126007Spjd continue; 690126007Spjd if (strcmp(md.md_name, sc->sc_name) != 0) 691126007Spjd continue; 692126007Spjd if (md.md_id != sc->sc_id) 693126007Spjd continue; 694126007Spjd break; 695126007Spjd } 696126007Spjd if (gp != NULL) { 697126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 698126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 699126007Spjd if (error != 0) { 700129478Spjd G_CONCAT_DEBUG(0, 701129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 702129478Spjd gp->name, error); 703126007Spjd return (NULL); 704126007Spjd } 705126007Spjd } else { 706126773Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 707126007Spjd if (gp == NULL) { 708132663Spjd G_CONCAT_DEBUG(0, "Cannot create device %s.", 709126007Spjd md.md_name); 710126007Spjd return (NULL); 711126007Spjd } 712126007Spjd sc = gp->softc; 713126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 714126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 715126007Spjd if (error != 0) { 716129478Spjd G_CONCAT_DEBUG(0, 717129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 718129478Spjd gp->name, error); 719126007Spjd g_concat_destroy(sc, 1); 720126007Spjd return (NULL); 721126007Spjd } 722126007Spjd } 723126007Spjd 724126007Spjd return (gp); 725126007Spjd} 726126007Spjd 727126007Spjdstatic void 728126007Spjdg_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 729126007Spjd{ 730126007Spjd u_int attached, no; 731129478Spjd struct g_concat_metadata md; 732126007Spjd struct g_provider *pp; 733126007Spjd struct g_concat_softc *sc; 734126007Spjd struct g_geom *gp; 735126007Spjd struct sbuf *sb; 736129478Spjd const char *name; 737129478Spjd char param[16]; 738129478Spjd int *nargs; 739126007Spjd 740126007Spjd g_topology_assert(); 741129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 742129478Spjd if (nargs == NULL) { 743129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 744126007Spjd return; 745126007Spjd } 746155071Spjd if (*nargs < 2) { 747129478Spjd gctl_error(req, "Too few arguments."); 748126007Spjd return; 749126007Spjd } 750126007Spjd 751129478Spjd strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 752129478Spjd md.md_version = G_CONCAT_VERSION; 753129478Spjd name = gctl_get_asciiparam(req, "arg0"); 754129478Spjd if (name == NULL) { 755129478Spjd gctl_error(req, "No 'arg%u' argument.", 0); 756129478Spjd return; 757129478Spjd } 758129478Spjd strlcpy(md.md_name, name, sizeof(md.md_name)); 759129478Spjd md.md_id = arc4random(); 760129478Spjd md.md_no = 0; 761129478Spjd md.md_all = *nargs - 1; 762133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 763142727Spjd /* This field is not important here. */ 764142727Spjd md.md_provsize = 0; 765129478Spjd 766126007Spjd /* Check all providers are valid */ 767129478Spjd for (no = 1; no < *nargs; no++) { 768129478Spjd snprintf(param, sizeof(param), "arg%u", no); 769129478Spjd name = gctl_get_asciiparam(req, param); 770129478Spjd if (name == NULL) { 771129478Spjd gctl_error(req, "No 'arg%u' argument.", no); 772129478Spjd return; 773129478Spjd } 774129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 775129478Spjd name += strlen("/dev/"); 776129478Spjd pp = g_provider_by_name(name); 777126007Spjd if (pp == NULL) { 778129478Spjd G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 779129478Spjd gctl_error(req, "Disk %s is invalid.", name); 780126007Spjd return; 781126007Spjd } 782126007Spjd } 783126007Spjd 784129478Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 785126007Spjd if (gp == NULL) { 786132663Spjd gctl_error(req, "Can't configure %s.", md.md_name); 787126007Spjd return; 788126007Spjd } 789126007Spjd 790126007Spjd sc = gp->softc; 791181463Sdes sb = sbuf_new_auto(); 792126007Spjd sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 793129478Spjd for (attached = 0, no = 1; no < *nargs; no++) { 794129478Spjd snprintf(param, sizeof(param), "arg%u", no); 795129478Spjd name = gctl_get_asciiparam(req, param); 796192021Strasz if (name == NULL) { 797192021Strasz gctl_error(req, "No 'arg%d' argument.", no); 798192021Strasz return; 799192021Strasz } 800129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 801129478Spjd name += strlen("/dev/"); 802129478Spjd pp = g_provider_by_name(name); 803129478Spjd KASSERT(pp != NULL, ("Provider %s disappear?!", name)); 804129478Spjd if (g_concat_add_disk(sc, pp, no - 1) != 0) { 805126007Spjd G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 806129478Spjd no, pp->name, gp->name); 807126007Spjd sbuf_printf(sb, " %s", pp->name); 808126007Spjd continue; 809126007Spjd } 810126007Spjd attached++; 811126007Spjd } 812126007Spjd sbuf_finish(sb); 813129478Spjd if (md.md_all != attached) { 814126007Spjd g_concat_destroy(gp->softc, 1); 815126007Spjd gctl_error(req, "%s", sbuf_data(sb)); 816126007Spjd } 817126007Spjd sbuf_delete(sb); 818126007Spjd} 819126007Spjd 820129478Spjdstatic struct g_concat_softc * 821129478Spjdg_concat_find_device(struct g_class *mp, const char *name) 822129478Spjd{ 823129478Spjd struct g_concat_softc *sc; 824129478Spjd struct g_geom *gp; 825129478Spjd 826129478Spjd LIST_FOREACH(gp, &mp->geom, geom) { 827129478Spjd sc = gp->softc; 828129478Spjd if (sc == NULL) 829129478Spjd continue; 830132663Spjd if (strcmp(sc->sc_name, name) == 0) 831129478Spjd return (sc); 832129478Spjd } 833129478Spjd return (NULL); 834129478Spjd} 835129478Spjd 836126007Spjdstatic void 837129478Spjdg_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 838126007Spjd{ 839126007Spjd struct g_concat_softc *sc; 840129478Spjd int *force, *nargs, error; 841129478Spjd const char *name; 842129478Spjd char param[16]; 843129478Spjd u_int i; 844126007Spjd 845126007Spjd g_topology_assert(); 846126773Spjd 847129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 848129478Spjd if (nargs == NULL) { 849129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 850129478Spjd return; 851129478Spjd } 852129478Spjd if (*nargs <= 0) { 853129478Spjd gctl_error(req, "Missing device(s)."); 854129478Spjd return; 855129478Spjd } 856126007Spjd force = gctl_get_paraml(req, "force", sizeof(*force)); 857126007Spjd if (force == NULL) { 858126773Spjd gctl_error(req, "No '%s' argument.", "force"); 859126007Spjd return; 860126007Spjd } 861129478Spjd 862129478Spjd for (i = 0; i < (u_int)*nargs; i++) { 863129478Spjd snprintf(param, sizeof(param), "arg%u", i); 864129478Spjd name = gctl_get_asciiparam(req, param); 865129478Spjd if (name == NULL) { 866129478Spjd gctl_error(req, "No 'arg%u' argument.", i); 867129478Spjd return; 868129478Spjd } 869129478Spjd sc = g_concat_find_device(mp, name); 870129478Spjd if (sc == NULL) { 871129478Spjd gctl_error(req, "No such device: %s.", name); 872129478Spjd return; 873129478Spjd } 874129478Spjd error = g_concat_destroy(sc, *force); 875129478Spjd if (error != 0) { 876129478Spjd gctl_error(req, "Cannot destroy device %s (error=%d).", 877132663Spjd sc->sc_name, error); 878129478Spjd return; 879129478Spjd } 880126007Spjd } 881126007Spjd} 882126007Spjd 883126007Spjdstatic void 884126007Spjdg_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 885126007Spjd{ 886129478Spjd uint32_t *version; 887126007Spjd 888126007Spjd g_topology_assert(); 889126007Spjd 890129478Spjd version = gctl_get_paraml(req, "version", sizeof(*version)); 891129478Spjd if (version == NULL) { 892129478Spjd gctl_error(req, "No '%s' argument.", "version"); 893126007Spjd return; 894126007Spjd } 895129478Spjd if (*version != G_CONCAT_VERSION) { 896129478Spjd gctl_error(req, "Userland and kernel parts are out of sync."); 897126007Spjd return; 898126007Spjd } 899129478Spjd 900129478Spjd if (strcmp(verb, "create") == 0) { 901129478Spjd g_concat_ctl_create(req, mp); 902126007Spjd return; 903131649Spjd } else if (strcmp(verb, "destroy") == 0 || 904131649Spjd strcmp(verb, "stop") == 0) { 905129478Spjd g_concat_ctl_destroy(req, mp); 906129478Spjd return; 907126007Spjd } 908126773Spjd gctl_error(req, "Unknown verb."); 909126007Spjd} 910126007Spjd 911126007Spjdstatic void 912126007Spjdg_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 913126007Spjd struct g_consumer *cp, struct g_provider *pp) 914126007Spjd{ 915128486Spjd struct g_concat_softc *sc; 916126007Spjd 917129747Spjd g_topology_assert(); 918128486Spjd sc = gp->softc; 919132665Spjd if (sc == NULL) 920128486Spjd return; 921132665Spjd if (pp != NULL) { 922132665Spjd /* Nothing here. */ 923132665Spjd } else if (cp != NULL) { 924132665Spjd struct g_concat_disk *disk; 925132665Spjd 926132665Spjd disk = cp->private; 927132665Spjd if (disk == NULL) 928132665Spjd return; 929132665Spjd sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 930132665Spjd (intmax_t)disk->d_end); 931132665Spjd sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 932132665Spjd (intmax_t)disk->d_start); 933132665Spjd } else { 934132665Spjd sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 935132665Spjd sbuf_printf(sb, "%s<Type>", indent); 936132665Spjd switch (sc->sc_type) { 937132665Spjd case G_CONCAT_TYPE_AUTOMATIC: 938132665Spjd sbuf_printf(sb, "AUTOMATIC"); 939132665Spjd break; 940132665Spjd case G_CONCAT_TYPE_MANUAL: 941132665Spjd sbuf_printf(sb, "MANUAL"); 942132665Spjd break; 943132665Spjd default: 944132665Spjd sbuf_printf(sb, "UNKNOWN"); 945132665Spjd break; 946132665Spjd } 947132665Spjd sbuf_printf(sb, "</Type>\n"); 948132665Spjd sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 949132665Spjd indent, sc->sc_ndisks, g_concat_nvalid(sc)); 950132665Spjd sbuf_printf(sb, "%s<State>", indent); 951132665Spjd if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 952132665Spjd sbuf_printf(sb, "UP"); 953132665Spjd else 954132665Spjd sbuf_printf(sb, "DOWN"); 955132665Spjd sbuf_printf(sb, "</State>\n"); 956128486Spjd } 957126007Spjd} 958126007Spjd 959126007SpjdDECLARE_GEOM_CLASS(g_concat_class, g_concat); 960