1140074Spjd/*- 2141993Spjd * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3140074Spjd * All rights reserved. 4140074Spjd * 5140074Spjd * Redistribution and use in source and binary forms, with or without 6140074Spjd * modification, are permitted provided that the following conditions 7140074Spjd * are met: 8140074Spjd * 1. Redistributions of source code must retain the above copyright 9140074Spjd * notice, this list of conditions and the following disclaimer. 10140074Spjd * 2. Redistributions in binary form must reproduce the above copyright 11140074Spjd * notice, this list of conditions and the following disclaimer in the 12140074Spjd * documentation and/or other materials provided with the distribution. 13155174Spjd * 14140074Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15140074Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16140074Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17140074Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18140074Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19140074Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20140074Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21140074Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22140074Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23140074Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24140074Spjd * SUCH DAMAGE. 25140074Spjd */ 26140074Spjd 27140074Spjd#include <sys/cdefs.h> 28140074Spjd__FBSDID("$FreeBSD$"); 29140074Spjd 30140074Spjd#include <sys/param.h> 31140074Spjd#include <sys/systm.h> 32140074Spjd#include <sys/kernel.h> 33140074Spjd#include <sys/module.h> 34140074Spjd#include <sys/lock.h> 35140074Spjd#include <sys/mutex.h> 36140074Spjd#include <sys/bio.h> 37223921Sae#include <sys/sbuf.h> 38140074Spjd#include <sys/sysctl.h> 39140074Spjd#include <sys/malloc.h> 40140074Spjd#include <vm/uma.h> 41140074Spjd#include <geom/geom.h> 42140074Spjd#include <geom/shsec/g_shsec.h> 43140074Spjd 44219029SnetchildFEATURE(geom_shsec, "GEOM shared secret device support"); 45140074Spjd 46151897Srwatsonstatic MALLOC_DEFINE(M_SHSEC, "shsec_data", "GEOM_SHSEC Data"); 47140074Spjd 48140074Spjdstatic uma_zone_t g_shsec_zone; 49140074Spjd 50140074Spjdstatic int g_shsec_destroy(struct g_shsec_softc *sc, boolean_t force); 51140074Spjdstatic int g_shsec_destroy_geom(struct gctl_req *req, struct g_class *mp, 52140074Spjd struct g_geom *gp); 53140074Spjd 54140074Spjdstatic g_taste_t g_shsec_taste; 55140074Spjdstatic g_ctl_req_t g_shsec_config; 56140074Spjdstatic g_dumpconf_t g_shsec_dumpconf; 57140074Spjdstatic g_init_t g_shsec_init; 58140074Spjdstatic g_fini_t g_shsec_fini; 59140074Spjd 60140074Spjdstruct g_class g_shsec_class = { 61140074Spjd .name = G_SHSEC_CLASS_NAME, 62140074Spjd .version = G_VERSION, 63140074Spjd .ctlreq = g_shsec_config, 64140074Spjd .taste = g_shsec_taste, 65140074Spjd .destroy_geom = g_shsec_destroy_geom, 66140074Spjd .init = g_shsec_init, 67140074Spjd .fini = g_shsec_fini 68140074Spjd}; 69140074Spjd 70140074SpjdSYSCTL_DECL(_kern_geom); 71227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, shsec, CTLFLAG_RW, 0, 72227309Sed "GEOM_SHSEC stuff"); 73140074Spjdstatic u_int g_shsec_debug = 0; 74140074SpjdTUNABLE_INT("kern.geom.shsec.debug", &g_shsec_debug); 75140074SpjdSYSCTL_UINT(_kern_geom_shsec, OID_AUTO, debug, CTLFLAG_RW, &g_shsec_debug, 0, 76140074Spjd "Debug level"); 77140074Spjdstatic u_int g_shsec_maxmem = MAXPHYS * 100; 78140074SpjdTUNABLE_INT("kern.geom.shsec.maxmem", &g_shsec_maxmem); 79140074SpjdSYSCTL_UINT(_kern_geom_shsec, OID_AUTO, maxmem, CTLFLAG_RD, &g_shsec_maxmem, 80140074Spjd 0, "Maximum memory that can be allocated for I/O (in bytes)"); 81140074Spjdstatic u_int g_shsec_alloc_failed = 0; 82140074SpjdSYSCTL_UINT(_kern_geom_shsec, OID_AUTO, alloc_failed, CTLFLAG_RD, 83140074Spjd &g_shsec_alloc_failed, 0, "How many times I/O allocation failed"); 84140074Spjd 85140074Spjd/* 86140074Spjd * Greatest Common Divisor. 87140074Spjd */ 88140074Spjdstatic u_int 89140074Spjdgcd(u_int a, u_int b) 90140074Spjd{ 91140074Spjd u_int c; 92140074Spjd 93140074Spjd while (b != 0) { 94140074Spjd c = a; 95140074Spjd a = b; 96140074Spjd b = (c % b); 97140074Spjd } 98140074Spjd return (a); 99140074Spjd} 100140074Spjd 101140074Spjd/* 102140074Spjd * Least Common Multiple. 103140074Spjd */ 104140074Spjdstatic u_int 105140074Spjdlcm(u_int a, u_int b) 106140074Spjd{ 107140074Spjd 108140074Spjd return ((a * b) / gcd(a, b)); 109140074Spjd} 110140074Spjd 111140074Spjdstatic void 112140074Spjdg_shsec_init(struct g_class *mp __unused) 113140074Spjd{ 114140074Spjd 115140074Spjd g_shsec_zone = uma_zcreate("g_shsec_zone", MAXPHYS, NULL, NULL, NULL, 116140074Spjd NULL, 0, 0); 117140074Spjd g_shsec_maxmem -= g_shsec_maxmem % MAXPHYS; 118140074Spjd uma_zone_set_max(g_shsec_zone, g_shsec_maxmem / MAXPHYS); 119140074Spjd} 120140074Spjd 121140074Spjdstatic void 122140074Spjdg_shsec_fini(struct g_class *mp __unused) 123140074Spjd{ 124140074Spjd 125140074Spjd uma_zdestroy(g_shsec_zone); 126140074Spjd} 127140074Spjd 128140074Spjd/* 129140074Spjd * Return the number of valid disks. 130140074Spjd */ 131140074Spjdstatic u_int 132140074Spjdg_shsec_nvalid(struct g_shsec_softc *sc) 133140074Spjd{ 134140074Spjd u_int i, no; 135140074Spjd 136140074Spjd no = 0; 137140074Spjd for (i = 0; i < sc->sc_ndisks; i++) { 138140074Spjd if (sc->sc_disks[i] != NULL) 139140074Spjd no++; 140140074Spjd } 141140074Spjd 142140074Spjd return (no); 143140074Spjd} 144140074Spjd 145140074Spjdstatic void 146140074Spjdg_shsec_remove_disk(struct g_consumer *cp) 147140074Spjd{ 148140074Spjd struct g_shsec_softc *sc; 149140074Spjd u_int no; 150140074Spjd 151140074Spjd KASSERT(cp != NULL, ("Non-valid disk in %s.", __func__)); 152140074Spjd sc = (struct g_shsec_softc *)cp->private; 153140074Spjd KASSERT(sc != NULL, ("NULL sc in %s.", __func__)); 154140074Spjd no = cp->index; 155140074Spjd 156140074Spjd G_SHSEC_DEBUG(0, "Disk %s removed from %s.", cp->provider->name, 157140074Spjd sc->sc_name); 158140074Spjd 159140074Spjd sc->sc_disks[no] = NULL; 160140074Spjd if (sc->sc_provider != NULL) { 161140074Spjd g_orphan_provider(sc->sc_provider, ENXIO); 162140074Spjd sc->sc_provider = NULL; 163140074Spjd G_SHSEC_DEBUG(0, "Device %s removed.", sc->sc_name); 164140074Spjd } 165140074Spjd 166140074Spjd if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 167140074Spjd g_access(cp, -cp->acr, -cp->acw, -cp->ace); 168140074Spjd g_detach(cp); 169140074Spjd g_destroy_consumer(cp); 170140074Spjd} 171140074Spjd 172140074Spjdstatic void 173140074Spjdg_shsec_orphan(struct g_consumer *cp) 174140074Spjd{ 175140074Spjd struct g_shsec_softc *sc; 176140074Spjd struct g_geom *gp; 177140074Spjd 178140074Spjd g_topology_assert(); 179140074Spjd gp = cp->geom; 180140074Spjd sc = gp->softc; 181140074Spjd if (sc == NULL) 182140074Spjd return; 183140074Spjd 184140074Spjd g_shsec_remove_disk(cp); 185140074Spjd /* If there are no valid disks anymore, remove device. */ 186140074Spjd if (g_shsec_nvalid(sc) == 0) 187140074Spjd g_shsec_destroy(sc, 1); 188140074Spjd} 189140074Spjd 190140074Spjdstatic int 191140074Spjdg_shsec_access(struct g_provider *pp, int dr, int dw, int de) 192140074Spjd{ 193140074Spjd struct g_consumer *cp1, *cp2; 194140074Spjd struct g_shsec_softc *sc; 195140074Spjd struct g_geom *gp; 196140074Spjd int error; 197140074Spjd 198140074Spjd gp = pp->geom; 199140074Spjd sc = gp->softc; 200140074Spjd 201140074Spjd if (sc == NULL) { 202140074Spjd /* 203140074Spjd * It looks like geom is being withered. 204140074Spjd * In that case we allow only negative requests. 205140074Spjd */ 206140074Spjd KASSERT(dr <= 0 && dw <= 0 && de <= 0, 207140074Spjd ("Positive access request (device=%s).", pp->name)); 208140074Spjd if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && 209140074Spjd (pp->ace + de) == 0) { 210140074Spjd G_SHSEC_DEBUG(0, "Device %s definitely destroyed.", 211140074Spjd gp->name); 212140074Spjd } 213140074Spjd return (0); 214140074Spjd } 215140074Spjd 216140074Spjd /* On first open, grab an extra "exclusive" bit */ 217140074Spjd if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 218140074Spjd de++; 219140074Spjd /* ... and let go of it on last close */ 220140074Spjd if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 221140074Spjd de--; 222140074Spjd 223140074Spjd error = ENXIO; 224140074Spjd LIST_FOREACH(cp1, &gp->consumer, consumer) { 225140074Spjd error = g_access(cp1, dr, dw, de); 226140074Spjd if (error == 0) 227140074Spjd continue; 228140074Spjd /* 229140074Spjd * If we fail here, backout all previous changes. 230140074Spjd */ 231140074Spjd LIST_FOREACH(cp2, &gp->consumer, consumer) { 232140074Spjd if (cp1 == cp2) 233140074Spjd return (error); 234140074Spjd g_access(cp2, -dr, -dw, -de); 235140074Spjd } 236140074Spjd /* NOTREACHED */ 237140074Spjd } 238140074Spjd 239140074Spjd return (error); 240140074Spjd} 241140074Spjd 242140074Spjdstatic void 243140074Spjdg_shsec_xor1(uint32_t *src, uint32_t *dst, ssize_t len) 244140074Spjd{ 245140074Spjd 246140074Spjd for (; len > 0; len -= sizeof(uint32_t), dst++) 247140074Spjd *dst = *dst ^ *src++; 248140074Spjd KASSERT(len == 0, ("len != 0 (len=%zd)", len)); 249140074Spjd} 250140074Spjd 251140074Spjdstatic void 252140074Spjdg_shsec_done(struct bio *bp) 253140074Spjd{ 254140074Spjd struct g_shsec_softc *sc; 255140074Spjd struct bio *pbp; 256140074Spjd 257140074Spjd pbp = bp->bio_parent; 258140074Spjd sc = pbp->bio_to->geom->softc; 259140074Spjd if (bp->bio_error == 0) 260140074Spjd G_SHSEC_LOGREQ(2, bp, "Request done."); 261140074Spjd else { 262140074Spjd G_SHSEC_LOGREQ(0, bp, "Request failed (error=%d).", 263140074Spjd bp->bio_error); 264140074Spjd if (pbp->bio_error == 0) 265140074Spjd pbp->bio_error = bp->bio_error; 266140074Spjd } 267140074Spjd if (pbp->bio_cmd == BIO_READ) { 268140074Spjd if ((pbp->bio_pflags & G_SHSEC_BFLAG_FIRST) != 0) { 269140074Spjd bcopy(bp->bio_data, pbp->bio_data, pbp->bio_length); 270140074Spjd pbp->bio_pflags = 0; 271140074Spjd } else { 272140074Spjd g_shsec_xor1((uint32_t *)bp->bio_data, 273140074Spjd (uint32_t *)pbp->bio_data, 274140074Spjd (ssize_t)pbp->bio_length); 275140074Spjd } 276140074Spjd } 277140074Spjd bzero(bp->bio_data, bp->bio_length); 278140074Spjd uma_zfree(g_shsec_zone, bp->bio_data); 279140074Spjd g_destroy_bio(bp); 280140074Spjd pbp->bio_inbed++; 281140074Spjd if (pbp->bio_children == pbp->bio_inbed) { 282140074Spjd pbp->bio_completed = pbp->bio_length; 283140074Spjd g_io_deliver(pbp, pbp->bio_error); 284140074Spjd } 285140074Spjd} 286140074Spjd 287140074Spjdstatic void 288140074Spjdg_shsec_xor2(uint32_t *rand, uint32_t *dst, ssize_t len) 289140074Spjd{ 290140074Spjd 291140074Spjd for (; len > 0; len -= sizeof(uint32_t), dst++) { 292140074Spjd *rand = arc4random(); 293140074Spjd *dst = *dst ^ *rand++; 294140074Spjd } 295140074Spjd KASSERT(len == 0, ("len != 0 (len=%zd)", len)); 296140074Spjd} 297140074Spjd 298140074Spjdstatic void 299140074Spjdg_shsec_start(struct bio *bp) 300140074Spjd{ 301140074Spjd TAILQ_HEAD(, bio) queue = TAILQ_HEAD_INITIALIZER(queue); 302140074Spjd struct g_shsec_softc *sc; 303140074Spjd struct bio *cbp; 304140074Spjd uint32_t *dst; 305140074Spjd ssize_t len; 306140074Spjd u_int no; 307140074Spjd int error; 308140074Spjd 309140074Spjd sc = bp->bio_to->geom->softc; 310140074Spjd /* 311140074Spjd * If sc == NULL, provider's error should be set and g_shsec_start() 312140074Spjd * should not be called at all. 313140074Spjd */ 314140074Spjd KASSERT(sc != NULL, 315140074Spjd ("Provider's error should be set (error=%d)(device=%s).", 316140074Spjd bp->bio_to->error, bp->bio_to->name)); 317140074Spjd 318140074Spjd G_SHSEC_LOGREQ(2, bp, "Request received."); 319140074Spjd 320140074Spjd switch (bp->bio_cmd) { 321140074Spjd case BIO_READ: 322140074Spjd case BIO_WRITE: 323163869Spjd case BIO_FLUSH: 324140074Spjd /* 325140074Spjd * Only those requests are supported. 326140074Spjd */ 327140074Spjd break; 328140074Spjd case BIO_DELETE: 329140074Spjd case BIO_GETATTR: 330140074Spjd /* To which provider it should be delivered? */ 331140074Spjd default: 332140074Spjd g_io_deliver(bp, EOPNOTSUPP); 333140074Spjd return; 334140074Spjd } 335140074Spjd 336140074Spjd /* 337140074Spjd * Allocate all bios first and calculate XOR. 338140074Spjd */ 339140074Spjd dst = NULL; 340140074Spjd len = bp->bio_length; 341140074Spjd if (bp->bio_cmd == BIO_READ) 342140074Spjd bp->bio_pflags = G_SHSEC_BFLAG_FIRST; 343140074Spjd for (no = 0; no < sc->sc_ndisks; no++) { 344140074Spjd cbp = g_clone_bio(bp); 345140074Spjd if (cbp == NULL) { 346140074Spjd error = ENOMEM; 347140074Spjd goto failure; 348140074Spjd } 349140074Spjd TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 350140074Spjd 351140074Spjd /* 352140074Spjd * Fill in the component buf structure. 353140074Spjd */ 354140074Spjd cbp->bio_done = g_shsec_done; 355140074Spjd cbp->bio_data = uma_zalloc(g_shsec_zone, M_NOWAIT); 356140074Spjd if (cbp->bio_data == NULL) { 357140074Spjd g_shsec_alloc_failed++; 358140074Spjd error = ENOMEM; 359140074Spjd goto failure; 360140074Spjd } 361140074Spjd cbp->bio_caller2 = sc->sc_disks[no]; 362140074Spjd if (bp->bio_cmd == BIO_WRITE) { 363140074Spjd if (no == 0) { 364140074Spjd dst = (uint32_t *)cbp->bio_data; 365140074Spjd bcopy(bp->bio_data, dst, len); 366140074Spjd } else { 367140074Spjd g_shsec_xor2((uint32_t *)cbp->bio_data, dst, 368140074Spjd len); 369140074Spjd } 370140074Spjd } 371140074Spjd } 372140074Spjd /* 373140074Spjd * Fire off all allocated requests! 374140074Spjd */ 375140074Spjd while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 376140074Spjd struct g_consumer *cp; 377140074Spjd 378140074Spjd TAILQ_REMOVE(&queue, cbp, bio_queue); 379140074Spjd cp = cbp->bio_caller2; 380140074Spjd cbp->bio_caller2 = NULL; 381140074Spjd cbp->bio_to = cp->provider; 382140074Spjd G_SHSEC_LOGREQ(2, cbp, "Sending request."); 383140074Spjd g_io_request(cbp, cp); 384140074Spjd } 385140074Spjd return; 386140074Spjdfailure: 387140074Spjd while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 388140074Spjd TAILQ_REMOVE(&queue, cbp, bio_queue); 389140074Spjd bp->bio_children--; 390140074Spjd if (cbp->bio_data != NULL) { 391140074Spjd bzero(cbp->bio_data, cbp->bio_length); 392140074Spjd uma_zfree(g_shsec_zone, cbp->bio_data); 393140074Spjd } 394140074Spjd g_destroy_bio(cbp); 395140074Spjd } 396140074Spjd if (bp->bio_error == 0) 397140074Spjd bp->bio_error = error; 398140074Spjd g_io_deliver(bp, bp->bio_error); 399140074Spjd} 400140074Spjd 401140074Spjdstatic void 402140074Spjdg_shsec_check_and_run(struct g_shsec_softc *sc) 403140074Spjd{ 404140074Spjd off_t mediasize, ms; 405140074Spjd u_int no, sectorsize = 0; 406140074Spjd 407140074Spjd if (g_shsec_nvalid(sc) != sc->sc_ndisks) 408140074Spjd return; 409140074Spjd 410140074Spjd sc->sc_provider = g_new_providerf(sc->sc_geom, "shsec/%s", sc->sc_name); 411140074Spjd /* 412140074Spjd * Find the smallest disk. 413140074Spjd */ 414140074Spjd mediasize = sc->sc_disks[0]->provider->mediasize; 415140074Spjd mediasize -= sc->sc_disks[0]->provider->sectorsize; 416140074Spjd sectorsize = sc->sc_disks[0]->provider->sectorsize; 417140074Spjd for (no = 1; no < sc->sc_ndisks; no++) { 418140074Spjd ms = sc->sc_disks[no]->provider->mediasize; 419140074Spjd ms -= sc->sc_disks[no]->provider->sectorsize; 420140074Spjd if (ms < mediasize) 421140074Spjd mediasize = ms; 422140074Spjd sectorsize = lcm(sectorsize, 423140074Spjd sc->sc_disks[no]->provider->sectorsize); 424140074Spjd } 425140074Spjd sc->sc_provider->sectorsize = sectorsize; 426140074Spjd sc->sc_provider->mediasize = mediasize; 427140074Spjd g_error_provider(sc->sc_provider, 0); 428140074Spjd 429140074Spjd G_SHSEC_DEBUG(0, "Device %s activated.", sc->sc_name); 430140074Spjd} 431140074Spjd 432140074Spjdstatic int 433140074Spjdg_shsec_read_metadata(struct g_consumer *cp, struct g_shsec_metadata *md) 434140074Spjd{ 435140074Spjd struct g_provider *pp; 436140074Spjd u_char *buf; 437140074Spjd int error; 438140074Spjd 439140074Spjd g_topology_assert(); 440140074Spjd 441140074Spjd error = g_access(cp, 1, 0, 0); 442140074Spjd if (error != 0) 443140074Spjd return (error); 444140074Spjd pp = cp->provider; 445140074Spjd g_topology_unlock(); 446140074Spjd buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 447140074Spjd &error); 448140074Spjd g_topology_lock(); 449140074Spjd g_access(cp, -1, 0, 0); 450140074Spjd if (buf == NULL) 451140074Spjd return (error); 452140074Spjd 453140074Spjd /* Decode metadata. */ 454140074Spjd shsec_metadata_decode(buf, md); 455140074Spjd g_free(buf); 456140074Spjd 457140074Spjd return (0); 458140074Spjd} 459140074Spjd 460140074Spjd/* 461140074Spjd * Add disk to given device. 462140074Spjd */ 463140074Spjdstatic int 464140074Spjdg_shsec_add_disk(struct g_shsec_softc *sc, struct g_provider *pp, u_int no) 465140074Spjd{ 466140074Spjd struct g_consumer *cp, *fcp; 467140074Spjd struct g_geom *gp; 468140074Spjd struct g_shsec_metadata md; 469140074Spjd int error; 470140074Spjd 471140074Spjd /* Metadata corrupted? */ 472140074Spjd if (no >= sc->sc_ndisks) 473140074Spjd return (EINVAL); 474140074Spjd 475140074Spjd /* Check if disk is not already attached. */ 476140074Spjd if (sc->sc_disks[no] != NULL) 477140074Spjd return (EEXIST); 478140074Spjd 479140074Spjd gp = sc->sc_geom; 480140074Spjd fcp = LIST_FIRST(&gp->consumer); 481140074Spjd 482140074Spjd cp = g_new_consumer(gp); 483140074Spjd error = g_attach(cp, pp); 484140074Spjd if (error != 0) { 485140074Spjd g_destroy_consumer(cp); 486140074Spjd return (error); 487140074Spjd } 488140074Spjd 489140074Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 490140074Spjd error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 491140074Spjd if (error != 0) { 492140074Spjd g_detach(cp); 493140074Spjd g_destroy_consumer(cp); 494140074Spjd return (error); 495140074Spjd } 496140074Spjd } 497140074Spjd 498140074Spjd /* Reread metadata. */ 499140074Spjd error = g_shsec_read_metadata(cp, &md); 500140074Spjd if (error != 0) 501140074Spjd goto fail; 502140074Spjd 503140074Spjd if (strcmp(md.md_magic, G_SHSEC_MAGIC) != 0 || 504140074Spjd strcmp(md.md_name, sc->sc_name) != 0 || md.md_id != sc->sc_id) { 505140074Spjd G_SHSEC_DEBUG(0, "Metadata on %s changed.", pp->name); 506140074Spjd goto fail; 507140074Spjd } 508140074Spjd 509140074Spjd cp->private = sc; 510140074Spjd cp->index = no; 511140074Spjd sc->sc_disks[no] = cp; 512140074Spjd 513140074Spjd G_SHSEC_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 514140074Spjd 515140074Spjd g_shsec_check_and_run(sc); 516140074Spjd 517140074Spjd return (0); 518140074Spjdfail: 519140074Spjd if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 520140074Spjd g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 521140074Spjd g_detach(cp); 522140074Spjd g_destroy_consumer(cp); 523140074Spjd return (error); 524140074Spjd} 525140074Spjd 526140074Spjdstatic struct g_geom * 527140074Spjdg_shsec_create(struct g_class *mp, const struct g_shsec_metadata *md) 528140074Spjd{ 529140074Spjd struct g_shsec_softc *sc; 530140074Spjd struct g_geom *gp; 531140074Spjd u_int no; 532140074Spjd 533140074Spjd G_SHSEC_DEBUG(1, "Creating device %s (id=%u).", md->md_name, md->md_id); 534140074Spjd 535140074Spjd /* Two disks is minimum. */ 536140074Spjd if (md->md_all < 2) { 537140074Spjd G_SHSEC_DEBUG(0, "Too few disks defined for %s.", md->md_name); 538140074Spjd return (NULL); 539140074Spjd } 540140074Spjd 541140074Spjd /* Check for duplicate unit */ 542140074Spjd LIST_FOREACH(gp, &mp->geom, geom) { 543140074Spjd sc = gp->softc; 544140074Spjd if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 545140074Spjd G_SHSEC_DEBUG(0, "Device %s already configured.", 546140074Spjd sc->sc_name); 547140074Spjd return (NULL); 548140074Spjd } 549140074Spjd } 550140074Spjd gp = g_new_geomf(mp, "%s", md->md_name); 551140074Spjd sc = malloc(sizeof(*sc), M_SHSEC, M_WAITOK | M_ZERO); 552140074Spjd gp->start = g_shsec_start; 553140074Spjd gp->spoiled = g_shsec_orphan; 554140074Spjd gp->orphan = g_shsec_orphan; 555140074Spjd gp->access = g_shsec_access; 556140074Spjd gp->dumpconf = g_shsec_dumpconf; 557140074Spjd 558140074Spjd sc->sc_id = md->md_id; 559140074Spjd sc->sc_ndisks = md->md_all; 560140074Spjd sc->sc_disks = malloc(sizeof(struct g_consumer *) * sc->sc_ndisks, 561140074Spjd M_SHSEC, M_WAITOK | M_ZERO); 562140074Spjd for (no = 0; no < sc->sc_ndisks; no++) 563140074Spjd sc->sc_disks[no] = NULL; 564140074Spjd 565140074Spjd gp->softc = sc; 566140074Spjd sc->sc_geom = gp; 567140074Spjd sc->sc_provider = NULL; 568140074Spjd 569140074Spjd G_SHSEC_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 570140074Spjd 571140074Spjd return (gp); 572140074Spjd} 573140074Spjd 574140074Spjdstatic int 575140074Spjdg_shsec_destroy(struct g_shsec_softc *sc, boolean_t force) 576140074Spjd{ 577140074Spjd struct g_provider *pp; 578140074Spjd struct g_geom *gp; 579140074Spjd u_int no; 580140074Spjd 581140074Spjd g_topology_assert(); 582140074Spjd 583140074Spjd if (sc == NULL) 584140074Spjd return (ENXIO); 585140074Spjd 586140074Spjd pp = sc->sc_provider; 587140074Spjd if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 588140074Spjd if (force) { 589140074Spjd G_SHSEC_DEBUG(0, "Device %s is still open, so it " 590140074Spjd "can't be definitely removed.", pp->name); 591140074Spjd } else { 592140074Spjd G_SHSEC_DEBUG(1, 593140074Spjd "Device %s is still open (r%dw%de%d).", pp->name, 594140074Spjd pp->acr, pp->acw, pp->ace); 595140074Spjd return (EBUSY); 596140074Spjd } 597140074Spjd } 598140074Spjd 599140074Spjd for (no = 0; no < sc->sc_ndisks; no++) { 600140074Spjd if (sc->sc_disks[no] != NULL) 601140074Spjd g_shsec_remove_disk(sc->sc_disks[no]); 602140074Spjd } 603140074Spjd 604140074Spjd gp = sc->sc_geom; 605140074Spjd gp->softc = NULL; 606140074Spjd KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 607140074Spjd gp->name)); 608140074Spjd free(sc->sc_disks, M_SHSEC); 609140074Spjd free(sc, M_SHSEC); 610140074Spjd 611155174Spjd pp = LIST_FIRST(&gp->provider); 612140074Spjd if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)) 613140074Spjd G_SHSEC_DEBUG(0, "Device %s destroyed.", gp->name); 614140074Spjd 615140074Spjd g_wither_geom(gp, ENXIO); 616140074Spjd 617140074Spjd return (0); 618140074Spjd} 619140074Spjd 620140074Spjdstatic int 621140074Spjdg_shsec_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused, 622140074Spjd struct g_geom *gp) 623140074Spjd{ 624140074Spjd struct g_shsec_softc *sc; 625140074Spjd 626140074Spjd sc = gp->softc; 627140074Spjd return (g_shsec_destroy(sc, 0)); 628140074Spjd} 629140074Spjd 630140074Spjdstatic struct g_geom * 631140074Spjdg_shsec_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 632140074Spjd{ 633140074Spjd struct g_shsec_metadata md; 634140074Spjd struct g_shsec_softc *sc; 635140074Spjd struct g_consumer *cp; 636140074Spjd struct g_geom *gp; 637140074Spjd int error; 638140074Spjd 639140074Spjd g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 640140074Spjd g_topology_assert(); 641140074Spjd 642197898Spjd /* Skip providers that are already open for writing. */ 643197898Spjd if (pp->acw > 0) 644197898Spjd return (NULL); 645197898Spjd 646140074Spjd G_SHSEC_DEBUG(3, "Tasting %s.", pp->name); 647140074Spjd 648140074Spjd gp = g_new_geomf(mp, "shsec:taste"); 649140074Spjd gp->start = g_shsec_start; 650140074Spjd gp->access = g_shsec_access; 651140074Spjd gp->orphan = g_shsec_orphan; 652140074Spjd cp = g_new_consumer(gp); 653140074Spjd g_attach(cp, pp); 654140074Spjd error = g_shsec_read_metadata(cp, &md); 655140074Spjd g_detach(cp); 656140074Spjd g_destroy_consumer(cp); 657140074Spjd g_destroy_geom(gp); 658140074Spjd if (error != 0) 659140074Spjd return (NULL); 660140074Spjd gp = NULL; 661140074Spjd 662140074Spjd if (strcmp(md.md_magic, G_SHSEC_MAGIC) != 0) 663140074Spjd return (NULL); 664140074Spjd if (md.md_version > G_SHSEC_VERSION) { 665140074Spjd G_SHSEC_DEBUG(0, "Kernel module is too old to handle %s.\n", 666140074Spjd pp->name); 667140074Spjd return (NULL); 668140074Spjd } 669142727Spjd /* 670142727Spjd * Backward compatibility: 671142727Spjd */ 672142727Spjd /* There was no md_provsize field in earlier versions of metadata. */ 673142727Spjd if (md.md_version < 1) 674142727Spjd md.md_provsize = pp->mediasize; 675142727Spjd 676221101Smav if (md.md_provider[0] != '\0' && 677221101Smav !g_compare_names(md.md_provider, pp->name)) 678140074Spjd return (NULL); 679142727Spjd if (md.md_provsize != pp->mediasize) 680142727Spjd return (NULL); 681140074Spjd 682140074Spjd /* 683140074Spjd * Let's check if device already exists. 684140074Spjd */ 685140074Spjd sc = NULL; 686140074Spjd LIST_FOREACH(gp, &mp->geom, geom) { 687140074Spjd sc = gp->softc; 688140074Spjd if (sc == NULL) 689140074Spjd continue; 690140074Spjd if (strcmp(md.md_name, sc->sc_name) != 0) 691140074Spjd continue; 692140074Spjd if (md.md_id != sc->sc_id) 693140074Spjd continue; 694140074Spjd break; 695140074Spjd } 696140074Spjd if (gp != NULL) { 697140074Spjd G_SHSEC_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 698140074Spjd error = g_shsec_add_disk(sc, pp, md.md_no); 699140074Spjd if (error != 0) { 700140074Spjd G_SHSEC_DEBUG(0, "Cannot add disk %s to %s (error=%d).", 701140074Spjd pp->name, gp->name, error); 702140074Spjd return (NULL); 703140074Spjd } 704140074Spjd } else { 705140074Spjd gp = g_shsec_create(mp, &md); 706140074Spjd if (gp == NULL) { 707140074Spjd G_SHSEC_DEBUG(0, "Cannot create device %s.", md.md_name); 708140074Spjd return (NULL); 709140074Spjd } 710140074Spjd sc = gp->softc; 711140074Spjd G_SHSEC_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 712140074Spjd error = g_shsec_add_disk(sc, pp, md.md_no); 713140074Spjd if (error != 0) { 714140074Spjd G_SHSEC_DEBUG(0, "Cannot add disk %s to %s (error=%d).", 715140074Spjd pp->name, gp->name, error); 716140074Spjd g_shsec_destroy(sc, 1); 717140074Spjd return (NULL); 718140074Spjd } 719140074Spjd } 720140074Spjd return (gp); 721140074Spjd} 722140074Spjd 723140074Spjdstatic struct g_shsec_softc * 724140074Spjdg_shsec_find_device(struct g_class *mp, const char *name) 725140074Spjd{ 726140074Spjd struct g_shsec_softc *sc; 727140074Spjd struct g_geom *gp; 728140074Spjd 729140074Spjd LIST_FOREACH(gp, &mp->geom, geom) { 730140074Spjd sc = gp->softc; 731140074Spjd if (sc == NULL) 732140074Spjd continue; 733140074Spjd if (strcmp(sc->sc_name, name) == 0) 734140074Spjd return (sc); 735140074Spjd } 736140074Spjd return (NULL); 737140074Spjd} 738140074Spjd 739140074Spjdstatic void 740140074Spjdg_shsec_ctl_destroy(struct gctl_req *req, struct g_class *mp) 741140074Spjd{ 742140074Spjd struct g_shsec_softc *sc; 743140074Spjd int *force, *nargs, error; 744140074Spjd const char *name; 745140074Spjd char param[16]; 746140074Spjd u_int i; 747140074Spjd 748140074Spjd g_topology_assert(); 749140074Spjd 750140074Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 751140074Spjd if (nargs == NULL) { 752140074Spjd gctl_error(req, "No '%s' argument.", "nargs"); 753140074Spjd return; 754140074Spjd } 755140074Spjd if (*nargs <= 0) { 756140074Spjd gctl_error(req, "Missing device(s)."); 757140074Spjd return; 758140074Spjd } 759140074Spjd force = gctl_get_paraml(req, "force", sizeof(*force)); 760140074Spjd if (force == NULL) { 761140074Spjd gctl_error(req, "No '%s' argument.", "force"); 762140074Spjd return; 763140074Spjd } 764140074Spjd 765140074Spjd for (i = 0; i < (u_int)*nargs; i++) { 766140074Spjd snprintf(param, sizeof(param), "arg%u", i); 767140074Spjd name = gctl_get_asciiparam(req, param); 768140074Spjd if (name == NULL) { 769140074Spjd gctl_error(req, "No 'arg%u' argument.", i); 770140074Spjd return; 771140074Spjd } 772140074Spjd sc = g_shsec_find_device(mp, name); 773140074Spjd if (sc == NULL) { 774140074Spjd gctl_error(req, "No such device: %s.", name); 775140074Spjd return; 776140074Spjd } 777140074Spjd error = g_shsec_destroy(sc, *force); 778140074Spjd if (error != 0) { 779140074Spjd gctl_error(req, "Cannot destroy device %s (error=%d).", 780140074Spjd sc->sc_name, error); 781140074Spjd return; 782140074Spjd } 783140074Spjd } 784140074Spjd} 785140074Spjd 786140074Spjdstatic void 787140074Spjdg_shsec_config(struct gctl_req *req, struct g_class *mp, const char *verb) 788140074Spjd{ 789140074Spjd uint32_t *version; 790140074Spjd 791140074Spjd g_topology_assert(); 792140074Spjd 793140074Spjd version = gctl_get_paraml(req, "version", sizeof(*version)); 794140074Spjd if (version == NULL) { 795140074Spjd gctl_error(req, "No '%s' argument.", "version"); 796140074Spjd return; 797140074Spjd } 798140074Spjd if (*version != G_SHSEC_VERSION) { 799140074Spjd gctl_error(req, "Userland and kernel parts are out of sync."); 800140074Spjd return; 801140074Spjd } 802140074Spjd 803140074Spjd if (strcmp(verb, "stop") == 0) { 804140074Spjd g_shsec_ctl_destroy(req, mp); 805140074Spjd return; 806140074Spjd } 807140074Spjd 808140074Spjd gctl_error(req, "Unknown verb."); 809140074Spjd} 810140074Spjd 811140074Spjdstatic void 812140074Spjdg_shsec_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 813140074Spjd struct g_consumer *cp, struct g_provider *pp) 814140074Spjd{ 815140074Spjd struct g_shsec_softc *sc; 816140074Spjd 817140074Spjd sc = gp->softc; 818140074Spjd if (sc == NULL) 819140074Spjd return; 820140074Spjd if (pp != NULL) { 821140074Spjd /* Nothing here. */ 822140074Spjd } else if (cp != NULL) { 823140074Spjd sbuf_printf(sb, "%s<Number>%u</Number>\n", indent, 824140074Spjd (u_int)cp->index); 825140074Spjd } else { 826140074Spjd sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 827140074Spjd sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 828140074Spjd indent, sc->sc_ndisks, g_shsec_nvalid(sc)); 829140074Spjd sbuf_printf(sb, "%s<State>", indent); 830140074Spjd if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 831140074Spjd sbuf_printf(sb, "UP"); 832140074Spjd else 833140074Spjd sbuf_printf(sb, "DOWN"); 834140074Spjd sbuf_printf(sb, "</State>\n"); 835140074Spjd } 836140074Spjd} 837140074Spjd 838140074SpjdDECLARE_GEOM_CLASS(g_shsec_class, g_shsec); 839