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: stable/10/sys/geom/concat/g_concat.c 306765 2016-10-06 15:36:13Z mav $"); 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) { 133227004Smav G_CONCAT_DEBUG(0, "Device %s deactivated.", 134227004Smav sc->sc_provider->name); 135306765Smav g_wither_provider(sc->sc_provider, ENXIO); 136129478Spjd sc->sc_provider = NULL; 137129478Spjd } 138126007Spjd 139126007Spjd if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 140227004Smav return; 141227004Smav disk->d_consumer = NULL; 142126007Spjd g_detach(cp); 143126007Spjd g_destroy_consumer(cp); 144227004Smav /* If there are no valid disks anymore, remove device. */ 145227004Smav if (LIST_EMPTY(&sc->sc_geom->consumer)) 146227004Smav g_concat_destroy(sc, 1); 147126007Spjd} 148126007Spjd 149126007Spjdstatic void 150126007Spjdg_concat_orphan(struct g_consumer *cp) 151126007Spjd{ 152126007Spjd struct g_concat_softc *sc; 153126007Spjd struct g_concat_disk *disk; 154126007Spjd struct g_geom *gp; 155126007Spjd 156126007Spjd g_topology_assert(); 157126007Spjd gp = cp->geom; 158126007Spjd sc = gp->softc; 159126007Spjd if (sc == NULL) 160126007Spjd return; 161126007Spjd 162126007Spjd disk = cp->private; 163126007Spjd if (disk == NULL) /* Possible? */ 164126007Spjd return; 165126007Spjd g_concat_remove_disk(disk); 166126007Spjd} 167126007Spjd 168126007Spjdstatic int 169126007Spjdg_concat_access(struct g_provider *pp, int dr, int dw, int de) 170126007Spjd{ 171227004Smav struct g_consumer *cp1, *cp2, *tmp; 172227004Smav struct g_concat_disk *disk; 173126007Spjd struct g_geom *gp; 174126007Spjd int error; 175126007Spjd 176227004Smav g_topology_assert(); 177126007Spjd gp = pp->geom; 178126007Spjd 179126007Spjd /* On first open, grab an extra "exclusive" bit */ 180126007Spjd if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 181126007Spjd de++; 182126007Spjd /* ... and let go of it on last close */ 183132342Spjd if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 184126007Spjd de--; 185126007Spjd 186227004Smav LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { 187126007Spjd error = g_access(cp1, dr, dw, de); 188227004Smav if (error != 0) 189227004Smav goto fail; 190227004Smav disk = cp1->private; 191227004Smav if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && 192227004Smav disk->d_removed) { 193227004Smav g_concat_remove_disk(disk); /* May destroy geom. */ 194126007Spjd } 195126007Spjd } 196227004Smav return (0); 197126007Spjd 198227004Smavfail: 199227004Smav LIST_FOREACH(cp2, &gp->consumer, consumer) { 200227004Smav if (cp1 == cp2) 201227004Smav break; 202227004Smav g_access(cp2, -dr, -dw, -de); 203227004Smav } 204126007Spjd return (error); 205126007Spjd} 206126007Spjd 207126007Spjdstatic void 208212754Smavg_concat_kernel_dump(struct bio *bp) 209212754Smav{ 210212754Smav struct g_concat_softc *sc; 211212754Smav struct g_concat_disk *disk; 212212754Smav struct bio *cbp; 213212754Smav struct g_kerneldump *gkd; 214212754Smav u_int i; 215212754Smav 216212754Smav sc = bp->bio_to->geom->softc; 217212754Smav gkd = (struct g_kerneldump *)bp->bio_data; 218212754Smav for (i = 0; i < sc->sc_ndisks; i++) { 219212754Smav if (sc->sc_disks[i].d_start <= gkd->offset && 220212754Smav sc->sc_disks[i].d_end > gkd->offset) 221212754Smav break; 222212754Smav } 223212754Smav if (i == sc->sc_ndisks) 224212754Smav g_io_deliver(bp, EOPNOTSUPP); 225212754Smav disk = &sc->sc_disks[i]; 226212754Smav gkd->offset -= disk->d_start; 227212754Smav if (gkd->length > disk->d_end - disk->d_start - gkd->offset) 228212754Smav gkd->length = disk->d_end - disk->d_start - gkd->offset; 229212754Smav cbp = g_clone_bio(bp); 230212754Smav if (cbp == NULL) { 231212754Smav g_io_deliver(bp, ENOMEM); 232212754Smav return; 233212754Smav } 234212754Smav cbp->bio_done = g_std_done; 235212754Smav g_io_request(cbp, disk->d_consumer); 236212754Smav G_CONCAT_DEBUG(1, "Kernel dump will go to %s.", 237212754Smav disk->d_consumer->provider->name); 238212754Smav} 239212754Smav 240212754Smavstatic void 241260385Sscottlg_concat_done(struct bio *bp) 242260385Sscottl{ 243260385Sscottl struct g_concat_softc *sc; 244260385Sscottl struct bio *pbp; 245260385Sscottl 246260385Sscottl pbp = bp->bio_parent; 247260385Sscottl sc = pbp->bio_to->geom->softc; 248260385Sscottl mtx_lock(&sc->sc_lock); 249260385Sscottl if (pbp->bio_error == 0) 250260385Sscottl pbp->bio_error = bp->bio_error; 251260385Sscottl pbp->bio_completed += bp->bio_completed; 252260385Sscottl pbp->bio_inbed++; 253260385Sscottl if (pbp->bio_children == pbp->bio_inbed) { 254260385Sscottl mtx_unlock(&sc->sc_lock); 255260385Sscottl g_io_deliver(pbp, pbp->bio_error); 256260385Sscottl } else 257260385Sscottl mtx_unlock(&sc->sc_lock); 258260385Sscottl g_destroy_bio(bp); 259260385Sscottl} 260260385Sscottl 261260385Sscottlstatic void 262163836Spjdg_concat_flush(struct g_concat_softc *sc, struct bio *bp) 263163836Spjd{ 264163836Spjd struct bio_queue_head queue; 265163836Spjd struct g_consumer *cp; 266163836Spjd struct bio *cbp; 267163836Spjd u_int no; 268163836Spjd 269163836Spjd bioq_init(&queue); 270163836Spjd for (no = 0; no < sc->sc_ndisks; no++) { 271163836Spjd cbp = g_clone_bio(bp); 272163836Spjd if (cbp == NULL) { 273260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) 274163836Spjd g_destroy_bio(cbp); 275163836Spjd if (bp->bio_error == 0) 276163836Spjd bp->bio_error = ENOMEM; 277163836Spjd g_io_deliver(bp, bp->bio_error); 278163836Spjd return; 279163836Spjd } 280163836Spjd bioq_insert_tail(&queue, cbp); 281260385Sscottl cbp->bio_done = g_concat_done; 282163836Spjd cbp->bio_caller1 = sc->sc_disks[no].d_consumer; 283163836Spjd cbp->bio_to = sc->sc_disks[no].d_consumer->provider; 284163836Spjd } 285260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) { 286163836Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 287163836Spjd cp = cbp->bio_caller1; 288163836Spjd cbp->bio_caller1 = NULL; 289163836Spjd g_io_request(cbp, cp); 290163836Spjd } 291163836Spjd} 292163836Spjd 293163836Spjdstatic void 294126007Spjdg_concat_start(struct bio *bp) 295126007Spjd{ 296144592Spjd struct bio_queue_head queue; 297126007Spjd struct g_concat_softc *sc; 298126007Spjd struct g_concat_disk *disk; 299126007Spjd struct g_provider *pp; 300126007Spjd off_t offset, end, length, off, len; 301126007Spjd struct bio *cbp; 302126007Spjd char *addr; 303126007Spjd u_int no; 304126007Spjd 305126007Spjd pp = bp->bio_to; 306126007Spjd sc = pp->geom->softc; 307126007Spjd /* 308126007Spjd * If sc == NULL, provider's error should be set and g_concat_start() 309126007Spjd * should not be called at all. 310126007Spjd */ 311126007Spjd KASSERT(sc != NULL, 312126007Spjd ("Provider's error should be set (error=%d)(device=%s).", 313126007Spjd bp->bio_to->error, bp->bio_to->name)); 314126007Spjd 315126007Spjd G_CONCAT_LOGREQ(bp, "Request received."); 316126007Spjd 317126007Spjd switch (bp->bio_cmd) { 318126007Spjd case BIO_READ: 319126007Spjd case BIO_WRITE: 320126007Spjd case BIO_DELETE: 321126007Spjd break; 322163836Spjd case BIO_FLUSH: 323163836Spjd g_concat_flush(sc, bp); 324163836Spjd return; 325126007Spjd case BIO_GETATTR: 326212754Smav if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 327212754Smav g_concat_kernel_dump(bp); 328212754Smav return; 329212754Smav } 330126007Spjd /* To which provider it should be delivered? */ 331212754Smav /* FALLTHROUGH */ 332126007Spjd default: 333126007Spjd g_io_deliver(bp, EOPNOTSUPP); 334126007Spjd return; 335126007Spjd } 336126007Spjd 337126007Spjd offset = bp->bio_offset; 338126007Spjd length = bp->bio_length; 339260385Sscottl if ((bp->bio_flags & BIO_UNMAPPED) != 0) 340260385Sscottl addr = NULL; 341260385Sscottl else 342260385Sscottl addr = bp->bio_data; 343126007Spjd end = offset + length; 344126007Spjd 345144592Spjd bioq_init(&queue); 346126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 347126007Spjd disk = &sc->sc_disks[no]; 348126007Spjd if (disk->d_end <= offset) 349126007Spjd continue; 350126007Spjd if (disk->d_start >= end) 351126007Spjd break; 352126007Spjd 353126007Spjd off = offset - disk->d_start; 354126007Spjd len = MIN(length, disk->d_end - offset); 355126007Spjd length -= len; 356126007Spjd offset += len; 357126007Spjd 358126007Spjd cbp = g_clone_bio(bp); 359126007Spjd if (cbp == NULL) { 360260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) 361144592Spjd g_destroy_bio(cbp); 362126007Spjd if (bp->bio_error == 0) 363126007Spjd bp->bio_error = ENOMEM; 364144592Spjd g_io_deliver(bp, bp->bio_error); 365126007Spjd return; 366126007Spjd } 367144592Spjd bioq_insert_tail(&queue, cbp); 368126007Spjd /* 369126007Spjd * Fill in the component buf structure. 370126007Spjd */ 371260385Sscottl if (len == bp->bio_length) 372260385Sscottl cbp->bio_done = g_std_done; 373260385Sscottl else 374260385Sscottl cbp->bio_done = g_concat_done; 375126007Spjd cbp->bio_offset = off; 376260385Sscottl cbp->bio_length = len; 377260385Sscottl if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 378260385Sscottl cbp->bio_ma_offset += (uintptr_t)addr; 379260385Sscottl cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; 380260385Sscottl cbp->bio_ma_offset %= PAGE_SIZE; 381260385Sscottl cbp->bio_ma_n = round_page(cbp->bio_ma_offset + 382260385Sscottl cbp->bio_length) / PAGE_SIZE; 383260385Sscottl } else 384260385Sscottl cbp->bio_data = addr; 385126007Spjd addr += len; 386126007Spjd cbp->bio_to = disk->d_consumer->provider; 387144592Spjd cbp->bio_caller1 = disk; 388126007Spjd 389126007Spjd if (length == 0) 390126007Spjd break; 391126007Spjd } 392126007Spjd KASSERT(length == 0, 393126007Spjd ("Length is still greater than 0 (class=%s, name=%s).", 394126007Spjd bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 395260385Sscottl while ((cbp = bioq_takefirst(&queue)) != NULL) { 396144592Spjd G_CONCAT_LOGREQ(cbp, "Sending request."); 397144592Spjd disk = cbp->bio_caller1; 398144592Spjd cbp->bio_caller1 = NULL; 399144592Spjd g_io_request(cbp, disk->d_consumer); 400144592Spjd } 401126007Spjd} 402126007Spjd 403126007Spjdstatic void 404126007Spjdg_concat_check_and_run(struct g_concat_softc *sc) 405126007Spjd{ 406126007Spjd struct g_concat_disk *disk; 407260385Sscottl struct g_provider *dp, *pp; 408126773Spjd u_int no, sectorsize = 0; 409126007Spjd off_t start; 410126007Spjd 411227004Smav g_topology_assert(); 412126007Spjd if (g_concat_nvalid(sc) != sc->sc_ndisks) 413126007Spjd return; 414126007Spjd 415200942Smav pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name); 416260385Sscottl pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | 417260385Sscottl G_PF_ACCEPT_UNMAPPED; 418126007Spjd start = 0; 419126007Spjd for (no = 0; no < sc->sc_ndisks; no++) { 420126007Spjd disk = &sc->sc_disks[no]; 421260385Sscottl dp = disk->d_consumer->provider; 422126007Spjd disk->d_start = start; 423260385Sscottl disk->d_end = disk->d_start + dp->mediasize; 424126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 425260385Sscottl disk->d_end -= dp->sectorsize; 426126007Spjd start = disk->d_end; 427126773Spjd if (no == 0) 428260385Sscottl sectorsize = dp->sectorsize; 429260385Sscottl else 430260385Sscottl sectorsize = lcm(sectorsize, dp->sectorsize); 431260385Sscottl 432260385Sscottl /* A provider underneath us doesn't support unmapped */ 433260385Sscottl if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 434260385Sscottl G_CONCAT_DEBUG(1, "Cancelling unmapped " 435260385Sscottl "because of %s.", dp->name); 436260385Sscottl pp->flags &= ~G_PF_ACCEPT_UNMAPPED; 437126773Spjd } 438126007Spjd } 439200942Smav pp->sectorsize = sectorsize; 440126007Spjd /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 441200942Smav pp->mediasize = start; 442200942Smav pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; 443200942Smav pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; 444200942Smav sc->sc_provider = pp; 445200942Smav g_error_provider(pp, 0); 446126007Spjd 447227004Smav G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 448126007Spjd} 449126007Spjd 450126007Spjdstatic int 451126007Spjdg_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 452126007Spjd{ 453126007Spjd struct g_provider *pp; 454126007Spjd u_char *buf; 455126007Spjd int error; 456126007Spjd 457126007Spjd g_topology_assert(); 458126007Spjd 459126007Spjd error = g_access(cp, 1, 0, 0); 460126007Spjd if (error != 0) 461126007Spjd return (error); 462126007Spjd pp = cp->provider; 463126007Spjd g_topology_unlock(); 464126007Spjd buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 465126007Spjd &error); 466126007Spjd g_topology_lock(); 467126007Spjd g_access(cp, -1, 0, 0); 468126007Spjd if (buf == NULL) 469126007Spjd return (error); 470126007Spjd 471126007Spjd /* Decode metadata. */ 472126007Spjd concat_metadata_decode(buf, md); 473126007Spjd g_free(buf); 474126007Spjd 475126007Spjd return (0); 476126007Spjd} 477126007Spjd 478126007Spjd/* 479126007Spjd * Add disk to given device. 480126007Spjd */ 481126007Spjdstatic int 482126007Spjdg_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 483126007Spjd{ 484126007Spjd struct g_concat_disk *disk; 485129478Spjd struct g_consumer *cp, *fcp; 486126007Spjd struct g_geom *gp; 487126007Spjd int error; 488126007Spjd 489227004Smav g_topology_assert(); 490126007Spjd /* Metadata corrupted? */ 491126007Spjd if (no >= sc->sc_ndisks) 492126007Spjd return (EINVAL); 493126007Spjd 494126007Spjd disk = &sc->sc_disks[no]; 495126007Spjd /* Check if disk is not already attached. */ 496126565Spjd if (disk->d_consumer != NULL) 497126007Spjd return (EEXIST); 498126007Spjd 499129478Spjd gp = sc->sc_geom; 500129478Spjd fcp = LIST_FIRST(&gp->consumer); 501126007Spjd 502126007Spjd cp = g_new_consumer(gp); 503260385Sscottl cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 504126007Spjd error = g_attach(cp, pp); 505126007Spjd if (error != 0) { 506126007Spjd g_destroy_consumer(cp); 507126007Spjd return (error); 508126007Spjd } 509126007Spjd 510129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 511129478Spjd error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 512126007Spjd if (error != 0) { 513126007Spjd g_detach(cp); 514126007Spjd g_destroy_consumer(cp); 515126007Spjd return (error); 516126007Spjd } 517126007Spjd } 518126007Spjd if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 519126007Spjd struct g_concat_metadata md; 520126007Spjd 521126773Spjd /* Re-read metadata. */ 522126007Spjd error = g_concat_read_metadata(cp, &md); 523126007Spjd if (error != 0) 524126007Spjd goto fail; 525126007Spjd 526126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 527126007Spjd strcmp(md.md_name, sc->sc_name) != 0 || 528126007Spjd md.md_id != sc->sc_id) { 529126007Spjd G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 530126007Spjd goto fail; 531126007Spjd } 532126007Spjd } 533126007Spjd 534126007Spjd cp->private = disk; 535126007Spjd disk->d_consumer = cp; 536126007Spjd disk->d_softc = sc; 537126007Spjd disk->d_start = 0; /* not yet */ 538126007Spjd disk->d_end = 0; /* not yet */ 539227004Smav disk->d_removed = 0; 540126007Spjd 541132663Spjd G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 542126007Spjd 543126007Spjd g_concat_check_and_run(sc); 544126007Spjd 545126007Spjd return (0); 546126007Spjdfail: 547129478Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 548129478Spjd g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 549126007Spjd g_detach(cp); 550126007Spjd g_destroy_consumer(cp); 551126007Spjd return (error); 552126007Spjd} 553126007Spjd 554126007Spjdstatic struct g_geom * 555126007Spjdg_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 556126773Spjd u_int type) 557126007Spjd{ 558126007Spjd struct g_concat_softc *sc; 559126007Spjd struct g_geom *gp; 560126007Spjd u_int no; 561126007Spjd 562132663Spjd G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 563126007Spjd md->md_id); 564126007Spjd 565155071Spjd /* One disks is minimum. */ 566155071Spjd if (md->md_all < 1) 567126007Spjd return (NULL); 568126007Spjd 569126007Spjd /* Check for duplicate unit */ 570126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 571126007Spjd sc = gp->softc; 572126007Spjd if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 573129478Spjd G_CONCAT_DEBUG(0, "Device %s already configured.", 574126007Spjd gp->name); 575126007Spjd return (NULL); 576126007Spjd } 577126007Spjd } 578132663Spjd gp = g_new_geomf(mp, "%s", md->md_name); 579132661Spjd sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 580126007Spjd gp->start = g_concat_start; 581126007Spjd gp->spoiled = g_concat_orphan; 582126007Spjd gp->orphan = g_concat_orphan; 583126007Spjd gp->access = g_concat_access; 584126007Spjd gp->dumpconf = g_concat_dumpconf; 585126007Spjd 586126007Spjd sc->sc_id = md->md_id; 587126007Spjd sc->sc_ndisks = md->md_all; 588126007Spjd sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 589126007Spjd M_CONCAT, M_WAITOK | M_ZERO); 590126007Spjd for (no = 0; no < sc->sc_ndisks; no++) 591126565Spjd sc->sc_disks[no].d_consumer = NULL; 592126007Spjd sc->sc_type = type; 593260385Sscottl mtx_init(&sc->sc_lock, "gconcat lock", NULL, MTX_DEF); 594126007Spjd 595126007Spjd gp->softc = sc; 596129478Spjd sc->sc_geom = gp; 597129478Spjd sc->sc_provider = NULL; 598126007Spjd 599132663Spjd G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 600126007Spjd 601126007Spjd return (gp); 602126007Spjd} 603126007Spjd 604126007Spjdstatic int 605126007Spjdg_concat_destroy(struct g_concat_softc *sc, boolean_t force) 606126007Spjd{ 607126007Spjd struct g_provider *pp; 608227004Smav struct g_consumer *cp, *cp1; 609126007Spjd struct g_geom *gp; 610126007Spjd 611126007Spjd g_topology_assert(); 612126007Spjd 613126007Spjd if (sc == NULL) 614126007Spjd return (ENXIO); 615126007Spjd 616126007Spjd pp = sc->sc_provider; 617129478Spjd if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 618126007Spjd if (force) { 619126007Spjd G_CONCAT_DEBUG(0, "Device %s is still open, so it " 620129478Spjd "can't be definitely removed.", pp->name); 621126007Spjd } else { 622126007Spjd G_CONCAT_DEBUG(1, 623129478Spjd "Device %s is still open (r%dw%de%d).", pp->name, 624129478Spjd pp->acr, pp->acw, pp->ace); 625126007Spjd return (EBUSY); 626126007Spjd } 627126007Spjd } 628126007Spjd 629227004Smav gp = sc->sc_geom; 630227004Smav LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 631227004Smav g_concat_remove_disk(cp->private); 632227004Smav if (cp1 == NULL) 633227004Smav return (0); /* Recursion happened. */ 634126007Spjd } 635227004Smav if (!LIST_EMPTY(&gp->consumer)) 636227004Smav return (EINPROGRESS); 637126007Spjd 638126007Spjd gp->softc = NULL; 639129478Spjd KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 640129478Spjd gp->name)); 641126007Spjd free(sc->sc_disks, M_CONCAT); 642260385Sscottl mtx_destroy(&sc->sc_lock); 643126007Spjd free(sc, M_CONCAT); 644126007Spjd 645227004Smav G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 646126007Spjd g_wither_geom(gp, ENXIO); 647126007Spjd return (0); 648126007Spjd} 649126007Spjd 650126007Spjdstatic int 651126773Spjdg_concat_destroy_geom(struct gctl_req *req __unused, 652126773Spjd struct g_class *mp __unused, struct g_geom *gp) 653126007Spjd{ 654126007Spjd struct g_concat_softc *sc; 655126007Spjd 656126007Spjd sc = gp->softc; 657126007Spjd return (g_concat_destroy(sc, 0)); 658126007Spjd} 659126007Spjd 660126007Spjdstatic struct g_geom * 661126007Spjdg_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 662126007Spjd{ 663126007Spjd struct g_concat_metadata md; 664126007Spjd struct g_concat_softc *sc; 665126007Spjd struct g_consumer *cp; 666126007Spjd struct g_geom *gp; 667126007Spjd int error; 668126007Spjd 669129478Spjd g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 670126007Spjd g_topology_assert(); 671126007Spjd 672197898Spjd /* Skip providers that are already open for writing. */ 673197898Spjd if (pp->acw > 0) 674197898Spjd return (NULL); 675197898Spjd 676126007Spjd G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 677126007Spjd 678126007Spjd gp = g_new_geomf(mp, "concat:taste"); 679126007Spjd gp->start = g_concat_start; 680126315Spjd gp->access = g_concat_access; 681126315Spjd gp->orphan = g_concat_orphan; 682126007Spjd cp = g_new_consumer(gp); 683126007Spjd g_attach(cp, pp); 684126007Spjd error = g_concat_read_metadata(cp, &md); 685133371Spjd g_detach(cp); 686133371Spjd g_destroy_consumer(cp); 687133371Spjd g_destroy_geom(gp); 688129478Spjd if (error != 0) 689126007Spjd return (NULL); 690126007Spjd gp = NULL; 691126007Spjd 692126007Spjd if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 693126007Spjd return (NULL); 694126007Spjd if (md.md_version > G_CONCAT_VERSION) { 695126007Spjd printf("geom_concat.ko module is too old to handle %s.\n", 696126007Spjd pp->name); 697126007Spjd return (NULL); 698126007Spjd } 699133373Spjd /* 700133373Spjd * Backward compatibility: 701133373Spjd */ 702142727Spjd /* There was no md_provider field in earlier versions of metadata. */ 703133373Spjd if (md.md_version < 3) 704133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 705142727Spjd /* There was no md_provsize field in earlier versions of metadata. */ 706142727Spjd if (md.md_version < 4) 707142727Spjd md.md_provsize = pp->mediasize; 708126007Spjd 709221101Smav if (md.md_provider[0] != '\0' && 710221101Smav !g_compare_names(md.md_provider, pp->name)) 711133373Spjd return (NULL); 712142727Spjd if (md.md_provsize != pp->mediasize) 713142727Spjd return (NULL); 714133373Spjd 715126007Spjd /* 716126007Spjd * Let's check if device already exists. 717126007Spjd */ 718128913Spjd sc = NULL; 719126007Spjd LIST_FOREACH(gp, &mp->geom, geom) { 720126007Spjd sc = gp->softc; 721126007Spjd if (sc == NULL) 722126007Spjd continue; 723126007Spjd if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 724126007Spjd continue; 725126007Spjd if (strcmp(md.md_name, sc->sc_name) != 0) 726126007Spjd continue; 727126007Spjd if (md.md_id != sc->sc_id) 728126007Spjd continue; 729126007Spjd break; 730126007Spjd } 731126007Spjd if (gp != NULL) { 732126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 733126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 734126007Spjd if (error != 0) { 735129478Spjd G_CONCAT_DEBUG(0, 736129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 737129478Spjd gp->name, error); 738126007Spjd return (NULL); 739126007Spjd } 740126007Spjd } else { 741126773Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 742126007Spjd if (gp == NULL) { 743132663Spjd G_CONCAT_DEBUG(0, "Cannot create device %s.", 744126007Spjd md.md_name); 745126007Spjd return (NULL); 746126007Spjd } 747126007Spjd sc = gp->softc; 748126007Spjd G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 749126007Spjd error = g_concat_add_disk(sc, pp, md.md_no); 750126007Spjd if (error != 0) { 751129478Spjd G_CONCAT_DEBUG(0, 752129478Spjd "Cannot add disk %s to %s (error=%d).", pp->name, 753129478Spjd gp->name, error); 754126007Spjd g_concat_destroy(sc, 1); 755126007Spjd return (NULL); 756126007Spjd } 757126007Spjd } 758126007Spjd 759126007Spjd return (gp); 760126007Spjd} 761126007Spjd 762126007Spjdstatic void 763126007Spjdg_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 764126007Spjd{ 765126007Spjd u_int attached, no; 766129478Spjd struct g_concat_metadata md; 767126007Spjd struct g_provider *pp; 768126007Spjd struct g_concat_softc *sc; 769126007Spjd struct g_geom *gp; 770126007Spjd struct sbuf *sb; 771129478Spjd const char *name; 772129478Spjd char param[16]; 773129478Spjd int *nargs; 774126007Spjd 775126007Spjd g_topology_assert(); 776129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 777129478Spjd if (nargs == NULL) { 778129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 779126007Spjd return; 780126007Spjd } 781155071Spjd if (*nargs < 2) { 782129478Spjd gctl_error(req, "Too few arguments."); 783126007Spjd return; 784126007Spjd } 785126007Spjd 786129478Spjd strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 787129478Spjd md.md_version = G_CONCAT_VERSION; 788129478Spjd name = gctl_get_asciiparam(req, "arg0"); 789129478Spjd if (name == NULL) { 790129478Spjd gctl_error(req, "No 'arg%u' argument.", 0); 791129478Spjd return; 792129478Spjd } 793129478Spjd strlcpy(md.md_name, name, sizeof(md.md_name)); 794129478Spjd md.md_id = arc4random(); 795129478Spjd md.md_no = 0; 796129478Spjd md.md_all = *nargs - 1; 797133373Spjd bzero(md.md_provider, sizeof(md.md_provider)); 798142727Spjd /* This field is not important here. */ 799142727Spjd md.md_provsize = 0; 800129478Spjd 801126007Spjd /* Check all providers are valid */ 802129478Spjd for (no = 1; no < *nargs; no++) { 803129478Spjd snprintf(param, sizeof(param), "arg%u", no); 804129478Spjd name = gctl_get_asciiparam(req, param); 805129478Spjd if (name == NULL) { 806129478Spjd gctl_error(req, "No 'arg%u' argument.", no); 807129478Spjd return; 808129478Spjd } 809129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 810129478Spjd name += strlen("/dev/"); 811129478Spjd pp = g_provider_by_name(name); 812126007Spjd if (pp == NULL) { 813129478Spjd G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 814129478Spjd gctl_error(req, "Disk %s is invalid.", name); 815126007Spjd return; 816126007Spjd } 817126007Spjd } 818126007Spjd 819129478Spjd gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 820126007Spjd if (gp == NULL) { 821132663Spjd gctl_error(req, "Can't configure %s.", md.md_name); 822126007Spjd return; 823126007Spjd } 824126007Spjd 825126007Spjd sc = gp->softc; 826181463Sdes sb = sbuf_new_auto(); 827126007Spjd sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 828129478Spjd for (attached = 0, no = 1; no < *nargs; no++) { 829129478Spjd snprintf(param, sizeof(param), "arg%u", no); 830129478Spjd name = gctl_get_asciiparam(req, param); 831192021Strasz if (name == NULL) { 832192021Strasz gctl_error(req, "No 'arg%d' argument.", no); 833192021Strasz return; 834192021Strasz } 835129478Spjd if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 836129478Spjd name += strlen("/dev/"); 837129478Spjd pp = g_provider_by_name(name); 838129478Spjd KASSERT(pp != NULL, ("Provider %s disappear?!", name)); 839129478Spjd if (g_concat_add_disk(sc, pp, no - 1) != 0) { 840126007Spjd G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 841129478Spjd no, pp->name, gp->name); 842126007Spjd sbuf_printf(sb, " %s", pp->name); 843126007Spjd continue; 844126007Spjd } 845126007Spjd attached++; 846126007Spjd } 847126007Spjd sbuf_finish(sb); 848129478Spjd if (md.md_all != attached) { 849126007Spjd g_concat_destroy(gp->softc, 1); 850126007Spjd gctl_error(req, "%s", sbuf_data(sb)); 851126007Spjd } 852126007Spjd sbuf_delete(sb); 853126007Spjd} 854126007Spjd 855129478Spjdstatic struct g_concat_softc * 856129478Spjdg_concat_find_device(struct g_class *mp, const char *name) 857129478Spjd{ 858129478Spjd struct g_concat_softc *sc; 859129478Spjd struct g_geom *gp; 860129478Spjd 861129478Spjd LIST_FOREACH(gp, &mp->geom, geom) { 862129478Spjd sc = gp->softc; 863129478Spjd if (sc == NULL) 864129478Spjd continue; 865132663Spjd if (strcmp(sc->sc_name, name) == 0) 866129478Spjd return (sc); 867129478Spjd } 868129478Spjd return (NULL); 869129478Spjd} 870129478Spjd 871126007Spjdstatic void 872129478Spjdg_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 873126007Spjd{ 874126007Spjd struct g_concat_softc *sc; 875129478Spjd int *force, *nargs, error; 876129478Spjd const char *name; 877129478Spjd char param[16]; 878129478Spjd u_int i; 879126007Spjd 880126007Spjd g_topology_assert(); 881126773Spjd 882129478Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 883129478Spjd if (nargs == NULL) { 884129478Spjd gctl_error(req, "No '%s' argument.", "nargs"); 885129478Spjd return; 886129478Spjd } 887129478Spjd if (*nargs <= 0) { 888129478Spjd gctl_error(req, "Missing device(s)."); 889129478Spjd return; 890129478Spjd } 891126007Spjd force = gctl_get_paraml(req, "force", sizeof(*force)); 892126007Spjd if (force == NULL) { 893126773Spjd gctl_error(req, "No '%s' argument.", "force"); 894126007Spjd return; 895126007Spjd } 896129478Spjd 897129478Spjd for (i = 0; i < (u_int)*nargs; i++) { 898129478Spjd snprintf(param, sizeof(param), "arg%u", i); 899129478Spjd name = gctl_get_asciiparam(req, param); 900129478Spjd if (name == NULL) { 901129478Spjd gctl_error(req, "No 'arg%u' argument.", i); 902129478Spjd return; 903129478Spjd } 904129478Spjd sc = g_concat_find_device(mp, name); 905129478Spjd if (sc == NULL) { 906129478Spjd gctl_error(req, "No such device: %s.", name); 907129478Spjd return; 908129478Spjd } 909129478Spjd error = g_concat_destroy(sc, *force); 910129478Spjd if (error != 0) { 911129478Spjd gctl_error(req, "Cannot destroy device %s (error=%d).", 912132663Spjd sc->sc_name, error); 913129478Spjd return; 914129478Spjd } 915126007Spjd } 916126007Spjd} 917126007Spjd 918126007Spjdstatic void 919126007Spjdg_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 920126007Spjd{ 921129478Spjd uint32_t *version; 922126007Spjd 923126007Spjd g_topology_assert(); 924126007Spjd 925129478Spjd version = gctl_get_paraml(req, "version", sizeof(*version)); 926129478Spjd if (version == NULL) { 927129478Spjd gctl_error(req, "No '%s' argument.", "version"); 928126007Spjd return; 929126007Spjd } 930129478Spjd if (*version != G_CONCAT_VERSION) { 931129478Spjd gctl_error(req, "Userland and kernel parts are out of sync."); 932126007Spjd return; 933126007Spjd } 934129478Spjd 935129478Spjd if (strcmp(verb, "create") == 0) { 936129478Spjd g_concat_ctl_create(req, mp); 937126007Spjd return; 938131649Spjd } else if (strcmp(verb, "destroy") == 0 || 939131649Spjd strcmp(verb, "stop") == 0) { 940129478Spjd g_concat_ctl_destroy(req, mp); 941129478Spjd return; 942126007Spjd } 943126773Spjd gctl_error(req, "Unknown verb."); 944126007Spjd} 945126007Spjd 946126007Spjdstatic void 947126007Spjdg_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 948126007Spjd struct g_consumer *cp, struct g_provider *pp) 949126007Spjd{ 950128486Spjd struct g_concat_softc *sc; 951126007Spjd 952129747Spjd g_topology_assert(); 953128486Spjd sc = gp->softc; 954132665Spjd if (sc == NULL) 955128486Spjd return; 956132665Spjd if (pp != NULL) { 957132665Spjd /* Nothing here. */ 958132665Spjd } else if (cp != NULL) { 959132665Spjd struct g_concat_disk *disk; 960132665Spjd 961132665Spjd disk = cp->private; 962132665Spjd if (disk == NULL) 963132665Spjd return; 964132665Spjd sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 965132665Spjd (intmax_t)disk->d_end); 966132665Spjd sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 967132665Spjd (intmax_t)disk->d_start); 968132665Spjd } else { 969132665Spjd sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 970132665Spjd sbuf_printf(sb, "%s<Type>", indent); 971132665Spjd switch (sc->sc_type) { 972132665Spjd case G_CONCAT_TYPE_AUTOMATIC: 973132665Spjd sbuf_printf(sb, "AUTOMATIC"); 974132665Spjd break; 975132665Spjd case G_CONCAT_TYPE_MANUAL: 976132665Spjd sbuf_printf(sb, "MANUAL"); 977132665Spjd break; 978132665Spjd default: 979132665Spjd sbuf_printf(sb, "UNKNOWN"); 980132665Spjd break; 981132665Spjd } 982132665Spjd sbuf_printf(sb, "</Type>\n"); 983132665Spjd sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 984132665Spjd indent, sc->sc_ndisks, g_concat_nvalid(sc)); 985132665Spjd sbuf_printf(sb, "%s<State>", indent); 986132665Spjd if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 987132665Spjd sbuf_printf(sb, "UP"); 988132665Spjd else 989132665Spjd sbuf_printf(sb, "DOWN"); 990132665Spjd sbuf_printf(sb, "</State>\n"); 991128486Spjd } 992126007Spjd} 993126007Spjd 994126007SpjdDECLARE_GEOM_CLASS(g_concat_class, g_concat); 995