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 242260385Sscottlg_concat_done(struct bio *bp) 243260385Sscottl{ 244260385Sscottl struct g_concat_softc *sc; 245260385Sscottl struct bio *pbp; 246260385Sscottl 247260385Sscottl pbp = bp->bio_parent; 248260385Sscottl sc = pbp->bio_to->geom->softc; 249260385Sscottl mtx_lock(&sc->sc_lock); 250260385Sscottl if (pbp->bio_error == 0) 251260385Sscottl pbp->bio_error = bp->bio_error; 252260385Sscottl pbp->bio_completed += bp->bio_completed; 253260385Sscottl pbp->bio_inbed++; 254260385Sscottl if (pbp->bio_children == pbp->bio_inbed) { 255260385Sscottl mtx_unlock(&sc->sc_lock); 256260385Sscottl g_io_deliver(pbp, pbp->bio_error); 257260385Sscottl } else 258260385Sscottl mtx_unlock(&sc->sc_lock); 259260385Sscottl g_destroy_bio(bp); 260260385Sscottl} 261260385Sscottl 262260385Sscottlstatic void 263163836Spjdg_concat_flush(struct g_concat_softc *sc, struct bio *bp) 264163836Spjd{ 265163836Spjd struct bio_queue_head queue; 266163836Spjd struct g_consumer *cp; 267163836Spjd struct bio *cbp; 268163836Spjd u_int no; 269163836Spjd 270163836Spjd bioq_init(&queue); 271163836Spjd for (no = 0; no < sc->sc_ndisks; no++) { 272163836Spjd cbp = g_clone_bio(bp); 273163836Spjd if (cbp == NULL) { 274260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) 275163836Spjd g_destroy_bio(cbp); 276163836Spjd if (bp->bio_error == 0) 277163836Spjd bp->bio_error = ENOMEM; 278163836Spjd g_io_deliver(bp, bp->bio_error); 279163836Spjd return; 280163836Spjd } 281163836Spjd bioq_insert_tail(&queue, cbp); 282260385Sscottl cbp->bio_done = g_concat_done; 283163836Spjd cbp->bio_caller1 = sc->sc_disks[no].d_consumer; 284163836Spjd cbp->bio_to = sc->sc_disks[no].d_consumer->provider; 285163836Spjd } 286260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) { 287163836Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 288163836Spjd cp = cbp->bio_caller1; 289163836Spjd cbp->bio_caller1 = NULL; 290163836Spjd g_io_request(cbp, cp); 291163836Spjd } 292163836Spjd} 293163836Spjd 294163836Spjdstatic void 295126007Spjdg_concat_start(struct bio *bp) 296126007Spjd{ 297144592Spjd struct bio_queue_head queue; 298126007Spjd struct g_concat_softc *sc; 299126007Spjd struct g_concat_disk *disk; 300126007Spjd struct g_provider *pp; 301126007Spjd off_t offset, end, length, off, len; 302126007Spjd struct bio *cbp; 303126007Spjd char *addr; 304126007Spjd u_int no; 305126007Spjd 306126007Spjd pp = bp->bio_to; 307126007Spjd sc = pp->geom->softc; 308126007Spjd /* 309126007Spjd * If sc == NULL, provider's error should be set and g_concat_start() 310126007Spjd * should not be called at all. 311126007Spjd */ 312126007Spjd KASSERT(sc != NULL, 313126007Spjd ("Provider's error should be set (error=%d)(device=%s).", 314126007Spjd bp->bio_to->error, bp->bio_to->name)); 315126007Spjd 316126007Spjd G_CONCAT_LOGREQ(bp, "Request received."); 317126007Spjd 318126007Spjd switch (bp->bio_cmd) { 319126007Spjd case BIO_READ: 320126007Spjd case BIO_WRITE: 321126007Spjd case BIO_DELETE: 322126007Spjd break; 323163836Spjd case BIO_FLUSH: 324163836Spjd g_concat_flush(sc, bp); 325163836Spjd return; 326126007Spjd case BIO_GETATTR: 327212754Smav if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 328212754Smav g_concat_kernel_dump(bp); 329212754Smav return; 330212754Smav } 331126007Spjd /* To which provider it should be delivered? */ 332212754Smav /* FALLTHROUGH */ 333126007Spjd default: 334126007Spjd g_io_deliver(bp, EOPNOTSUPP); 335126007Spjd return; 336126007Spjd } 337126007Spjd 338126007Spjd offset = bp->bio_offset; 339126007Spjd length = bp->bio_length; 340260385Sscottl if ((bp->bio_flags & BIO_UNMAPPED) != 0) 341260385Sscottl addr = NULL; 342260385Sscottl else 343260385Sscottl addr = bp->bio_data; 344126007Spjd end = offset + length; 345126007Spjd 346144592Spjd bioq_init(&queue); 347126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 348126007Spjd disk = &sc->sc_disks[no]; 349126007Spjd if (disk->d_end <= offset) 350126007Spjd continue; 351126007Spjd if (disk->d_start >= end) 352126007Spjd break; 353126007Spjd 354126007Spjd off = offset - disk->d_start; 355126007Spjd len = MIN(length, disk->d_end - offset); 356126007Spjd length -= len; 357126007Spjd offset += len; 358126007Spjd 359126007Spjd cbp = g_clone_bio(bp); 360126007Spjd if (cbp == NULL) { 361260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) 362144592Spjd g_destroy_bio(cbp); 363126007Spjd if (bp->bio_error == 0) 364126007Spjd bp->bio_error = ENOMEM; 365144592Spjd g_io_deliver(bp, bp->bio_error); 366126007Spjd return; 367126007Spjd } 368144592Spjd bioq_insert_tail(&queue, cbp); 369126007Spjd /* 370126007Spjd * Fill in the component buf structure. 371126007Spjd */ 372260385Sscottl if (len == bp->bio_length) 373260385Sscottl cbp->bio_done = g_std_done; 374260385Sscottl else 375260385Sscottl cbp->bio_done = g_concat_done; 376126007Spjd cbp->bio_offset = off; 377260385Sscottl cbp->bio_length = len; 378260385Sscottl if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 379260385Sscottl cbp->bio_ma_offset += (uintptr_t)addr; 380260385Sscottl cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; 381260385Sscottl cbp->bio_ma_offset %= PAGE_SIZE; 382260385Sscottl cbp->bio_ma_n = round_page(cbp->bio_ma_offset + 383260385Sscottl cbp->bio_length) / PAGE_SIZE; 384260385Sscottl } else 385260385Sscottl cbp->bio_data = addr; 386126007Spjd addr += len; 387126007Spjd cbp->bio_to = disk->d_consumer->provider; 388144592Spjd cbp->bio_caller1 = disk; 389126007Spjd 390126007Spjd if (length == 0) 391126007Spjd break; 392126007Spjd } 393126007Spjd KASSERT(length == 0, 394126007Spjd ("Length is still greater than 0 (class=%s, name=%s).", 395126007Spjd bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 396260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) { 397144592Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 398144592Spjd disk = cbp->bio_caller1; 399144592Spjd cbp->bio_caller1 = NULL; 400144592Spjd g_io_request(cbp, disk->d_consumer); 401144592Spjd } 402126007Spjd} 403126007Spjd 404126007Spjdstatic void 405126007Spjdg_concat_check_and_run(struct g_concat_softc *sc) 406126007Spjd{ 407126007Spjd struct g_concat_disk *disk; 408260385Sscottl struct g_provider *dp, *pp; 409126773Spjd u_int no, sectorsize = 0; 410126007Spjd off_t start; 411126007Spjd 412227004Smav g_topology_assert(); 413126007Spjd if (g_concat_nvalid(sc) != sc->sc_ndisks) 414126007Spjd return; 415126007Spjd 416200942Smav pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name); 417260385Sscottl pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | 418260385Sscottl G_PF_ACCEPT_UNMAPPED; 419126007Spjd start = 0; 420126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 421126007Spjd disk = &sc->sc_disks[no]; 422260385Sscottl dp = disk->d_consumer->provider; 423126007Spjd disk->d_start = start; 424260385Sscottl disk->d_end = disk->d_start + dp->mediasize; 425126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 426260385Sscottl disk->d_end -= dp->sectorsize; 427126007Spjd start = disk->d_end; 428126773Spjd if (no == 0) 429260385Sscottl sectorsize = dp->sectorsize; 430260385Sscottl else 431260385Sscottl sectorsize = lcm(sectorsize, dp->sectorsize); 432260385Sscottl 433260385Sscottl /* A provider underneath us doesn't support unmapped */ 434260385Sscottl if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 435260385Sscottl G_CONCAT_DEBUG(1, "Cancelling unmapped " 436260385Sscottl "because of %s.", dp->name); 437260385Sscottl pp->flags &= ~G_PF_ACCEPT_UNMAPPED; 438126773Spjd } 439126007Spjd } 440200942Smav pp->sectorsize = sectorsize; 441126007Spjd /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 442200942Smav pp->mediasize = start; 443200942Smav pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; 444200942Smav pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; 445200942Smav sc->sc_provider = pp; 446200942Smav g_error_provider(pp, 0); 447126007Spjd 448227004Smav G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 449126007Spjd} 450126007Spjd 451126007Spjdstatic int 452126007Spjdg_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 453126007Spjd{ 454126007Spjd struct g_provider *pp; 455126007Spjd u_char *buf; 456126007Spjd int error; 457126007Spjd 458126007Spjd g_topology_assert(); 459126007Spjd 460126007Spjd error = g_access(cp, 1, 0, 0); 461126007Spjd if (error != 0) 462126007Spjd return (error); 463126007Spjd pp = cp->provider; 464126007Spjd g_topology_unlock(); 465126007Spjd buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 466126007Spjd &error); 467126007Spjd g_topology_lock(); 468126007Spjd g_access(cp, -1, 0, 0); 469126007Spjd if (buf == NULL) 470126007Spjd return (error); 471126007Spjd 472126007Spjd /* Decode metadata. */ 473126007Spjd concat_metadata_decode(buf, md); 474126007Spjd g_free(buf); 475126007Spjd 476126007Spjd return (0); 477126007Spjd} 478126007Spjd 479126007Spjd/* 480126007Spjd * Add disk to given device. 481126007Spjd */ 482126007Spjdstatic int 483126007Spjdg_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 484126007Spjd{ 485126007Spjd struct g_concat_disk *disk; 486129478Spjd struct g_consumer *cp, *fcp; 487126007Spjd struct g_geom *gp; 488126007Spjd int error; 489126007Spjd 490227004Smav g_topology_assert(); 491126007Spjd /* Metadata corrupted? */ 492126007Spjd if (no >= sc->sc_ndisks) 493126007Spjd return (EINVAL); 494126007Spjd 495126007Spjd disk = &sc->sc_disks[no]; 496126007Spjd /* Check if disk is not already attached. */ 497126565Spjd if (disk->d_consumer != NULL) 498126007Spjd return (EEXIST); 499126007Spjd 500129478Spjd gp = sc->sc_geom; 501129478Spjd fcp = LIST_FIRST(&gp->consumer); 502126007Spjd 503126007Spjd cp = g_new_consumer(gp); 504260385Sscottl cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 505126007Spjd error = g_attach(cp, pp); 506126007Spjd if (error != 0) { 507126007Spjd g_destroy_consumer(cp); 508126007Spjd return (error); 509126007Spjd } 510126007Spjd 511129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 512129478Spjd error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 513126007Spjd if (error != 0) { 514126007Spjd g_detach(cp); 515126007Spjd g_destroy_consumer(cp); 516126007Spjd return (error); 517126007Spjd } 518126007Spjd } 519126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 520126007Spjd struct g_concat_metadata md; 521126007Spjd 522126773Spjd /* Re-read metadata. */ 523126007Spjd error = g_concat_read_metadata(cp, &md); 524126007Spjd if (error != 0) 525126007Spjd goto fail; 526126007Spjd 527126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 528126007Spjd strcmp(md.md_name, sc->sc_name) != 0 || 529126007Spjd md.md_id != sc->sc_id) { 530126007Spjd G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 531126007Spjd goto fail; 532126007Spjd } 533126007Spjd } 534126007Spjd 535126007Spjd cp->private = disk; 536126007Spjd disk->d_consumer = cp; 537126007Spjd disk->d_softc = sc; 538126007Spjd disk->d_start = 0; /* not yet */ 539126007Spjd disk->d_end = 0; /* not yet */ 540227004Smav disk->d_removed = 0; 541126007Spjd 542132663Spjd G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 543126007Spjd 544126007Spjd g_concat_check_and_run(sc); 545126007Spjd 546126007Spjd return (0); 547126007Spjdfail: 548129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 549129478Spjd g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 550126007Spjd g_detach(cp); 551126007Spjd g_destroy_consumer(cp); 552126007Spjd return (error); 553126007Spjd} 554126007Spjd 555126007Spjdstatic struct g_geom * 556126007Spjdg_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 557126773Spjd u_int type) 558126007Spjd{ 559126007Spjd struct g_concat_softc *sc; 560126007Spjd struct g_geom *gp; 561126007Spjd u_int no; 562126007Spjd 563132663Spjd G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 564126007Spjd md->md_id); 565126007Spjd 566155071Spjd /* One disks is minimum. */ 567155071Spjd if (md->md_all < 1) 568126007Spjd return (NULL); 569126007Spjd 570126007Spjd /* Check for duplicate unit */ 571126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 572126007Spjd sc = gp->softc; 573126007Spjd if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 574129478Spjd G_CONCAT_DEBUG(0, "Device %s already configured.", 575126007Spjd gp->name); 576126007Spjd return (NULL); 577126007Spjd } 578126007Spjd } 579132663Spjd gp = g_new_geomf(mp, "%s", md->md_name); 580132661Spjd sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 581126007Spjd gp->start = g_concat_start; 582126007Spjd gp->spoiled = g_concat_orphan; 583126007Spjd gp->orphan = g_concat_orphan; 584126007Spjd gp->access = g_concat_access; 585126007Spjd gp->dumpconf = g_concat_dumpconf; 586126007Spjd 587126007Spjd sc->sc_id = md->md_id; 588126007Spjd sc->sc_ndisks = md->md_all; 589126007Spjd sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 590126007Spjd M_CONCAT, M_WAITOK | M_ZERO); 591126007Spjd for (no = 0; no < sc->sc_ndisks; no++) 592126565Spjd sc->sc_disks[no].d_consumer = NULL; 593126007Spjd sc->sc_type = type; 594260385Sscottl mtx_init(&sc->sc_lock, "gconcat lock", NULL, MTX_DEF); 595126007Spjd 596126007Spjd gp->softc = sc; 597129478Spjd sc->sc_geom = gp; 598129478Spjd sc->sc_provider = NULL; 599126007Spjd 600132663Spjd G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 601126007Spjd 602126007Spjd return (gp); 603126007Spjd} 604126007Spjd 605126007Spjdstatic int 606126007Spjdg_concat_destroy(struct g_concat_softc *sc, boolean_t force) 607126007Spjd{ 608126007Spjd struct g_provider *pp; 609227004Smav struct g_consumer *cp, *cp1; 610126007Spjd struct g_geom *gp; 611126007Spjd 612126007Spjd g_topology_assert(); 613126007Spjd 614126007Spjd if (sc == NULL) 615126007Spjd return (ENXIO); 616126007Spjd 617126007Spjd pp = sc->sc_provider; 618129478Spjd if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 619126007Spjd if (force) { 620126007Spjd G_CONCAT_DEBUG(0, "Device %s is still open, so it " 621129478Spjd "can't be definitely removed.", pp->name); 622126007Spjd } else { 623126007Spjd G_CONCAT_DEBUG(1, 624129478Spjd "Device %s is still open (r%dw%de%d).", pp->name, 625129478Spjd pp->acr, pp->acw, pp->ace); 626126007Spjd return (EBUSY); 627126007Spjd } 628126007Spjd } 629126007Spjd 630227004Smav gp = sc->sc_geom; 631227004Smav LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 632227004Smav g_concat_remove_disk(cp->private); 633227004Smav if (cp1 == NULL) 634227004Smav return (0); /* Recursion happened. */ 635126007Spjd } 636227004Smav if (!LIST_EMPTY(&gp->consumer)) 637227004Smav return (EINPROGRESS); 638126007Spjd 639126007Spjd gp->softc = NULL; 640129478Spjd KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 641129478Spjd gp->name)); 642126007Spjd free(sc->sc_disks, M_CONCAT); 643260385Sscottl mtx_destroy(&sc->sc_lock); 644126007Spjd free(sc, M_CONCAT); 645126007Spjd 646227004Smav G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 647126007Spjd g_wither_geom(gp, ENXIO); 648126007Spjd return (0); 649126007Spjd} 650126007Spjd 651126007Spjdstatic int 652126773Spjdg_concat_destroy_geom(struct gctl_req *req __unused, 653126773Spjd struct g_class *mp __unused, struct g_geom *gp) 654126007Spjd{ 655126007Spjd struct g_concat_softc *sc; 656126007Spjd 657126007Spjd sc = gp->softc; 658126007Spjd return (g_concat_destroy(sc, 0)); 659126007Spjd} 660126007Spjd 661126007Spjdstatic struct g_geom * 662126007Spjdg_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 663126007Spjd{ 664126007Spjd struct g_concat_metadata md; 665126007Spjd struct g_concat_softc *sc; 666126007Spjd struct g_consumer *cp; 667126007Spjd struct g_geom *gp; 668126007Spjd int error; 669126007Spjd 670129478Spjd g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 671126007Spjd g_topology_assert(); 672126007Spjd 673197898Spjd /* Skip providers that are already open for writing. */ 674197898Spjd if (pp->acw > 0) 675197898Spjd return (NULL); 676197898Spjd 677126007Spjd G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 678126007Spjd 679126007Spjd gp = g_new_geomf(mp, "concat:taste"); 680126007Spjd gp->start = g_concat_start; 681126315Spjd gp->access = g_concat_access; 682126315Spjd gp->orphan = g_concat_orphan; 683126007Spjd cp = g_new_consumer(gp); 684126007Spjd g_attach(cp, pp); 685126007Spjd error = g_concat_read_metadata(cp, &md); 686133371Spjd g_detach(cp); 687133371Spjd g_destroy_consumer(cp); 688133371Spjd g_destroy_geom(gp); 689129478Spjd if (error != 0) 690126007Spjd return (NULL); 691126007Spjd gp = NULL; 692126007Spjd 693126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 694126007Spjd return (NULL); 695126007Spjd if (md.md_version > G_CONCAT_VERSION) { 696126007Spjd printf("geom_concat.ko module is too old to handle %s.\n", 697126007Spjd pp->name); 698126007Spjd return (NULL); 699126007Spjd } 700133373Spjd /* 701133373Spjd * Backward compatibility: 702133373Spjd */ 703142727Spjd /* There was no md_provider field in earlier versions of metadata. */ 704133373Spjd if (md.md_version < 3) 705133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 706142727Spjd /* There was no md_provsize field in earlier versions of metadata. */ 707142727Spjd if (md.md_version < 4) 708142727Spjd md.md_provsize = pp->mediasize; 709126007Spjd 710221101Smav if (md.md_provider[0] != '\0' && 711221101Smav !g_compare_names(md.md_provider, pp->name)) 712133373Spjd return (NULL); 713142727Spjd if (md.md_provsize != pp->mediasize) 714142727Spjd return (NULL); 715133373Spjd 716126007Spjd /* 717126007Spjd * Let's check if device already exists. 718126007Spjd */ 719128913Spjd sc = NULL; 720126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 721126007Spjd sc = gp->softc; 722126007Spjd if (sc == NULL) 723126007Spjd continue; 724126007Spjd if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 725126007Spjd continue; 726126007Spjd if (strcmp(md.md_name, sc->sc_name) != 0) 727126007Spjd continue; 728126007Spjd if (md.md_id != sc->sc_id) 729126007Spjd continue; 730126007Spjd break; 731126007Spjd } 732126007Spjd if (gp != NULL) { 733126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 734126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 735126007Spjd if (error != 0) { 736129478Spjd G_CONCAT_DEBUG(0, 737129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 738129478Spjd gp->name, error); 739126007Spjd return (NULL); 740126007Spjd } 741126007Spjd } else { 742126773Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 743126007Spjd if (gp == NULL) { 744132663Spjd G_CONCAT_DEBUG(0, "Cannot create device %s.", 745126007Spjd md.md_name); 746126007Spjd return (NULL); 747126007Spjd } 748126007Spjd sc = gp->softc; 749126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 750126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 751126007Spjd if (error != 0) { 752129478Spjd G_CONCAT_DEBUG(0, 753129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 754129478Spjd gp->name, error); 755126007Spjd g_concat_destroy(sc, 1); 756126007Spjd return (NULL); 757126007Spjd } 758126007Spjd } 759126007Spjd 760126007Spjd return (gp); 761126007Spjd} 762126007Spjd 763126007Spjdstatic void 764126007Spjdg_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 765126007Spjd{ 766126007Spjd u_int attached, no; 767129478Spjd struct g_concat_metadata md; 768126007Spjd struct g_provider *pp; 769126007Spjd struct g_concat_softc *sc; 770126007Spjd struct g_geom *gp; 771126007Spjd struct sbuf *sb; 772129478Spjd const char *name; 773129478Spjd char param[16]; 774129478Spjd int *nargs; 775126007Spjd 776126007Spjd g_topology_assert(); 777129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 778129478Spjd if (nargs == NULL) { 779129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 780126007Spjd return; 781126007Spjd } 782155071Spjd if (*nargs < 2) { 783129478Spjd gctl_error(req, "Too few arguments."); 784126007Spjd return; 785126007Spjd } 786126007Spjd 787129478Spjd strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 788129478Spjd md.md_version = G_CONCAT_VERSION; 789129478Spjd name = gctl_get_asciiparam(req, "arg0"); 790129478Spjd if (name == NULL) { 791129478Spjd gctl_error(req, "No 'arg%u' argument.", 0); 792129478Spjd return; 793129478Spjd } 794129478Spjd strlcpy(md.md_name, name, sizeof(md.md_name)); 795129478Spjd md.md_id = arc4random(); 796129478Spjd md.md_no = 0; 797129478Spjd md.md_all = *nargs - 1; 798133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 799142727Spjd /* This field is not important here. */ 800142727Spjd md.md_provsize = 0; 801129478Spjd 802126007Spjd /* Check all providers are valid */ 803129478Spjd for (no = 1; no < *nargs; no++) { 804129478Spjd snprintf(param, sizeof(param), "arg%u", no); 805129478Spjd name = gctl_get_asciiparam(req, param); 806129478Spjd if (name == NULL) { 807129478Spjd gctl_error(req, "No 'arg%u' argument.", no); 808129478Spjd return; 809129478Spjd } 810129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 811129478Spjd name += strlen("/dev/"); 812129478Spjd pp = g_provider_by_name(name); 813126007Spjd if (pp == NULL) { 814129478Spjd G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 815129478Spjd gctl_error(req, "Disk %s is invalid.", name); 816126007Spjd return; 817126007Spjd } 818126007Spjd } 819126007Spjd 820129478Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 821126007Spjd if (gp == NULL) { 822132663Spjd gctl_error(req, "Can't configure %s.", md.md_name); 823126007Spjd return; 824126007Spjd } 825126007Spjd 826126007Spjd sc = gp->softc; 827181463Sdes sb = sbuf_new_auto(); 828126007Spjd sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 829129478Spjd for (attached = 0, no = 1; no < *nargs; no++) { 830129478Spjd snprintf(param, sizeof(param), "arg%u", no); 831129478Spjd name = gctl_get_asciiparam(req, param); 832192021Strasz if (name == NULL) { 833192021Strasz gctl_error(req, "No 'arg%d' argument.", no); 834192021Strasz return; 835192021Strasz } 836129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 837129478Spjd name += strlen("/dev/"); 838129478Spjd pp = g_provider_by_name(name); 839129478Spjd KASSERT(pp != NULL, ("Provider %s disappear?!", name)); 840129478Spjd if (g_concat_add_disk(sc, pp, no - 1) != 0) { 841126007Spjd G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 842129478Spjd no, pp->name, gp->name); 843126007Spjd sbuf_printf(sb, " %s", pp->name); 844126007Spjd continue; 845126007Spjd } 846126007Spjd attached++; 847126007Spjd } 848126007Spjd sbuf_finish(sb); 849129478Spjd if (md.md_all != attached) { 850126007Spjd g_concat_destroy(gp->softc, 1); 851126007Spjd gctl_error(req, "%s", sbuf_data(sb)); 852126007Spjd } 853126007Spjd sbuf_delete(sb); 854126007Spjd} 855126007Spjd 856129478Spjdstatic struct g_concat_softc * 857129478Spjdg_concat_find_device(struct g_class *mp, const char *name) 858129478Spjd{ 859129478Spjd struct g_concat_softc *sc; 860129478Spjd struct g_geom *gp; 861129478Spjd 862129478Spjd LIST_FOREACH(gp, &mp->geom, geom) { 863129478Spjd sc = gp->softc; 864129478Spjd if (sc == NULL) 865129478Spjd continue; 866132663Spjd if (strcmp(sc->sc_name, name) == 0) 867129478Spjd return (sc); 868129478Spjd } 869129478Spjd return (NULL); 870129478Spjd} 871129478Spjd 872126007Spjdstatic void 873129478Spjdg_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 874126007Spjd{ 875126007Spjd struct g_concat_softc *sc; 876129478Spjd int *force, *nargs, error; 877129478Spjd const char *name; 878129478Spjd char param[16]; 879129478Spjd u_int i; 880126007Spjd 881126007Spjd g_topology_assert(); 882126773Spjd 883129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 884129478Spjd if (nargs == NULL) { 885129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 886129478Spjd return; 887129478Spjd } 888129478Spjd if (*nargs <= 0) { 889129478Spjd gctl_error(req, "Missing device(s)."); 890129478Spjd return; 891129478Spjd } 892126007Spjd force = gctl_get_paraml(req, "force", sizeof(*force)); 893126007Spjd if (force == NULL) { 894126773Spjd gctl_error(req, "No '%s' argument.", "force"); 895126007Spjd return; 896126007Spjd } 897129478Spjd 898129478Spjd for (i = 0; i < (u_int)*nargs; i++) { 899129478Spjd snprintf(param, sizeof(param), "arg%u", i); 900129478Spjd name = gctl_get_asciiparam(req, param); 901129478Spjd if (name == NULL) { 902129478Spjd gctl_error(req, "No 'arg%u' argument.", i); 903129478Spjd return; 904129478Spjd } 905129478Spjd sc = g_concat_find_device(mp, name); 906129478Spjd if (sc == NULL) { 907129478Spjd gctl_error(req, "No such device: %s.", name); 908129478Spjd return; 909129478Spjd } 910129478Spjd error = g_concat_destroy(sc, *force); 911129478Spjd if (error != 0) { 912129478Spjd gctl_error(req, "Cannot destroy device %s (error=%d).", 913132663Spjd sc->sc_name, error); 914129478Spjd return; 915129478Spjd } 916126007Spjd } 917126007Spjd} 918126007Spjd 919126007Spjdstatic void 920126007Spjdg_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 921126007Spjd{ 922129478Spjd uint32_t *version; 923126007Spjd 924126007Spjd g_topology_assert(); 925126007Spjd 926129478Spjd version = gctl_get_paraml(req, "version", sizeof(*version)); 927129478Spjd if (version == NULL) { 928129478Spjd gctl_error(req, "No '%s' argument.", "version"); 929126007Spjd return; 930126007Spjd } 931129478Spjd if (*version != G_CONCAT_VERSION) { 932129478Spjd gctl_error(req, "Userland and kernel parts are out of sync."); 933126007Spjd return; 934126007Spjd } 935129478Spjd 936129478Spjd if (strcmp(verb, "create") == 0) { 937129478Spjd g_concat_ctl_create(req, mp); 938126007Spjd return; 939131649Spjd } else if (strcmp(verb, "destroy") == 0 || 940131649Spjd strcmp(verb, "stop") == 0) { 941129478Spjd g_concat_ctl_destroy(req, mp); 942129478Spjd return; 943126007Spjd } 944126773Spjd gctl_error(req, "Unknown verb."); 945126007Spjd} 946126007Spjd 947126007Spjdstatic void 948126007Spjdg_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 949126007Spjd struct g_consumer *cp, struct g_provider *pp) 950126007Spjd{ 951128486Spjd struct g_concat_softc *sc; 952126007Spjd 953129747Spjd g_topology_assert(); 954128486Spjd sc = gp->softc; 955132665Spjd if (sc == NULL) 956128486Spjd return; 957132665Spjd if (pp != NULL) { 958132665Spjd /* Nothing here. */ 959132665Spjd } else if (cp != NULL) { 960132665Spjd struct g_concat_disk *disk; 961132665Spjd 962132665Spjd disk = cp->private; 963132665Spjd if (disk == NULL) 964132665Spjd return; 965132665Spjd sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 966132665Spjd (intmax_t)disk->d_end); 967132665Spjd sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 968132665Spjd (intmax_t)disk->d_start); 969132665Spjd } else { 970132665Spjd sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 971132665Spjd sbuf_printf(sb, "%s<Type>", indent); 972132665Spjd switch (sc->sc_type) { 973132665Spjd case G_CONCAT_TYPE_AUTOMATIC: 974132665Spjd sbuf_printf(sb, "AUTOMATIC"); 975132665Spjd break; 976132665Spjd case G_CONCAT_TYPE_MANUAL: 977132665Spjd sbuf_printf(sb, "MANUAL"); 978132665Spjd break; 979132665Spjd default: 980132665Spjd sbuf_printf(sb, "UNKNOWN"); 981132665Spjd break; 982132665Spjd } 983132665Spjd sbuf_printf(sb, "</Type>\n"); 984132665Spjd sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 985132665Spjd indent, sc->sc_ndisks, g_concat_nvalid(sc)); 986132665Spjd sbuf_printf(sb, "%s<State>", indent); 987132665Spjd if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 988132665Spjd sbuf_printf(sb, "UP"); 989132665Spjd else 990132665Spjd sbuf_printf(sb, "DOWN"); 991132665Spjd sbuf_printf(sb, "</State>\n"); 992128486Spjd } 993126007Spjd} 994126007Spjd 995126007SpjdDECLARE_GEOM_CLASS(g_concat_class, g_concat); 996