1b09121f9SPawel Jakub Dawidek /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 33728855aSPedro F. Giffuni * 4e6890985SPawel Jakub Dawidek * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5b09121f9SPawel Jakub Dawidek * All rights reserved. 6b09121f9SPawel Jakub Dawidek * 7b09121f9SPawel Jakub Dawidek * Redistribution and use in source and binary forms, with or without 8b09121f9SPawel Jakub Dawidek * modification, are permitted provided that the following conditions 9b09121f9SPawel Jakub Dawidek * are met: 10b09121f9SPawel Jakub Dawidek * 1. Redistributions of source code must retain the above copyright 11b09121f9SPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer. 12b09121f9SPawel Jakub Dawidek * 2. Redistributions in binary form must reproduce the above copyright 13b09121f9SPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer in the 14b09121f9SPawel Jakub Dawidek * documentation and/or other materials provided with the distribution. 15b09121f9SPawel Jakub Dawidek * 16b09121f9SPawel Jakub Dawidek * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17b09121f9SPawel Jakub Dawidek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18b09121f9SPawel Jakub Dawidek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19b09121f9SPawel Jakub Dawidek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20b09121f9SPawel Jakub Dawidek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21b09121f9SPawel Jakub Dawidek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22b09121f9SPawel Jakub Dawidek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23b09121f9SPawel Jakub Dawidek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24b09121f9SPawel Jakub Dawidek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25b09121f9SPawel Jakub Dawidek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26b09121f9SPawel Jakub Dawidek * SUCH DAMAGE. 27b09121f9SPawel Jakub Dawidek */ 28b09121f9SPawel Jakub Dawidek 29b09121f9SPawel Jakub Dawidek #include <sys/param.h> 30b09121f9SPawel Jakub Dawidek #include <sys/systm.h> 31b09121f9SPawel Jakub Dawidek #include <sys/kernel.h> 32b09121f9SPawel Jakub Dawidek #include <sys/module.h> 33b09121f9SPawel Jakub Dawidek #include <sys/lock.h> 34b09121f9SPawel Jakub Dawidek #include <sys/mutex.h> 35b09121f9SPawel Jakub Dawidek #include <sys/bio.h> 365d807a0eSAndrey V. Elsukov #include <sys/sbuf.h> 37b09121f9SPawel Jakub Dawidek #include <sys/sysctl.h> 38b09121f9SPawel Jakub Dawidek #include <sys/malloc.h> 394c55f05bSPawel Jakub Dawidek #include <vm/uma.h> 40b09121f9SPawel Jakub Dawidek #include <geom/geom.h> 41ac03832eSConrad Meyer #include <geom/geom_dbg.h> 42b09121f9SPawel Jakub Dawidek #include <geom/stripe/g_stripe.h> 43b09121f9SPawel Jakub Dawidek 44cb08c2ccSAlexander Leidinger FEATURE(geom_stripe, "GEOM striping support"); 45b09121f9SPawel Jakub Dawidek 465bb84bc8SRobert Watson static MALLOC_DEFINE(M_STRIPE, "stripe_data", "GEOM_STRIPE Data"); 47b09121f9SPawel Jakub Dawidek 484c55f05bSPawel Jakub Dawidek static uma_zone_t g_stripe_zone; 49b09121f9SPawel Jakub Dawidek 50b09121f9SPawel Jakub Dawidek static int g_stripe_destroy(struct g_stripe_softc *sc, boolean_t force); 51b09121f9SPawel Jakub Dawidek static int g_stripe_destroy_geom(struct gctl_req *req, struct g_class *mp, 52b09121f9SPawel Jakub Dawidek struct g_geom *gp); 53b09121f9SPawel Jakub Dawidek 54b09121f9SPawel Jakub Dawidek static g_taste_t g_stripe_taste; 55b09121f9SPawel Jakub Dawidek static g_ctl_req_t g_stripe_config; 56b09121f9SPawel Jakub Dawidek static g_dumpconf_t g_stripe_dumpconf; 574c55f05bSPawel Jakub Dawidek static g_init_t g_stripe_init; 584c55f05bSPawel Jakub Dawidek static g_fini_t g_stripe_fini; 59b09121f9SPawel Jakub Dawidek 60b09121f9SPawel Jakub Dawidek struct g_class g_stripe_class = { 61b09121f9SPawel Jakub Dawidek .name = G_STRIPE_CLASS_NAME, 625721c9c7SPoul-Henning Kamp .version = G_VERSION, 63b09121f9SPawel Jakub Dawidek .ctlreq = g_stripe_config, 64b09121f9SPawel Jakub Dawidek .taste = g_stripe_taste, 654c55f05bSPawel Jakub Dawidek .destroy_geom = g_stripe_destroy_geom, 664c55f05bSPawel Jakub Dawidek .init = g_stripe_init, 674c55f05bSPawel Jakub Dawidek .fini = g_stripe_fini 68b09121f9SPawel Jakub Dawidek }; 69b09121f9SPawel Jakub Dawidek 704c55f05bSPawel Jakub Dawidek SYSCTL_DECL(_kern_geom); 717029da5cSPawel Biernacki static SYSCTL_NODE(_kern_geom, OID_AUTO, stripe, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 726472ac3dSEd Schouten "GEOM_STRIPE stuff"); 734c55f05bSPawel Jakub Dawidek static u_int g_stripe_debug = 0; 74af3b2549SHans Petter Selasky SYSCTL_UINT(_kern_geom_stripe, OID_AUTO, debug, CTLFLAG_RWTUN, &g_stripe_debug, 0, 754c55f05bSPawel Jakub Dawidek "Debug level"); 7653ed4e0dSPawel Jakub Dawidek static int g_stripe_fast = 0; 77c2da9542SAlexander Motin SYSCTL_INT(_kern_geom_stripe, OID_AUTO, fast, 78c2da9542SAlexander Motin CTLFLAG_RWTUN, &g_stripe_fast, 0, 797029da5cSPawel Biernacki "Fast, but memory-consuming, mode"); 80cd853791SKonstantin Belousov static u_long g_stripe_maxmem; 81cd853791SKonstantin Belousov SYSCTL_ULONG(_kern_geom_stripe, OID_AUTO, maxmem, 82cd853791SKonstantin Belousov CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &g_stripe_maxmem, 0, 83cd853791SKonstantin Belousov "Maximum memory that can be allocated in \"fast\" mode (in bytes)"); 84cea36368SPawel Jakub Dawidek static u_int g_stripe_fast_failed = 0; 85cea36368SPawel Jakub Dawidek SYSCTL_UINT(_kern_geom_stripe, OID_AUTO, fast_failed, CTLFLAG_RD, 86cea36368SPawel Jakub Dawidek &g_stripe_fast_failed, 0, "How many times \"fast\" mode failed"); 87b09121f9SPawel Jakub Dawidek 88b09121f9SPawel Jakub Dawidek /* 89b09121f9SPawel Jakub Dawidek * Greatest Common Divisor. 90b09121f9SPawel Jakub Dawidek */ 91b09121f9SPawel Jakub Dawidek static u_int 92b09121f9SPawel Jakub Dawidek gcd(u_int a, u_int b) 93b09121f9SPawel Jakub Dawidek { 94b09121f9SPawel Jakub Dawidek u_int c; 95b09121f9SPawel Jakub Dawidek 96b09121f9SPawel Jakub Dawidek while (b != 0) { 97b09121f9SPawel Jakub Dawidek c = a; 98b09121f9SPawel Jakub Dawidek a = b; 99b09121f9SPawel Jakub Dawidek b = (c % b); 100b09121f9SPawel Jakub Dawidek } 101b09121f9SPawel Jakub Dawidek return (a); 102b09121f9SPawel Jakub Dawidek } 103b09121f9SPawel Jakub Dawidek 104b09121f9SPawel Jakub Dawidek /* 105b09121f9SPawel Jakub Dawidek * Least Common Multiple. 106b09121f9SPawel Jakub Dawidek */ 107b09121f9SPawel Jakub Dawidek static u_int 108b09121f9SPawel Jakub Dawidek lcm(u_int a, u_int b) 109b09121f9SPawel Jakub Dawidek { 110b09121f9SPawel Jakub Dawidek 111b09121f9SPawel Jakub Dawidek return ((a * b) / gcd(a, b)); 112b09121f9SPawel Jakub Dawidek } 113b09121f9SPawel Jakub Dawidek 1144c55f05bSPawel Jakub Dawidek static void 1154c55f05bSPawel Jakub Dawidek g_stripe_init(struct g_class *mp __unused) 1164c55f05bSPawel Jakub Dawidek { 1174c55f05bSPawel Jakub Dawidek 118cd853791SKonstantin Belousov g_stripe_maxmem = maxphys * 100; 119cd853791SKonstantin Belousov TUNABLE_ULONG_FETCH("kern.geom.stripe.maxmem,", &g_stripe_maxmem); 120cd853791SKonstantin Belousov g_stripe_zone = uma_zcreate("g_stripe_zone", maxphys, NULL, NULL, 1214c55f05bSPawel Jakub Dawidek NULL, NULL, 0, 0); 122cd853791SKonstantin Belousov g_stripe_maxmem -= g_stripe_maxmem % maxphys; 123cd853791SKonstantin Belousov uma_zone_set_max(g_stripe_zone, g_stripe_maxmem / maxphys); 1244c55f05bSPawel Jakub Dawidek } 1254c55f05bSPawel Jakub Dawidek 1264c55f05bSPawel Jakub Dawidek static void 1274c55f05bSPawel Jakub Dawidek g_stripe_fini(struct g_class *mp __unused) 1284c55f05bSPawel Jakub Dawidek { 1294c55f05bSPawel Jakub Dawidek 1304c55f05bSPawel Jakub Dawidek uma_zdestroy(g_stripe_zone); 1314c55f05bSPawel Jakub Dawidek } 1324c55f05bSPawel Jakub Dawidek 133b09121f9SPawel Jakub Dawidek /* 134b09121f9SPawel Jakub Dawidek * Return the number of valid disks. 135b09121f9SPawel Jakub Dawidek */ 136b09121f9SPawel Jakub Dawidek static u_int 137b09121f9SPawel Jakub Dawidek g_stripe_nvalid(struct g_stripe_softc *sc) 138b09121f9SPawel Jakub Dawidek { 139b09121f9SPawel Jakub Dawidek u_int i, no; 140b09121f9SPawel Jakub Dawidek 141b09121f9SPawel Jakub Dawidek no = 0; 142b09121f9SPawel Jakub Dawidek for (i = 0; i < sc->sc_ndisks; i++) { 143b09121f9SPawel Jakub Dawidek if (sc->sc_disks[i] != NULL) 144b09121f9SPawel Jakub Dawidek no++; 145b09121f9SPawel Jakub Dawidek } 146b09121f9SPawel Jakub Dawidek 147b09121f9SPawel Jakub Dawidek return (no); 148b09121f9SPawel Jakub Dawidek } 149b09121f9SPawel Jakub Dawidek 150b09121f9SPawel Jakub Dawidek static void 151b09121f9SPawel Jakub Dawidek g_stripe_remove_disk(struct g_consumer *cp) 152b09121f9SPawel Jakub Dawidek { 153b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 154b09121f9SPawel Jakub Dawidek 1550849a53fSAlexander Motin g_topology_assert(); 156b09121f9SPawel Jakub Dawidek KASSERT(cp != NULL, ("Non-valid disk in %s.", __func__)); 1570849a53fSAlexander Motin sc = (struct g_stripe_softc *)cp->geom->softc; 158b09121f9SPawel Jakub Dawidek KASSERT(sc != NULL, ("NULL sc in %s.", __func__)); 159b09121f9SPawel Jakub Dawidek 1600849a53fSAlexander Motin if (cp->private == NULL) { 1610849a53fSAlexander Motin G_STRIPE_DEBUG(0, "Disk %s removed from %s.", 1620849a53fSAlexander Motin cp->provider->name, sc->sc_name); 1630849a53fSAlexander Motin cp->private = (void *)(uintptr_t)-1; 1640849a53fSAlexander Motin } 165b09121f9SPawel Jakub Dawidek 166b09121f9SPawel Jakub Dawidek if (sc->sc_provider != NULL) { 1670849a53fSAlexander Motin G_STRIPE_DEBUG(0, "Device %s deactivated.", 1680849a53fSAlexander Motin sc->sc_provider->name); 1698b64f3caSAlexander Motin g_wither_provider(sc->sc_provider, ENXIO); 170b09121f9SPawel Jakub Dawidek sc->sc_provider = NULL; 171b09121f9SPawel Jakub Dawidek } 172b09121f9SPawel Jakub Dawidek 173b09121f9SPawel Jakub Dawidek if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 1740849a53fSAlexander Motin return; 1750849a53fSAlexander Motin sc->sc_disks[cp->index] = NULL; 1760849a53fSAlexander Motin cp->index = 0; 177b09121f9SPawel Jakub Dawidek g_detach(cp); 178b09121f9SPawel Jakub Dawidek g_destroy_consumer(cp); 1790849a53fSAlexander Motin /* If there are no valid disks anymore, remove device. */ 1800849a53fSAlexander Motin if (LIST_EMPTY(&sc->sc_geom->consumer)) 1810849a53fSAlexander Motin g_stripe_destroy(sc, 1); 182b09121f9SPawel Jakub Dawidek } 183b09121f9SPawel Jakub Dawidek 184b09121f9SPawel Jakub Dawidek static void 185b09121f9SPawel Jakub Dawidek g_stripe_orphan(struct g_consumer *cp) 186b09121f9SPawel Jakub Dawidek { 187b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 188b09121f9SPawel Jakub Dawidek struct g_geom *gp; 189b09121f9SPawel Jakub Dawidek 190b09121f9SPawel Jakub Dawidek g_topology_assert(); 191b09121f9SPawel Jakub Dawidek gp = cp->geom; 192b09121f9SPawel Jakub Dawidek sc = gp->softc; 193b09121f9SPawel Jakub Dawidek if (sc == NULL) 194b09121f9SPawel Jakub Dawidek return; 195b09121f9SPawel Jakub Dawidek 196b09121f9SPawel Jakub Dawidek g_stripe_remove_disk(cp); 197b09121f9SPawel Jakub Dawidek } 198b09121f9SPawel Jakub Dawidek 199b09121f9SPawel Jakub Dawidek static int 200b09121f9SPawel Jakub Dawidek g_stripe_access(struct g_provider *pp, int dr, int dw, int de) 201b09121f9SPawel Jakub Dawidek { 2020849a53fSAlexander Motin struct g_consumer *cp1, *cp2, *tmp; 203739a9c51SEdward Tomasz Napierala struct g_stripe_softc *sc __diagused; 204b09121f9SPawel Jakub Dawidek struct g_geom *gp; 205b09121f9SPawel Jakub Dawidek int error; 206b09121f9SPawel Jakub Dawidek 2070849a53fSAlexander Motin g_topology_assert(); 208b09121f9SPawel Jakub Dawidek gp = pp->geom; 209b09121f9SPawel Jakub Dawidek sc = gp->softc; 2100849a53fSAlexander Motin KASSERT(sc != NULL, ("NULL sc in %s.", __func__)); 211b09121f9SPawel Jakub Dawidek 212b09121f9SPawel Jakub Dawidek /* On first open, grab an extra "exclusive" bit */ 213b09121f9SPawel Jakub Dawidek if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 214b09121f9SPawel Jakub Dawidek de++; 215b09121f9SPawel Jakub Dawidek /* ... and let go of it on last close */ 216b09121f9SPawel Jakub Dawidek if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 217b09121f9SPawel Jakub Dawidek de--; 218b09121f9SPawel Jakub Dawidek 2190849a53fSAlexander Motin LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { 220b09121f9SPawel Jakub Dawidek error = g_access(cp1, dr, dw, de); 2210849a53fSAlexander Motin if (error != 0) 2220849a53fSAlexander Motin goto fail; 2230849a53fSAlexander Motin if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && 2240849a53fSAlexander Motin cp1->private != NULL) { 2250849a53fSAlexander Motin g_stripe_remove_disk(cp1); /* May destroy geom. */ 2260849a53fSAlexander Motin } 2270849a53fSAlexander Motin } 2280849a53fSAlexander Motin return (0); 2290849a53fSAlexander Motin 2300849a53fSAlexander Motin fail: 231b09121f9SPawel Jakub Dawidek LIST_FOREACH(cp2, &gp->consumer, consumer) { 232b09121f9SPawel Jakub Dawidek if (cp1 == cp2) 2330849a53fSAlexander Motin break; 234b09121f9SPawel Jakub Dawidek g_access(cp2, -dr, -dw, -de); 235b09121f9SPawel Jakub Dawidek } 236b09121f9SPawel Jakub Dawidek return (error); 237b09121f9SPawel Jakub Dawidek } 238b09121f9SPawel Jakub Dawidek 239b09121f9SPawel Jakub Dawidek static void 2404c55f05bSPawel Jakub Dawidek g_stripe_copy(struct g_stripe_softc *sc, char *src, char *dst, off_t offset, 2414c55f05bSPawel Jakub Dawidek off_t length, int mode) 2424c55f05bSPawel Jakub Dawidek { 2436d305ab0SEugene Grosbein off_t stripesize; 2444c55f05bSPawel Jakub Dawidek size_t len; 2454c55f05bSPawel Jakub Dawidek 2464c55f05bSPawel Jakub Dawidek stripesize = sc->sc_stripesize; 2474c55f05bSPawel Jakub Dawidek len = (size_t)(stripesize - (offset & (stripesize - 1))); 2484c55f05bSPawel Jakub Dawidek do { 2494c55f05bSPawel Jakub Dawidek bcopy(src, dst, len); 2504c55f05bSPawel Jakub Dawidek if (mode) { 2514c55f05bSPawel Jakub Dawidek dst += len + stripesize * (sc->sc_ndisks - 1); 2524c55f05bSPawel Jakub Dawidek src += len; 2534c55f05bSPawel Jakub Dawidek } else { 2544c55f05bSPawel Jakub Dawidek dst += len; 2554c55f05bSPawel Jakub Dawidek src += len + stripesize * (sc->sc_ndisks - 1); 2564c55f05bSPawel Jakub Dawidek } 2574c55f05bSPawel Jakub Dawidek length -= len; 2584c55f05bSPawel Jakub Dawidek KASSERT(length >= 0, 2596d305ab0SEugene Grosbein ("Length < 0 (stripesize=%ju, offset=%ju, length=%jd).", 2606d305ab0SEugene Grosbein (uintmax_t)stripesize, (uintmax_t)offset, (intmax_t)length)); 2614c55f05bSPawel Jakub Dawidek if (length > stripesize) 2624c55f05bSPawel Jakub Dawidek len = stripesize; 2634c55f05bSPawel Jakub Dawidek else 2644c55f05bSPawel Jakub Dawidek len = length; 2654c55f05bSPawel Jakub Dawidek } while (length > 0); 2664c55f05bSPawel Jakub Dawidek } 2674c55f05bSPawel Jakub Dawidek 2684c55f05bSPawel Jakub Dawidek static void 2694c55f05bSPawel Jakub Dawidek g_stripe_done(struct bio *bp) 2704c55f05bSPawel Jakub Dawidek { 2714c55f05bSPawel Jakub Dawidek struct g_stripe_softc *sc; 2724c55f05bSPawel Jakub Dawidek struct bio *pbp; 2734c55f05bSPawel Jakub Dawidek 2744c55f05bSPawel Jakub Dawidek pbp = bp->bio_parent; 2754c55f05bSPawel Jakub Dawidek sc = pbp->bio_to->geom->softc; 276ec704301SPawel Jakub Dawidek if (bp->bio_cmd == BIO_READ && bp->bio_caller1 != NULL) { 277ec704301SPawel Jakub Dawidek g_stripe_copy(sc, bp->bio_data, bp->bio_caller1, bp->bio_offset, 2784c55f05bSPawel Jakub Dawidek bp->bio_length, 1); 279ec704301SPawel Jakub Dawidek bp->bio_data = bp->bio_caller1; 280ec704301SPawel Jakub Dawidek bp->bio_caller1 = NULL; 2814c55f05bSPawel Jakub Dawidek } 28240ea77a0SAlexander Motin mtx_lock(&sc->sc_lock); 28340ea77a0SAlexander Motin if (pbp->bio_error == 0) 28440ea77a0SAlexander Motin pbp->bio_error = bp->bio_error; 28540ea77a0SAlexander Motin pbp->bio_completed += bp->bio_completed; 2864c55f05bSPawel Jakub Dawidek pbp->bio_inbed++; 2874c55f05bSPawel Jakub Dawidek if (pbp->bio_children == pbp->bio_inbed) { 28840ea77a0SAlexander Motin mtx_unlock(&sc->sc_lock); 289ec704301SPawel Jakub Dawidek if (pbp->bio_driver1 != NULL) 290ec704301SPawel Jakub Dawidek uma_zfree(g_stripe_zone, pbp->bio_driver1); 291fd99699dSKonstantin Belousov if (bp->bio_cmd == BIO_SPEEDUP) 292fd99699dSKonstantin Belousov pbp->bio_completed = pbp->bio_length; 2934c55f05bSPawel Jakub Dawidek g_io_deliver(pbp, pbp->bio_error); 29440ea77a0SAlexander Motin } else 29540ea77a0SAlexander Motin mtx_unlock(&sc->sc_lock); 29640ea77a0SAlexander Motin g_destroy_bio(bp); 2974c55f05bSPawel Jakub Dawidek } 2984c55f05bSPawel Jakub Dawidek 2994c55f05bSPawel Jakub Dawidek static int 3004c55f05bSPawel Jakub Dawidek g_stripe_start_fast(struct bio *bp, u_int no, off_t offset, off_t length) 3014c55f05bSPawel Jakub Dawidek { 3024c55f05bSPawel Jakub Dawidek TAILQ_HEAD(, bio) queue = TAILQ_HEAD_INITIALIZER(queue); 3034c55f05bSPawel Jakub Dawidek struct g_stripe_softc *sc; 3044c55f05bSPawel Jakub Dawidek char *addr, *data = NULL; 3054c55f05bSPawel Jakub Dawidek struct bio *cbp; 3066d305ab0SEugene Grosbein off_t stripesize; 3076d305ab0SEugene Grosbein u_int nparts = 0; 3084c55f05bSPawel Jakub Dawidek int error; 3094c55f05bSPawel Jakub Dawidek 3104c55f05bSPawel Jakub Dawidek sc = bp->bio_to->geom->softc; 3114c55f05bSPawel Jakub Dawidek 3124c55f05bSPawel Jakub Dawidek addr = bp->bio_data; 3134c55f05bSPawel Jakub Dawidek stripesize = sc->sc_stripesize; 3144c55f05bSPawel Jakub Dawidek 3154c55f05bSPawel Jakub Dawidek cbp = g_clone_bio(bp); 3164c55f05bSPawel Jakub Dawidek if (cbp == NULL) { 3174c55f05bSPawel Jakub Dawidek error = ENOMEM; 3184c55f05bSPawel Jakub Dawidek goto failure; 3194c55f05bSPawel Jakub Dawidek } 3204c55f05bSPawel Jakub Dawidek TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 3214c55f05bSPawel Jakub Dawidek nparts++; 3224c55f05bSPawel Jakub Dawidek /* 3234c55f05bSPawel Jakub Dawidek * Fill in the component buf structure. 3244c55f05bSPawel Jakub Dawidek */ 3254c55f05bSPawel Jakub Dawidek cbp->bio_done = g_stripe_done; 3264c55f05bSPawel Jakub Dawidek cbp->bio_offset = offset; 3274c55f05bSPawel Jakub Dawidek cbp->bio_data = addr; 328ec704301SPawel Jakub Dawidek cbp->bio_caller1 = NULL; 3294c55f05bSPawel Jakub Dawidek cbp->bio_length = length; 330ec704301SPawel Jakub Dawidek cbp->bio_caller2 = sc->sc_disks[no]; 3314c55f05bSPawel Jakub Dawidek 3324c55f05bSPawel Jakub Dawidek /* offset -= offset % stripesize; */ 3334c55f05bSPawel Jakub Dawidek offset -= offset & (stripesize - 1); 3344c55f05bSPawel Jakub Dawidek addr += length; 3354c55f05bSPawel Jakub Dawidek length = bp->bio_length - length; 3364c55f05bSPawel Jakub Dawidek for (no++; length > 0; no++, length -= stripesize, addr += stripesize) { 3374c55f05bSPawel Jakub Dawidek if (no > sc->sc_ndisks - 1) { 3384c55f05bSPawel Jakub Dawidek no = 0; 3394c55f05bSPawel Jakub Dawidek offset += stripesize; 3404c55f05bSPawel Jakub Dawidek } 3414c55f05bSPawel Jakub Dawidek if (nparts >= sc->sc_ndisks) { 3424c55f05bSPawel Jakub Dawidek cbp = TAILQ_NEXT(cbp, bio_queue); 3434c55f05bSPawel Jakub Dawidek if (cbp == NULL) 3444c55f05bSPawel Jakub Dawidek cbp = TAILQ_FIRST(&queue); 3454c55f05bSPawel Jakub Dawidek nparts++; 3464c55f05bSPawel Jakub Dawidek /* 3474c55f05bSPawel Jakub Dawidek * Update bio structure. 3484c55f05bSPawel Jakub Dawidek */ 3494c55f05bSPawel Jakub Dawidek /* 3504c55f05bSPawel Jakub Dawidek * MIN() is in case when 3514c55f05bSPawel Jakub Dawidek * (bp->bio_length % sc->sc_stripesize) != 0. 3524c55f05bSPawel Jakub Dawidek */ 3534c55f05bSPawel Jakub Dawidek cbp->bio_length += MIN(stripesize, length); 354ec704301SPawel Jakub Dawidek if (cbp->bio_caller1 == NULL) { 355ec704301SPawel Jakub Dawidek cbp->bio_caller1 = cbp->bio_data; 3564c55f05bSPawel Jakub Dawidek cbp->bio_data = NULL; 3574c55f05bSPawel Jakub Dawidek if (data == NULL) { 3584c55f05bSPawel Jakub Dawidek data = uma_zalloc(g_stripe_zone, 3594c55f05bSPawel Jakub Dawidek M_NOWAIT); 3604c55f05bSPawel Jakub Dawidek if (data == NULL) { 3614c55f05bSPawel Jakub Dawidek error = ENOMEM; 3624c55f05bSPawel Jakub Dawidek goto failure; 3634c55f05bSPawel Jakub Dawidek } 3644c55f05bSPawel Jakub Dawidek } 3654c55f05bSPawel Jakub Dawidek } 3664c55f05bSPawel Jakub Dawidek } else { 3674c55f05bSPawel Jakub Dawidek cbp = g_clone_bio(bp); 3684c55f05bSPawel Jakub Dawidek if (cbp == NULL) { 3694c55f05bSPawel Jakub Dawidek error = ENOMEM; 3704c55f05bSPawel Jakub Dawidek goto failure; 3714c55f05bSPawel Jakub Dawidek } 3724c55f05bSPawel Jakub Dawidek TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 3734c55f05bSPawel Jakub Dawidek nparts++; 3744c55f05bSPawel Jakub Dawidek /* 3754c55f05bSPawel Jakub Dawidek * Fill in the component buf structure. 3764c55f05bSPawel Jakub Dawidek */ 3774c55f05bSPawel Jakub Dawidek cbp->bio_done = g_stripe_done; 3784c55f05bSPawel Jakub Dawidek cbp->bio_offset = offset; 3794c55f05bSPawel Jakub Dawidek cbp->bio_data = addr; 380ec704301SPawel Jakub Dawidek cbp->bio_caller1 = NULL; 3814c55f05bSPawel Jakub Dawidek /* 3824c55f05bSPawel Jakub Dawidek * MIN() is in case when 3834c55f05bSPawel Jakub Dawidek * (bp->bio_length % sc->sc_stripesize) != 0. 3844c55f05bSPawel Jakub Dawidek */ 3854c55f05bSPawel Jakub Dawidek cbp->bio_length = MIN(stripesize, length); 386ec704301SPawel Jakub Dawidek cbp->bio_caller2 = sc->sc_disks[no]; 3874c55f05bSPawel Jakub Dawidek } 3884c55f05bSPawel Jakub Dawidek } 3894c55f05bSPawel Jakub Dawidek if (data != NULL) 3904ffa3fefSPawel Jakub Dawidek bp->bio_driver1 = data; 3914c55f05bSPawel Jakub Dawidek /* 3924c55f05bSPawel Jakub Dawidek * Fire off all allocated requests! 3934c55f05bSPawel Jakub Dawidek */ 3944c55f05bSPawel Jakub Dawidek while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 3954c55f05bSPawel Jakub Dawidek struct g_consumer *cp; 3964c55f05bSPawel Jakub Dawidek 3974c55f05bSPawel Jakub Dawidek TAILQ_REMOVE(&queue, cbp, bio_queue); 398ec704301SPawel Jakub Dawidek cp = cbp->bio_caller2; 399ec704301SPawel Jakub Dawidek cbp->bio_caller2 = NULL; 4004c55f05bSPawel Jakub Dawidek cbp->bio_to = cp->provider; 401ec704301SPawel Jakub Dawidek if (cbp->bio_caller1 != NULL) { 4024c55f05bSPawel Jakub Dawidek cbp->bio_data = data; 4034c55f05bSPawel Jakub Dawidek if (bp->bio_cmd == BIO_WRITE) { 404ec704301SPawel Jakub Dawidek g_stripe_copy(sc, cbp->bio_caller1, data, 4054c55f05bSPawel Jakub Dawidek cbp->bio_offset, cbp->bio_length, 0); 4064c55f05bSPawel Jakub Dawidek } 4074c55f05bSPawel Jakub Dawidek data += cbp->bio_length; 4084c55f05bSPawel Jakub Dawidek } 4094c55f05bSPawel Jakub Dawidek G_STRIPE_LOGREQ(cbp, "Sending request."); 4104c55f05bSPawel Jakub Dawidek g_io_request(cbp, cp); 4114c55f05bSPawel Jakub Dawidek } 4124c55f05bSPawel Jakub Dawidek return (0); 4134c55f05bSPawel Jakub Dawidek failure: 4144c55f05bSPawel Jakub Dawidek if (data != NULL) 4154c55f05bSPawel Jakub Dawidek uma_zfree(g_stripe_zone, data); 4164c55f05bSPawel Jakub Dawidek while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 4174c55f05bSPawel Jakub Dawidek TAILQ_REMOVE(&queue, cbp, bio_queue); 418ec704301SPawel Jakub Dawidek if (cbp->bio_caller1 != NULL) { 419ec704301SPawel Jakub Dawidek cbp->bio_data = cbp->bio_caller1; 420ec704301SPawel Jakub Dawidek cbp->bio_caller1 = NULL; 4214c55f05bSPawel Jakub Dawidek } 42237abacd4SPawel Jakub Dawidek bp->bio_children--; 4234c55f05bSPawel Jakub Dawidek g_destroy_bio(cbp); 4244c55f05bSPawel Jakub Dawidek } 4254c55f05bSPawel Jakub Dawidek return (error); 4264c55f05bSPawel Jakub Dawidek } 4274c55f05bSPawel Jakub Dawidek 4284c55f05bSPawel Jakub Dawidek static int 4294c55f05bSPawel Jakub Dawidek g_stripe_start_economic(struct bio *bp, u_int no, off_t offset, off_t length) 4304c55f05bSPawel Jakub Dawidek { 4314c55f05bSPawel Jakub Dawidek TAILQ_HEAD(, bio) queue = TAILQ_HEAD_INITIALIZER(queue); 4324c55f05bSPawel Jakub Dawidek struct g_stripe_softc *sc; 4336d305ab0SEugene Grosbein off_t stripesize; 4344c55f05bSPawel Jakub Dawidek struct bio *cbp; 4354c55f05bSPawel Jakub Dawidek char *addr; 4364c55f05bSPawel Jakub Dawidek int error; 4374c55f05bSPawel Jakub Dawidek 4384c55f05bSPawel Jakub Dawidek sc = bp->bio_to->geom->softc; 4394c55f05bSPawel Jakub Dawidek 4404c55f05bSPawel Jakub Dawidek stripesize = sc->sc_stripesize; 4414c55f05bSPawel Jakub Dawidek 4424c55f05bSPawel Jakub Dawidek cbp = g_clone_bio(bp); 4434c55f05bSPawel Jakub Dawidek if (cbp == NULL) { 4444c55f05bSPawel Jakub Dawidek error = ENOMEM; 4454c55f05bSPawel Jakub Dawidek goto failure; 4464c55f05bSPawel Jakub Dawidek } 4474c55f05bSPawel Jakub Dawidek TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 4484c55f05bSPawel Jakub Dawidek /* 4494c55f05bSPawel Jakub Dawidek * Fill in the component buf structure. 4504c55f05bSPawel Jakub Dawidek */ 45140ea77a0SAlexander Motin if (bp->bio_length == length) 45240ea77a0SAlexander Motin cbp->bio_done = g_std_done; /* Optimized lockless case. */ 45340ea77a0SAlexander Motin else 45440ea77a0SAlexander Motin cbp->bio_done = g_stripe_done; 4554c55f05bSPawel Jakub Dawidek cbp->bio_offset = offset; 4564c55f05bSPawel Jakub Dawidek cbp->bio_length = length; 45740ea77a0SAlexander Motin if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 45840ea77a0SAlexander Motin bp->bio_ma_n = round_page(bp->bio_ma_offset + 45940ea77a0SAlexander Motin bp->bio_length) / PAGE_SIZE; 46040ea77a0SAlexander Motin addr = NULL; 46140ea77a0SAlexander Motin } else 46240ea77a0SAlexander Motin addr = bp->bio_data; 463ec704301SPawel Jakub Dawidek cbp->bio_caller2 = sc->sc_disks[no]; 4644c55f05bSPawel Jakub Dawidek 4654c55f05bSPawel Jakub Dawidek /* offset -= offset % stripesize; */ 4664c55f05bSPawel Jakub Dawidek offset -= offset & (stripesize - 1); 46766b92c07SAlexander Motin if (bp->bio_cmd != BIO_DELETE) 4684c55f05bSPawel Jakub Dawidek addr += length; 4694c55f05bSPawel Jakub Dawidek length = bp->bio_length - length; 47066b92c07SAlexander Motin for (no++; length > 0; no++, length -= stripesize) { 4714c55f05bSPawel Jakub Dawidek if (no > sc->sc_ndisks - 1) { 4724c55f05bSPawel Jakub Dawidek no = 0; 4734c55f05bSPawel Jakub Dawidek offset += stripesize; 4744c55f05bSPawel Jakub Dawidek } 4754c55f05bSPawel Jakub Dawidek cbp = g_clone_bio(bp); 4764c55f05bSPawel Jakub Dawidek if (cbp == NULL) { 4774c55f05bSPawel Jakub Dawidek error = ENOMEM; 4784c55f05bSPawel Jakub Dawidek goto failure; 4794c55f05bSPawel Jakub Dawidek } 4804c55f05bSPawel Jakub Dawidek TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 4814c55f05bSPawel Jakub Dawidek 4824c55f05bSPawel Jakub Dawidek /* 4834c55f05bSPawel Jakub Dawidek * Fill in the component buf structure. 4844c55f05bSPawel Jakub Dawidek */ 48540ea77a0SAlexander Motin cbp->bio_done = g_stripe_done; 4864c55f05bSPawel Jakub Dawidek cbp->bio_offset = offset; 4874c55f05bSPawel Jakub Dawidek /* 4884c55f05bSPawel Jakub Dawidek * MIN() is in case when 4894c55f05bSPawel Jakub Dawidek * (bp->bio_length % sc->sc_stripesize) != 0. 4904c55f05bSPawel Jakub Dawidek */ 4914c55f05bSPawel Jakub Dawidek cbp->bio_length = MIN(stripesize, length); 49240ea77a0SAlexander Motin if ((bp->bio_flags & BIO_UNMAPPED) != 0) { 49340ea77a0SAlexander Motin cbp->bio_ma_offset += (uintptr_t)addr; 49440ea77a0SAlexander Motin cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; 49540ea77a0SAlexander Motin cbp->bio_ma_offset %= PAGE_SIZE; 49640ea77a0SAlexander Motin cbp->bio_ma_n = round_page(cbp->bio_ma_offset + 49740ea77a0SAlexander Motin cbp->bio_length) / PAGE_SIZE; 49840ea77a0SAlexander Motin } else 49940ea77a0SAlexander Motin cbp->bio_data = addr; 5004c55f05bSPawel Jakub Dawidek 501ec704301SPawel Jakub Dawidek cbp->bio_caller2 = sc->sc_disks[no]; 50266b92c07SAlexander Motin 50366b92c07SAlexander Motin if (bp->bio_cmd != BIO_DELETE) 50466b92c07SAlexander Motin addr += stripesize; 5054c55f05bSPawel Jakub Dawidek } 5064c55f05bSPawel Jakub Dawidek /* 5074c55f05bSPawel Jakub Dawidek * Fire off all allocated requests! 5084c55f05bSPawel Jakub Dawidek */ 5094c55f05bSPawel Jakub Dawidek while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 5104c55f05bSPawel Jakub Dawidek struct g_consumer *cp; 5114c55f05bSPawel Jakub Dawidek 5124c55f05bSPawel Jakub Dawidek TAILQ_REMOVE(&queue, cbp, bio_queue); 513ec704301SPawel Jakub Dawidek cp = cbp->bio_caller2; 514ec704301SPawel Jakub Dawidek cbp->bio_caller2 = NULL; 5154c55f05bSPawel Jakub Dawidek cbp->bio_to = cp->provider; 5164c55f05bSPawel Jakub Dawidek G_STRIPE_LOGREQ(cbp, "Sending request."); 5174c55f05bSPawel Jakub Dawidek g_io_request(cbp, cp); 5184c55f05bSPawel Jakub Dawidek } 5194c55f05bSPawel Jakub Dawidek return (0); 5204c55f05bSPawel Jakub Dawidek failure: 5214c55f05bSPawel Jakub Dawidek while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 5224c55f05bSPawel Jakub Dawidek TAILQ_REMOVE(&queue, cbp, bio_queue); 52337abacd4SPawel Jakub Dawidek bp->bio_children--; 5244c55f05bSPawel Jakub Dawidek g_destroy_bio(cbp); 5254c55f05bSPawel Jakub Dawidek } 5264c55f05bSPawel Jakub Dawidek return (error); 5274c55f05bSPawel Jakub Dawidek } 5284c55f05bSPawel Jakub Dawidek 5294c55f05bSPawel Jakub Dawidek static void 5308b522bdaSWarner Losh g_stripe_pushdown(struct g_stripe_softc *sc, struct bio *bp) 53142461fbaSPawel Jakub Dawidek { 53242461fbaSPawel Jakub Dawidek struct bio_queue_head queue; 53342461fbaSPawel Jakub Dawidek struct g_consumer *cp; 53442461fbaSPawel Jakub Dawidek struct bio *cbp; 53542461fbaSPawel Jakub Dawidek u_int no; 53642461fbaSPawel Jakub Dawidek 53742461fbaSPawel Jakub Dawidek bioq_init(&queue); 53842461fbaSPawel Jakub Dawidek for (no = 0; no < sc->sc_ndisks; no++) { 53942461fbaSPawel Jakub Dawidek cbp = g_clone_bio(bp); 54042461fbaSPawel Jakub Dawidek if (cbp == NULL) { 54142461fbaSPawel Jakub Dawidek for (cbp = bioq_first(&queue); cbp != NULL; 54242461fbaSPawel Jakub Dawidek cbp = bioq_first(&queue)) { 54342461fbaSPawel Jakub Dawidek bioq_remove(&queue, cbp); 54442461fbaSPawel Jakub Dawidek g_destroy_bio(cbp); 54542461fbaSPawel Jakub Dawidek } 54642461fbaSPawel Jakub Dawidek if (bp->bio_error == 0) 54742461fbaSPawel Jakub Dawidek bp->bio_error = ENOMEM; 54842461fbaSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 54942461fbaSPawel Jakub Dawidek return; 55042461fbaSPawel Jakub Dawidek } 55142461fbaSPawel Jakub Dawidek bioq_insert_tail(&queue, cbp); 55240ea77a0SAlexander Motin cbp->bio_done = g_stripe_done; 55340ea77a0SAlexander Motin cbp->bio_caller2 = sc->sc_disks[no]; 55442461fbaSPawel Jakub Dawidek cbp->bio_to = sc->sc_disks[no]->provider; 55542461fbaSPawel Jakub Dawidek } 55642461fbaSPawel Jakub Dawidek for (cbp = bioq_first(&queue); cbp != NULL; cbp = bioq_first(&queue)) { 55742461fbaSPawel Jakub Dawidek bioq_remove(&queue, cbp); 55842461fbaSPawel Jakub Dawidek G_STRIPE_LOGREQ(cbp, "Sending request."); 55940ea77a0SAlexander Motin cp = cbp->bio_caller2; 56040ea77a0SAlexander Motin cbp->bio_caller2 = NULL; 56142461fbaSPawel Jakub Dawidek g_io_request(cbp, cp); 56242461fbaSPawel Jakub Dawidek } 56342461fbaSPawel Jakub Dawidek } 56442461fbaSPawel Jakub Dawidek 56542461fbaSPawel Jakub Dawidek static void 566b09121f9SPawel Jakub Dawidek g_stripe_start(struct bio *bp) 567b09121f9SPawel Jakub Dawidek { 5686d305ab0SEugene Grosbein off_t offset, start, length, nstripe, stripesize; 569b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 5706d305ab0SEugene Grosbein u_int no; 5714c55f05bSPawel Jakub Dawidek int error, fast = 0; 572b09121f9SPawel Jakub Dawidek 5734c55f05bSPawel Jakub Dawidek sc = bp->bio_to->geom->softc; 574b09121f9SPawel Jakub Dawidek /* 575b09121f9SPawel Jakub Dawidek * If sc == NULL, provider's error should be set and g_stripe_start() 576b09121f9SPawel Jakub Dawidek * should not be called at all. 577b09121f9SPawel Jakub Dawidek */ 578b09121f9SPawel Jakub Dawidek KASSERT(sc != NULL, 579b09121f9SPawel Jakub Dawidek ("Provider's error should be set (error=%d)(device=%s).", 580b09121f9SPawel Jakub Dawidek bp->bio_to->error, bp->bio_to->name)); 581b09121f9SPawel Jakub Dawidek 582b09121f9SPawel Jakub Dawidek G_STRIPE_LOGREQ(bp, "Request received."); 583b09121f9SPawel Jakub Dawidek 584b09121f9SPawel Jakub Dawidek switch (bp->bio_cmd) { 585b09121f9SPawel Jakub Dawidek case BIO_READ: 586b09121f9SPawel Jakub Dawidek case BIO_WRITE: 587b09121f9SPawel Jakub Dawidek case BIO_DELETE: 588b09121f9SPawel Jakub Dawidek break; 5898b522bdaSWarner Losh case BIO_SPEEDUP: 59042461fbaSPawel Jakub Dawidek case BIO_FLUSH: 5918b522bdaSWarner Losh g_stripe_pushdown(sc, bp); 59242461fbaSPawel Jakub Dawidek return; 593b09121f9SPawel Jakub Dawidek case BIO_GETATTR: 594*ea2d874cSMatthew Grooms if (!strcmp(bp->bio_attribute, "GEOM::candelete")) { 595*ea2d874cSMatthew Grooms int val = (sc->sc_flags & G_STRIPE_FLAG_CANDELETE) != 0; 596*ea2d874cSMatthew Grooms g_handleattr(bp, "GEOM::candelete", &val, sizeof(val)); 597*ea2d874cSMatthew Grooms return; 598*ea2d874cSMatthew Grooms } 599*ea2d874cSMatthew Grooms /* otherwise: To which provider it should be delivered? */ 600b09121f9SPawel Jakub Dawidek default: 601b09121f9SPawel Jakub Dawidek g_io_deliver(bp, EOPNOTSUPP); 602b09121f9SPawel Jakub Dawidek return; 603b09121f9SPawel Jakub Dawidek } 604b09121f9SPawel Jakub Dawidek 605b09121f9SPawel Jakub Dawidek stripesize = sc->sc_stripesize; 606b09121f9SPawel Jakub Dawidek 607b09121f9SPawel Jakub Dawidek /* 6084c55f05bSPawel Jakub Dawidek * Calculations are quite messy, but fast I hope. 609b09121f9SPawel Jakub Dawidek */ 610b09121f9SPawel Jakub Dawidek 611b09121f9SPawel Jakub Dawidek /* Stripe number. */ 612b09121f9SPawel Jakub Dawidek /* nstripe = bp->bio_offset / stripesize; */ 613b09121f9SPawel Jakub Dawidek nstripe = bp->bio_offset >> (off_t)sc->sc_stripebits; 614b09121f9SPawel Jakub Dawidek /* Disk number. */ 615b09121f9SPawel Jakub Dawidek no = nstripe % sc->sc_ndisks; 616b09121f9SPawel Jakub Dawidek /* Start position in stripe. */ 617b09121f9SPawel Jakub Dawidek /* start = bp->bio_offset % stripesize; */ 618b09121f9SPawel Jakub Dawidek start = bp->bio_offset & (stripesize - 1); 619b09121f9SPawel Jakub Dawidek /* Start position in disk. */ 6204c55f05bSPawel Jakub Dawidek /* offset = (nstripe / sc->sc_ndisks) * stripesize + start; */ 6214c55f05bSPawel Jakub Dawidek offset = ((nstripe / sc->sc_ndisks) << sc->sc_stripebits) + start; 622b09121f9SPawel Jakub Dawidek /* Length of data to operate. */ 623b09121f9SPawel Jakub Dawidek length = MIN(bp->bio_length, stripesize - start); 624b09121f9SPawel Jakub Dawidek 625b09121f9SPawel Jakub Dawidek /* 6264c55f05bSPawel Jakub Dawidek * Do use "fast" mode when: 6274c55f05bSPawel Jakub Dawidek * 1. "Fast" mode is ON. 6284c55f05bSPawel Jakub Dawidek * and 629cd853791SKonstantin Belousov * 2. Request size is less than or equal to maxphys, 6304c55f05bSPawel Jakub Dawidek * which should always be true. 6314c55f05bSPawel Jakub Dawidek * and 6324c55f05bSPawel Jakub Dawidek * 3. Request size is bigger than stripesize * ndisks. If it isn't, 6334c55f05bSPawel Jakub Dawidek * there will be no need to send more than one I/O request to 6344c55f05bSPawel Jakub Dawidek * a provider, so there is nothing to optmize. 63540ea77a0SAlexander Motin * and 63640ea77a0SAlexander Motin * 4. Request is not unmapped. 63766b92c07SAlexander Motin * and 63866b92c07SAlexander Motin * 5. It is not a BIO_DELETE. 639b09121f9SPawel Jakub Dawidek */ 640cd853791SKonstantin Belousov if (g_stripe_fast && bp->bio_length <= maxphys && 64140ea77a0SAlexander Motin bp->bio_length >= stripesize * sc->sc_ndisks && 64266b92c07SAlexander Motin (bp->bio_flags & BIO_UNMAPPED) == 0 && 64366b92c07SAlexander Motin bp->bio_cmd != BIO_DELETE) { 6444c55f05bSPawel Jakub Dawidek fast = 1; 6454c55f05bSPawel Jakub Dawidek } 6464c55f05bSPawel Jakub Dawidek error = 0; 647cea36368SPawel Jakub Dawidek if (fast) { 6484c55f05bSPawel Jakub Dawidek error = g_stripe_start_fast(bp, no, offset, length); 649cea36368SPawel Jakub Dawidek if (error != 0) 650cea36368SPawel Jakub Dawidek g_stripe_fast_failed++; 651cea36368SPawel Jakub Dawidek } 6524c55f05bSPawel Jakub Dawidek /* 6534c55f05bSPawel Jakub Dawidek * Do use "economic" when: 6544c55f05bSPawel Jakub Dawidek * 1. "Economic" mode is ON. 6554c55f05bSPawel Jakub Dawidek * or 656f24bf752SPawel Jakub Dawidek * 2. "Fast" mode failed. It can only fail if there is no memory. 6574c55f05bSPawel Jakub Dawidek */ 6584c55f05bSPawel Jakub Dawidek if (!fast || error != 0) 6594c55f05bSPawel Jakub Dawidek error = g_stripe_start_economic(bp, no, offset, length); 6604c55f05bSPawel Jakub Dawidek if (error != 0) { 661b09121f9SPawel Jakub Dawidek if (bp->bio_error == 0) 6624c55f05bSPawel Jakub Dawidek bp->bio_error = error; 663b09121f9SPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 664b09121f9SPawel Jakub Dawidek } 665b09121f9SPawel Jakub Dawidek } 666b09121f9SPawel Jakub Dawidek 667b09121f9SPawel Jakub Dawidek static void 668b09121f9SPawel Jakub Dawidek g_stripe_check_and_run(struct g_stripe_softc *sc) 669b09121f9SPawel Jakub Dawidek { 67040ea77a0SAlexander Motin struct g_provider *dp; 671b09121f9SPawel Jakub Dawidek off_t mediasize, ms; 672b09121f9SPawel Jakub Dawidek u_int no, sectorsize = 0; 673b09121f9SPawel Jakub Dawidek 6740849a53fSAlexander Motin g_topology_assert(); 675b09121f9SPawel Jakub Dawidek if (g_stripe_nvalid(sc) != sc->sc_ndisks) 676b09121f9SPawel Jakub Dawidek return; 677b09121f9SPawel Jakub Dawidek 678889c5dc2SPawel Jakub Dawidek sc->sc_provider = g_new_providerf(sc->sc_geom, "stripe/%s", 679889c5dc2SPawel Jakub Dawidek sc->sc_name); 68040ea77a0SAlexander Motin sc->sc_provider->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; 68140ea77a0SAlexander Motin if (g_stripe_fast == 0) 68240ea77a0SAlexander Motin sc->sc_provider->flags |= G_PF_ACCEPT_UNMAPPED; 683b09121f9SPawel Jakub Dawidek /* 684b09121f9SPawel Jakub Dawidek * Find the smallest disk. 685b09121f9SPawel Jakub Dawidek */ 686b09121f9SPawel Jakub Dawidek mediasize = sc->sc_disks[0]->provider->mediasize; 687b09121f9SPawel Jakub Dawidek if (sc->sc_type == G_STRIPE_TYPE_AUTOMATIC) 688b09121f9SPawel Jakub Dawidek mediasize -= sc->sc_disks[0]->provider->sectorsize; 689b09121f9SPawel Jakub Dawidek mediasize -= mediasize % sc->sc_stripesize; 690b09121f9SPawel Jakub Dawidek sectorsize = sc->sc_disks[0]->provider->sectorsize; 691b09121f9SPawel Jakub Dawidek for (no = 1; no < sc->sc_ndisks; no++) { 69240ea77a0SAlexander Motin dp = sc->sc_disks[no]->provider; 69340ea77a0SAlexander Motin ms = dp->mediasize; 694b09121f9SPawel Jakub Dawidek if (sc->sc_type == G_STRIPE_TYPE_AUTOMATIC) 69540ea77a0SAlexander Motin ms -= dp->sectorsize; 696b09121f9SPawel Jakub Dawidek ms -= ms % sc->sc_stripesize; 697b09121f9SPawel Jakub Dawidek if (ms < mediasize) 698b09121f9SPawel Jakub Dawidek mediasize = ms; 69940ea77a0SAlexander Motin sectorsize = lcm(sectorsize, dp->sectorsize); 70040ea77a0SAlexander Motin 70140ea77a0SAlexander Motin /* A provider underneath us doesn't support unmapped */ 70240ea77a0SAlexander Motin if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 70340ea77a0SAlexander Motin G_STRIPE_DEBUG(1, "Cancelling unmapped " 70440ea77a0SAlexander Motin "because of %s.", dp->name); 70540ea77a0SAlexander Motin sc->sc_provider->flags &= ~G_PF_ACCEPT_UNMAPPED; 70640ea77a0SAlexander Motin } 707b09121f9SPawel Jakub Dawidek } 708b09121f9SPawel Jakub Dawidek sc->sc_provider->sectorsize = sectorsize; 709b09121f9SPawel Jakub Dawidek sc->sc_provider->mediasize = mediasize * sc->sc_ndisks; 710f00919d2SAlexander Motin sc->sc_provider->stripesize = sc->sc_stripesize; 711f00919d2SAlexander Motin sc->sc_provider->stripeoffset = 0; 712b09121f9SPawel Jakub Dawidek g_error_provider(sc->sc_provider, 0); 713b09121f9SPawel Jakub Dawidek 7140849a53fSAlexander Motin G_STRIPE_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 715b09121f9SPawel Jakub Dawidek } 716b09121f9SPawel Jakub Dawidek 717b09121f9SPawel Jakub Dawidek static int 718b09121f9SPawel Jakub Dawidek g_stripe_read_metadata(struct g_consumer *cp, struct g_stripe_metadata *md) 719b09121f9SPawel Jakub Dawidek { 720b09121f9SPawel Jakub Dawidek struct g_provider *pp; 721b09121f9SPawel Jakub Dawidek u_char *buf; 722b09121f9SPawel Jakub Dawidek int error; 723b09121f9SPawel Jakub Dawidek 724b09121f9SPawel Jakub Dawidek g_topology_assert(); 725b09121f9SPawel Jakub Dawidek 726b09121f9SPawel Jakub Dawidek error = g_access(cp, 1, 0, 0); 727b09121f9SPawel Jakub Dawidek if (error != 0) 728b09121f9SPawel Jakub Dawidek return (error); 729b09121f9SPawel Jakub Dawidek pp = cp->provider; 730b09121f9SPawel Jakub Dawidek g_topology_unlock(); 731b09121f9SPawel Jakub Dawidek buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 732b09121f9SPawel Jakub Dawidek &error); 733b09121f9SPawel Jakub Dawidek g_topology_lock(); 734b09121f9SPawel Jakub Dawidek g_access(cp, -1, 0, 0); 735b09121f9SPawel Jakub Dawidek if (buf == NULL) 736b09121f9SPawel Jakub Dawidek return (error); 737b09121f9SPawel Jakub Dawidek 738b09121f9SPawel Jakub Dawidek /* Decode metadata. */ 739b09121f9SPawel Jakub Dawidek stripe_metadata_decode(buf, md); 740b09121f9SPawel Jakub Dawidek g_free(buf); 741b09121f9SPawel Jakub Dawidek 742b09121f9SPawel Jakub Dawidek return (0); 743b09121f9SPawel Jakub Dawidek } 744b09121f9SPawel Jakub Dawidek 745b09121f9SPawel Jakub Dawidek /* 746b09121f9SPawel Jakub Dawidek * Add disk to given device. 747b09121f9SPawel Jakub Dawidek */ 748b09121f9SPawel Jakub Dawidek static int 749b09121f9SPawel Jakub Dawidek g_stripe_add_disk(struct g_stripe_softc *sc, struct g_provider *pp, u_int no) 750b09121f9SPawel Jakub Dawidek { 751b09121f9SPawel Jakub Dawidek struct g_consumer *cp, *fcp; 752b09121f9SPawel Jakub Dawidek struct g_geom *gp; 753b09121f9SPawel Jakub Dawidek int error; 754b09121f9SPawel Jakub Dawidek 7550849a53fSAlexander Motin g_topology_assert(); 756b09121f9SPawel Jakub Dawidek /* Metadata corrupted? */ 757b09121f9SPawel Jakub Dawidek if (no >= sc->sc_ndisks) 758b09121f9SPawel Jakub Dawidek return (EINVAL); 759b09121f9SPawel Jakub Dawidek 760b09121f9SPawel Jakub Dawidek /* Check if disk is not already attached. */ 761b09121f9SPawel Jakub Dawidek if (sc->sc_disks[no] != NULL) 762b09121f9SPawel Jakub Dawidek return (EEXIST); 763b09121f9SPawel Jakub Dawidek 764b09121f9SPawel Jakub Dawidek gp = sc->sc_geom; 765b09121f9SPawel Jakub Dawidek fcp = LIST_FIRST(&gp->consumer); 766b09121f9SPawel Jakub Dawidek 767b09121f9SPawel Jakub Dawidek cp = g_new_consumer(gp); 76840ea77a0SAlexander Motin cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 7690849a53fSAlexander Motin cp->private = NULL; 7700849a53fSAlexander Motin cp->index = no; 771b09121f9SPawel Jakub Dawidek error = g_attach(cp, pp); 772b09121f9SPawel Jakub Dawidek if (error != 0) { 773b09121f9SPawel Jakub Dawidek g_destroy_consumer(cp); 774b09121f9SPawel Jakub Dawidek return (error); 775b09121f9SPawel Jakub Dawidek } 776b09121f9SPawel Jakub Dawidek 777b09121f9SPawel Jakub Dawidek if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 778b09121f9SPawel Jakub Dawidek error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 779b09121f9SPawel Jakub Dawidek if (error != 0) { 780b09121f9SPawel Jakub Dawidek g_detach(cp); 781b09121f9SPawel Jakub Dawidek g_destroy_consumer(cp); 782b09121f9SPawel Jakub Dawidek return (error); 783b09121f9SPawel Jakub Dawidek } 784b09121f9SPawel Jakub Dawidek } 785b09121f9SPawel Jakub Dawidek if (sc->sc_type == G_STRIPE_TYPE_AUTOMATIC) { 786b09121f9SPawel Jakub Dawidek struct g_stripe_metadata md; 787b09121f9SPawel Jakub Dawidek 788b09121f9SPawel Jakub Dawidek /* Reread metadata. */ 789b09121f9SPawel Jakub Dawidek error = g_stripe_read_metadata(cp, &md); 790b09121f9SPawel Jakub Dawidek if (error != 0) 791b09121f9SPawel Jakub Dawidek goto fail; 792b09121f9SPawel Jakub Dawidek 793b09121f9SPawel Jakub Dawidek if (strcmp(md.md_magic, G_STRIPE_MAGIC) != 0 || 794b09121f9SPawel Jakub Dawidek strcmp(md.md_name, sc->sc_name) != 0 || 795b09121f9SPawel Jakub Dawidek md.md_id != sc->sc_id) { 796b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Metadata on %s changed.", pp->name); 797b09121f9SPawel Jakub Dawidek goto fail; 798b09121f9SPawel Jakub Dawidek } 799b09121f9SPawel Jakub Dawidek } 800b09121f9SPawel Jakub Dawidek 801b09121f9SPawel Jakub Dawidek sc->sc_disks[no] = cp; 802*ea2d874cSMatthew Grooms 803*ea2d874cSMatthew Grooms /* cascade candelete */ 804*ea2d874cSMatthew Grooms error = g_access(cp, 1, 0, 0); 805*ea2d874cSMatthew Grooms if (error == 0) { 806*ea2d874cSMatthew Grooms int can_delete; 807*ea2d874cSMatthew Grooms 808*ea2d874cSMatthew Grooms error = g_getattr("GEOM::candelete", cp, &can_delete); 809*ea2d874cSMatthew Grooms if (error == 0 && can_delete != 0) 810*ea2d874cSMatthew Grooms sc->sc_flags |= G_STRIPE_FLAG_CANDELETE; 811*ea2d874cSMatthew Grooms G_STRIPE_DEBUG(1, "Provider %s candelete %i.", pp->name, 812*ea2d874cSMatthew Grooms can_delete); 813*ea2d874cSMatthew Grooms g_access(cp, -1, 0, 0); 814*ea2d874cSMatthew Grooms } 815*ea2d874cSMatthew Grooms 816889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 817b09121f9SPawel Jakub Dawidek g_stripe_check_and_run(sc); 818b09121f9SPawel Jakub Dawidek 819b09121f9SPawel Jakub Dawidek return (0); 820b09121f9SPawel Jakub Dawidek fail: 821b09121f9SPawel Jakub Dawidek if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 822b09121f9SPawel Jakub Dawidek g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 823b09121f9SPawel Jakub Dawidek g_detach(cp); 824b09121f9SPawel Jakub Dawidek g_destroy_consumer(cp); 825b09121f9SPawel Jakub Dawidek return (error); 826b09121f9SPawel Jakub Dawidek } 827b09121f9SPawel Jakub Dawidek 828b09121f9SPawel Jakub Dawidek static struct g_geom * 829b09121f9SPawel Jakub Dawidek g_stripe_create(struct g_class *mp, const struct g_stripe_metadata *md, 830b09121f9SPawel Jakub Dawidek u_int type) 831b09121f9SPawel Jakub Dawidek { 832b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 833b09121f9SPawel Jakub Dawidek struct g_geom *gp; 834b09121f9SPawel Jakub Dawidek u_int no; 835b09121f9SPawel Jakub Dawidek 8360849a53fSAlexander Motin g_topology_assert(); 837889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 838b09121f9SPawel Jakub Dawidek md->md_id); 839b09121f9SPawel Jakub Dawidek 840b09121f9SPawel Jakub Dawidek /* Two disks is minimum. */ 841889c5dc2SPawel Jakub Dawidek if (md->md_all < 2) { 842889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Too few disks defined for %s.", md->md_name); 843b09121f9SPawel Jakub Dawidek return (NULL); 844b09121f9SPawel Jakub Dawidek } 845b09121f9SPawel Jakub Dawidek #if 0 846b09121f9SPawel Jakub Dawidek /* Stripe size have to be grater than or equal to sector size. */ 847b09121f9SPawel Jakub Dawidek if (md->md_stripesize < sectorsize) { 848889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Invalid stripe size for %s.", md->md_name); 849b09121f9SPawel Jakub Dawidek return (NULL); 850b09121f9SPawel Jakub Dawidek } 851b09121f9SPawel Jakub Dawidek #endif 852b09121f9SPawel Jakub Dawidek /* Stripe size have to be power of 2. */ 853b09121f9SPawel Jakub Dawidek if (!powerof2(md->md_stripesize)) { 854889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Invalid stripe size for %s.", md->md_name); 855b09121f9SPawel Jakub Dawidek return (NULL); 856b09121f9SPawel Jakub Dawidek } 857b09121f9SPawel Jakub Dawidek 858b09121f9SPawel Jakub Dawidek /* Check for duplicate unit */ 859b09121f9SPawel Jakub Dawidek LIST_FOREACH(gp, &mp->geom, geom) { 860b09121f9SPawel Jakub Dawidek sc = gp->softc; 861b09121f9SPawel Jakub Dawidek if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 862b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Device %s already configured.", 863889c5dc2SPawel Jakub Dawidek sc->sc_name); 864b09121f9SPawel Jakub Dawidek return (NULL); 865b09121f9SPawel Jakub Dawidek } 866b09121f9SPawel Jakub Dawidek } 867889c5dc2SPawel Jakub Dawidek gp = g_new_geomf(mp, "%s", md->md_name); 8682017a9d3SPawel Jakub Dawidek sc = malloc(sizeof(*sc), M_STRIPE, M_WAITOK | M_ZERO); 869b09121f9SPawel Jakub Dawidek gp->start = g_stripe_start; 870b09121f9SPawel Jakub Dawidek gp->spoiled = g_stripe_orphan; 871b09121f9SPawel Jakub Dawidek gp->orphan = g_stripe_orphan; 872b09121f9SPawel Jakub Dawidek gp->access = g_stripe_access; 873b09121f9SPawel Jakub Dawidek gp->dumpconf = g_stripe_dumpconf; 874b09121f9SPawel Jakub Dawidek 875b09121f9SPawel Jakub Dawidek sc->sc_id = md->md_id; 876b09121f9SPawel Jakub Dawidek sc->sc_stripesize = md->md_stripesize; 877a95452eeSPawel Jakub Dawidek sc->sc_stripebits = bitcount32(sc->sc_stripesize - 1); 878b09121f9SPawel Jakub Dawidek sc->sc_ndisks = md->md_all; 879b09121f9SPawel Jakub Dawidek sc->sc_disks = malloc(sizeof(struct g_consumer *) * sc->sc_ndisks, 880b09121f9SPawel Jakub Dawidek M_STRIPE, M_WAITOK | M_ZERO); 881b09121f9SPawel Jakub Dawidek for (no = 0; no < sc->sc_ndisks; no++) 882b09121f9SPawel Jakub Dawidek sc->sc_disks[no] = NULL; 883b09121f9SPawel Jakub Dawidek sc->sc_type = type; 88440ea77a0SAlexander Motin mtx_init(&sc->sc_lock, "gstripe lock", NULL, MTX_DEF); 885b09121f9SPawel Jakub Dawidek 886b09121f9SPawel Jakub Dawidek gp->softc = sc; 887b09121f9SPawel Jakub Dawidek sc->sc_geom = gp; 888b09121f9SPawel Jakub Dawidek sc->sc_provider = NULL; 889b09121f9SPawel Jakub Dawidek 890889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 891b09121f9SPawel Jakub Dawidek 892b09121f9SPawel Jakub Dawidek return (gp); 893b09121f9SPawel Jakub Dawidek } 894b09121f9SPawel Jakub Dawidek 895b09121f9SPawel Jakub Dawidek static int 896b09121f9SPawel Jakub Dawidek g_stripe_destroy(struct g_stripe_softc *sc, boolean_t force) 897b09121f9SPawel Jakub Dawidek { 898b09121f9SPawel Jakub Dawidek struct g_provider *pp; 8990849a53fSAlexander Motin struct g_consumer *cp, *cp1; 900b09121f9SPawel Jakub Dawidek struct g_geom *gp; 901b09121f9SPawel Jakub Dawidek 902b09121f9SPawel Jakub Dawidek g_topology_assert(); 903b09121f9SPawel Jakub Dawidek 904b09121f9SPawel Jakub Dawidek if (sc == NULL) 905b09121f9SPawel Jakub Dawidek return (ENXIO); 906b09121f9SPawel Jakub Dawidek 907b09121f9SPawel Jakub Dawidek pp = sc->sc_provider; 908b09121f9SPawel Jakub Dawidek if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 909b09121f9SPawel Jakub Dawidek if (force) { 910b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Device %s is still open, so it " 911b09121f9SPawel Jakub Dawidek "can't be definitely removed.", pp->name); 912b09121f9SPawel Jakub Dawidek } else { 913b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(1, 914b09121f9SPawel Jakub Dawidek "Device %s is still open (r%dw%de%d).", pp->name, 915b09121f9SPawel Jakub Dawidek pp->acr, pp->acw, pp->ace); 916b09121f9SPawel Jakub Dawidek return (EBUSY); 917b09121f9SPawel Jakub Dawidek } 918b09121f9SPawel Jakub Dawidek } 919b09121f9SPawel Jakub Dawidek 920b09121f9SPawel Jakub Dawidek gp = sc->sc_geom; 9210849a53fSAlexander Motin LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 9220849a53fSAlexander Motin g_stripe_remove_disk(cp); 9230849a53fSAlexander Motin if (cp1 == NULL) 9240849a53fSAlexander Motin return (0); /* Recursion happened. */ 9250849a53fSAlexander Motin } 9260849a53fSAlexander Motin if (!LIST_EMPTY(&gp->consumer)) 9270849a53fSAlexander Motin return (EINPROGRESS); 9280849a53fSAlexander Motin 929b09121f9SPawel Jakub Dawidek gp->softc = NULL; 930b09121f9SPawel Jakub Dawidek KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 931b09121f9SPawel Jakub Dawidek gp->name)); 932b09121f9SPawel Jakub Dawidek free(sc->sc_disks, M_STRIPE); 93340ea77a0SAlexander Motin mtx_destroy(&sc->sc_lock); 934b09121f9SPawel Jakub Dawidek free(sc, M_STRIPE); 935b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Device %s destroyed.", gp->name); 936b09121f9SPawel Jakub Dawidek g_wither_geom(gp, ENXIO); 937b09121f9SPawel Jakub Dawidek return (0); 938b09121f9SPawel Jakub Dawidek } 939b09121f9SPawel Jakub Dawidek 940b09121f9SPawel Jakub Dawidek static int 941b09121f9SPawel Jakub Dawidek g_stripe_destroy_geom(struct gctl_req *req __unused, 942b09121f9SPawel Jakub Dawidek struct g_class *mp __unused, struct g_geom *gp) 943b09121f9SPawel Jakub Dawidek { 944b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 945b09121f9SPawel Jakub Dawidek 946b09121f9SPawel Jakub Dawidek sc = gp->softc; 947b09121f9SPawel Jakub Dawidek return (g_stripe_destroy(sc, 0)); 948b09121f9SPawel Jakub Dawidek } 949b09121f9SPawel Jakub Dawidek 950b09121f9SPawel Jakub Dawidek static struct g_geom * 951b09121f9SPawel Jakub Dawidek g_stripe_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 952b09121f9SPawel Jakub Dawidek { 953b09121f9SPawel Jakub Dawidek struct g_stripe_metadata md; 954b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 955b09121f9SPawel Jakub Dawidek struct g_consumer *cp; 956b09121f9SPawel Jakub Dawidek struct g_geom *gp; 957b09121f9SPawel Jakub Dawidek int error; 958b09121f9SPawel Jakub Dawidek 959b09121f9SPawel Jakub Dawidek g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 960b09121f9SPawel Jakub Dawidek g_topology_assert(); 961b09121f9SPawel Jakub Dawidek 962f8727e71SPawel Jakub Dawidek /* Skip providers that are already open for writing. */ 963f8727e71SPawel Jakub Dawidek if (pp->acw > 0) 964f8727e71SPawel Jakub Dawidek return (NULL); 965f8727e71SPawel Jakub Dawidek 966b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(3, "Tasting %s.", pp->name); 967b09121f9SPawel Jakub Dawidek 968b09121f9SPawel Jakub Dawidek gp = g_new_geomf(mp, "stripe:taste"); 969b09121f9SPawel Jakub Dawidek gp->start = g_stripe_start; 970b09121f9SPawel Jakub Dawidek gp->access = g_stripe_access; 971b09121f9SPawel Jakub Dawidek gp->orphan = g_stripe_orphan; 972b09121f9SPawel Jakub Dawidek cp = g_new_consumer(gp); 97310ae42ccSAlexander Motin cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 974d22ff249SEdward Tomasz Napierala error = g_attach(cp, pp); 975d22ff249SEdward Tomasz Napierala if (error == 0) { 976b09121f9SPawel Jakub Dawidek error = g_stripe_read_metadata(cp, &md); 9777e72a708SPawel Jakub Dawidek g_detach(cp); 978d22ff249SEdward Tomasz Napierala } 9797e72a708SPawel Jakub Dawidek g_destroy_consumer(cp); 9807e72a708SPawel Jakub Dawidek g_destroy_geom(gp); 981b09121f9SPawel Jakub Dawidek if (error != 0) 982b09121f9SPawel Jakub Dawidek return (NULL); 983b09121f9SPawel Jakub Dawidek gp = NULL; 984b09121f9SPawel Jakub Dawidek 985b09121f9SPawel Jakub Dawidek if (strcmp(md.md_magic, G_STRIPE_MAGIC) != 0) 986b09121f9SPawel Jakub Dawidek return (NULL); 987b09121f9SPawel Jakub Dawidek if (md.md_version > G_STRIPE_VERSION) { 988b09121f9SPawel Jakub Dawidek printf("geom_stripe.ko module is too old to handle %s.\n", 989b09121f9SPawel Jakub Dawidek pp->name); 990b09121f9SPawel Jakub Dawidek return (NULL); 991b09121f9SPawel Jakub Dawidek } 9926c74f517SPawel Jakub Dawidek /* 9936c74f517SPawel Jakub Dawidek * Backward compatibility: 9946c74f517SPawel Jakub Dawidek */ 995e6890985SPawel Jakub Dawidek /* There was no md_provider field in earlier versions of metadata. */ 9966c74f517SPawel Jakub Dawidek if (md.md_version < 2) 9976c74f517SPawel Jakub Dawidek bzero(md.md_provider, sizeof(md.md_provider)); 998e6890985SPawel Jakub Dawidek /* There was no md_provsize field in earlier versions of metadata. */ 999e6890985SPawel Jakub Dawidek if (md.md_version < 3) 1000e6890985SPawel Jakub Dawidek md.md_provsize = pp->mediasize; 10016c74f517SPawel Jakub Dawidek 100290f2be24SAlexander Motin if (md.md_provider[0] != '\0' && 100390f2be24SAlexander Motin !g_compare_names(md.md_provider, pp->name)) 10046c74f517SPawel Jakub Dawidek return (NULL); 1005e6890985SPawel Jakub Dawidek if (md.md_provsize != pp->mediasize) 1006e6890985SPawel Jakub Dawidek return (NULL); 1007b09121f9SPawel Jakub Dawidek 1008b09121f9SPawel Jakub Dawidek /* 1009b09121f9SPawel Jakub Dawidek * Let's check if device already exists. 1010b09121f9SPawel Jakub Dawidek */ 1011b09121f9SPawel Jakub Dawidek sc = NULL; 1012b09121f9SPawel Jakub Dawidek LIST_FOREACH(gp, &mp->geom, geom) { 1013b09121f9SPawel Jakub Dawidek sc = gp->softc; 1014b09121f9SPawel Jakub Dawidek if (sc == NULL) 1015b09121f9SPawel Jakub Dawidek continue; 1016b09121f9SPawel Jakub Dawidek if (sc->sc_type != G_STRIPE_TYPE_AUTOMATIC) 1017b09121f9SPawel Jakub Dawidek continue; 1018b09121f9SPawel Jakub Dawidek if (strcmp(md.md_name, sc->sc_name) != 0) 1019b09121f9SPawel Jakub Dawidek continue; 1020b09121f9SPawel Jakub Dawidek if (md.md_id != sc->sc_id) 1021b09121f9SPawel Jakub Dawidek continue; 1022b09121f9SPawel Jakub Dawidek break; 1023b09121f9SPawel Jakub Dawidek } 1024b09121f9SPawel Jakub Dawidek if (gp != NULL) { 1025b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 1026b09121f9SPawel Jakub Dawidek error = g_stripe_add_disk(sc, pp, md.md_no); 1027b09121f9SPawel Jakub Dawidek if (error != 0) { 1028b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, 1029b09121f9SPawel Jakub Dawidek "Cannot add disk %s to %s (error=%d).", pp->name, 1030b09121f9SPawel Jakub Dawidek gp->name, error); 1031b09121f9SPawel Jakub Dawidek return (NULL); 1032b09121f9SPawel Jakub Dawidek } 1033b09121f9SPawel Jakub Dawidek } else { 1034b09121f9SPawel Jakub Dawidek gp = g_stripe_create(mp, &md, G_STRIPE_TYPE_AUTOMATIC); 1035b09121f9SPawel Jakub Dawidek if (gp == NULL) { 1036889c5dc2SPawel Jakub Dawidek G_STRIPE_DEBUG(0, "Cannot create device %s.", 1037b09121f9SPawel Jakub Dawidek md.md_name); 1038b09121f9SPawel Jakub Dawidek return (NULL); 1039b09121f9SPawel Jakub Dawidek } 1040b09121f9SPawel Jakub Dawidek sc = gp->softc; 1041b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 1042b09121f9SPawel Jakub Dawidek error = g_stripe_add_disk(sc, pp, md.md_no); 1043b09121f9SPawel Jakub Dawidek if (error != 0) { 1044b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(0, 1045b09121f9SPawel Jakub Dawidek "Cannot add disk %s to %s (error=%d).", pp->name, 1046b09121f9SPawel Jakub Dawidek gp->name, error); 1047b09121f9SPawel Jakub Dawidek g_stripe_destroy(sc, 1); 1048b09121f9SPawel Jakub Dawidek return (NULL); 1049b09121f9SPawel Jakub Dawidek } 1050b09121f9SPawel Jakub Dawidek } 1051b09121f9SPawel Jakub Dawidek 1052b09121f9SPawel Jakub Dawidek return (gp); 1053b09121f9SPawel Jakub Dawidek } 1054b09121f9SPawel Jakub Dawidek 1055b09121f9SPawel Jakub Dawidek static void 1056b09121f9SPawel Jakub Dawidek g_stripe_ctl_create(struct gctl_req *req, struct g_class *mp) 1057b09121f9SPawel Jakub Dawidek { 1058b09121f9SPawel Jakub Dawidek u_int attached, no; 1059b09121f9SPawel Jakub Dawidek struct g_stripe_metadata md; 1060b09121f9SPawel Jakub Dawidek struct g_provider *pp; 1061b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 1062b09121f9SPawel Jakub Dawidek struct g_geom *gp; 1063b09121f9SPawel Jakub Dawidek struct sbuf *sb; 10646d305ab0SEugene Grosbein off_t *stripesize; 1065b09121f9SPawel Jakub Dawidek const char *name; 1066b09121f9SPawel Jakub Dawidek char param[16]; 1067b09121f9SPawel Jakub Dawidek int *nargs; 1068b09121f9SPawel Jakub Dawidek 1069b09121f9SPawel Jakub Dawidek g_topology_assert(); 1070b09121f9SPawel Jakub Dawidek nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1071b09121f9SPawel Jakub Dawidek if (nargs == NULL) { 1072b09121f9SPawel Jakub Dawidek gctl_error(req, "No '%s' argument.", "nargs"); 1073b09121f9SPawel Jakub Dawidek return; 1074b09121f9SPawel Jakub Dawidek } 1075b09121f9SPawel Jakub Dawidek if (*nargs <= 2) { 1076b09121f9SPawel Jakub Dawidek gctl_error(req, "Too few arguments."); 1077b09121f9SPawel Jakub Dawidek return; 1078b09121f9SPawel Jakub Dawidek } 1079b09121f9SPawel Jakub Dawidek 1080b09121f9SPawel Jakub Dawidek strlcpy(md.md_magic, G_STRIPE_MAGIC, sizeof(md.md_magic)); 1081b09121f9SPawel Jakub Dawidek md.md_version = G_STRIPE_VERSION; 1082b09121f9SPawel Jakub Dawidek name = gctl_get_asciiparam(req, "arg0"); 1083b09121f9SPawel Jakub Dawidek if (name == NULL) { 1084b09121f9SPawel Jakub Dawidek gctl_error(req, "No 'arg%u' argument.", 0); 1085b09121f9SPawel Jakub Dawidek return; 1086b09121f9SPawel Jakub Dawidek } 1087b09121f9SPawel Jakub Dawidek strlcpy(md.md_name, name, sizeof(md.md_name)); 1088b09121f9SPawel Jakub Dawidek md.md_id = arc4random(); 1089b09121f9SPawel Jakub Dawidek md.md_no = 0; 1090b09121f9SPawel Jakub Dawidek md.md_all = *nargs - 1; 1091b09121f9SPawel Jakub Dawidek stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize)); 1092b09121f9SPawel Jakub Dawidek if (stripesize == NULL) { 1093b09121f9SPawel Jakub Dawidek gctl_error(req, "No '%s' argument.", "stripesize"); 1094b09121f9SPawel Jakub Dawidek return; 1095b09121f9SPawel Jakub Dawidek } 10966d305ab0SEugene Grosbein md.md_stripesize = (uint32_t)*stripesize; 10976c74f517SPawel Jakub Dawidek bzero(md.md_provider, sizeof(md.md_provider)); 1098e6890985SPawel Jakub Dawidek /* This field is not important here. */ 1099e6890985SPawel Jakub Dawidek md.md_provsize = 0; 1100b09121f9SPawel Jakub Dawidek 1101b09121f9SPawel Jakub Dawidek /* Check all providers are valid */ 1102b09121f9SPawel Jakub Dawidek for (no = 1; no < *nargs; no++) { 1103b09121f9SPawel Jakub Dawidek snprintf(param, sizeof(param), "arg%u", no); 1104fcf69f3dSXin LI pp = gctl_get_provider(req, param); 1105fcf69f3dSXin LI if (pp == NULL) 1106b09121f9SPawel Jakub Dawidek return; 1107b09121f9SPawel Jakub Dawidek } 1108b09121f9SPawel Jakub Dawidek 1109b09121f9SPawel Jakub Dawidek gp = g_stripe_create(mp, &md, G_STRIPE_TYPE_MANUAL); 1110b09121f9SPawel Jakub Dawidek if (gp == NULL) { 1111889c5dc2SPawel Jakub Dawidek gctl_error(req, "Can't configure %s.", md.md_name); 1112b09121f9SPawel Jakub Dawidek return; 1113b09121f9SPawel Jakub Dawidek } 1114b09121f9SPawel Jakub Dawidek 1115b09121f9SPawel Jakub Dawidek sc = gp->softc; 11162616144eSDag-Erling Smørgrav sb = sbuf_new_auto(); 1117b09121f9SPawel Jakub Dawidek sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 1118b09121f9SPawel Jakub Dawidek for (attached = 0, no = 1; no < *nargs; no++) { 1119b09121f9SPawel Jakub Dawidek snprintf(param, sizeof(param), "arg%u", no); 1120fcf69f3dSXin LI pp = gctl_get_provider(req, param); 1121fcf69f3dSXin LI if (pp == NULL) { 1122b09121f9SPawel Jakub Dawidek name = gctl_get_asciiparam(req, param); 1123fcf69f3dSXin LI MPASS(name != NULL); 1124fcf69f3dSXin LI sbuf_printf(sb, " %s", name); 11250a3384a8SPawel Jakub Dawidek continue; 11260a3384a8SPawel Jakub Dawidek } 1127b09121f9SPawel Jakub Dawidek if (g_stripe_add_disk(sc, pp, no - 1) != 0) { 1128b09121f9SPawel Jakub Dawidek G_STRIPE_DEBUG(1, "Disk %u (%s) not attached to %s.", 1129b09121f9SPawel Jakub Dawidek no, pp->name, gp->name); 1130b09121f9SPawel Jakub Dawidek sbuf_printf(sb, " %s", pp->name); 1131b09121f9SPawel Jakub Dawidek continue; 1132b09121f9SPawel Jakub Dawidek } 1133b09121f9SPawel Jakub Dawidek attached++; 1134b09121f9SPawel Jakub Dawidek } 1135b09121f9SPawel Jakub Dawidek sbuf_finish(sb); 1136b09121f9SPawel Jakub Dawidek if (md.md_all != attached) { 1137b09121f9SPawel Jakub Dawidek g_stripe_destroy(gp->softc, 1); 1138b09121f9SPawel Jakub Dawidek gctl_error(req, "%s", sbuf_data(sb)); 1139b09121f9SPawel Jakub Dawidek } 1140b09121f9SPawel Jakub Dawidek sbuf_delete(sb); 1141b09121f9SPawel Jakub Dawidek } 1142b09121f9SPawel Jakub Dawidek 1143b09121f9SPawel Jakub Dawidek static struct g_stripe_softc * 1144b09121f9SPawel Jakub Dawidek g_stripe_find_device(struct g_class *mp, const char *name) 1145b09121f9SPawel Jakub Dawidek { 1146b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 1147b09121f9SPawel Jakub Dawidek struct g_geom *gp; 1148b09121f9SPawel Jakub Dawidek 1149b09121f9SPawel Jakub Dawidek LIST_FOREACH(gp, &mp->geom, geom) { 1150b09121f9SPawel Jakub Dawidek sc = gp->softc; 1151b09121f9SPawel Jakub Dawidek if (sc == NULL) 1152b09121f9SPawel Jakub Dawidek continue; 1153889c5dc2SPawel Jakub Dawidek if (strcmp(sc->sc_name, name) == 0) 1154b09121f9SPawel Jakub Dawidek return (sc); 1155b09121f9SPawel Jakub Dawidek } 1156b09121f9SPawel Jakub Dawidek return (NULL); 1157b09121f9SPawel Jakub Dawidek } 1158b09121f9SPawel Jakub Dawidek 1159b09121f9SPawel Jakub Dawidek static void 1160b09121f9SPawel Jakub Dawidek g_stripe_ctl_destroy(struct gctl_req *req, struct g_class *mp) 1161b09121f9SPawel Jakub Dawidek { 1162b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 1163b09121f9SPawel Jakub Dawidek int *force, *nargs, error; 1164b09121f9SPawel Jakub Dawidek const char *name; 1165b09121f9SPawel Jakub Dawidek char param[16]; 1166b09121f9SPawel Jakub Dawidek u_int i; 1167b09121f9SPawel Jakub Dawidek 1168b09121f9SPawel Jakub Dawidek g_topology_assert(); 1169b09121f9SPawel Jakub Dawidek 1170b09121f9SPawel Jakub Dawidek nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1171b09121f9SPawel Jakub Dawidek if (nargs == NULL) { 1172b09121f9SPawel Jakub Dawidek gctl_error(req, "No '%s' argument.", "nargs"); 1173b09121f9SPawel Jakub Dawidek return; 1174b09121f9SPawel Jakub Dawidek } 1175b09121f9SPawel Jakub Dawidek if (*nargs <= 0) { 1176b09121f9SPawel Jakub Dawidek gctl_error(req, "Missing device(s)."); 1177b09121f9SPawel Jakub Dawidek return; 1178b09121f9SPawel Jakub Dawidek } 1179b09121f9SPawel Jakub Dawidek force = gctl_get_paraml(req, "force", sizeof(*force)); 1180b09121f9SPawel Jakub Dawidek if (force == NULL) { 1181b09121f9SPawel Jakub Dawidek gctl_error(req, "No '%s' argument.", "force"); 1182b09121f9SPawel Jakub Dawidek return; 1183b09121f9SPawel Jakub Dawidek } 1184b09121f9SPawel Jakub Dawidek 1185b09121f9SPawel Jakub Dawidek for (i = 0; i < (u_int)*nargs; i++) { 1186b09121f9SPawel Jakub Dawidek snprintf(param, sizeof(param), "arg%u", i); 1187b09121f9SPawel Jakub Dawidek name = gctl_get_asciiparam(req, param); 1188b09121f9SPawel Jakub Dawidek if (name == NULL) { 1189b09121f9SPawel Jakub Dawidek gctl_error(req, "No 'arg%u' argument.", i); 1190b09121f9SPawel Jakub Dawidek return; 1191b09121f9SPawel Jakub Dawidek } 1192b09121f9SPawel Jakub Dawidek sc = g_stripe_find_device(mp, name); 1193b09121f9SPawel Jakub Dawidek if (sc == NULL) { 1194b09121f9SPawel Jakub Dawidek gctl_error(req, "No such device: %s.", name); 1195b09121f9SPawel Jakub Dawidek return; 1196b09121f9SPawel Jakub Dawidek } 1197b09121f9SPawel Jakub Dawidek error = g_stripe_destroy(sc, *force); 1198b09121f9SPawel Jakub Dawidek if (error != 0) { 1199b09121f9SPawel Jakub Dawidek gctl_error(req, "Cannot destroy device %s (error=%d).", 1200889c5dc2SPawel Jakub Dawidek sc->sc_name, error); 1201b09121f9SPawel Jakub Dawidek return; 1202b09121f9SPawel Jakub Dawidek } 1203b09121f9SPawel Jakub Dawidek } 1204b09121f9SPawel Jakub Dawidek } 1205b09121f9SPawel Jakub Dawidek 1206b09121f9SPawel Jakub Dawidek static void 1207b09121f9SPawel Jakub Dawidek g_stripe_config(struct gctl_req *req, struct g_class *mp, const char *verb) 1208b09121f9SPawel Jakub Dawidek { 1209b09121f9SPawel Jakub Dawidek uint32_t *version; 1210b09121f9SPawel Jakub Dawidek 1211b09121f9SPawel Jakub Dawidek g_topology_assert(); 1212b09121f9SPawel Jakub Dawidek 1213b09121f9SPawel Jakub Dawidek version = gctl_get_paraml(req, "version", sizeof(*version)); 1214b09121f9SPawel Jakub Dawidek if (version == NULL) { 1215b09121f9SPawel Jakub Dawidek gctl_error(req, "No '%s' argument.", "version"); 1216b09121f9SPawel Jakub Dawidek return; 1217b09121f9SPawel Jakub Dawidek } 1218b09121f9SPawel Jakub Dawidek if (*version != G_STRIPE_VERSION) { 1219b09121f9SPawel Jakub Dawidek gctl_error(req, "Userland and kernel parts are out of sync."); 1220b09121f9SPawel Jakub Dawidek return; 1221b09121f9SPawel Jakub Dawidek } 1222b09121f9SPawel Jakub Dawidek 1223b09121f9SPawel Jakub Dawidek if (strcmp(verb, "create") == 0) { 1224b09121f9SPawel Jakub Dawidek g_stripe_ctl_create(req, mp); 1225b09121f9SPawel Jakub Dawidek return; 1226a2e31b8bSPawel Jakub Dawidek } else if (strcmp(verb, "destroy") == 0 || 1227a2e31b8bSPawel Jakub Dawidek strcmp(verb, "stop") == 0) { 1228b09121f9SPawel Jakub Dawidek g_stripe_ctl_destroy(req, mp); 1229b09121f9SPawel Jakub Dawidek return; 1230b09121f9SPawel Jakub Dawidek } 1231b09121f9SPawel Jakub Dawidek 1232b09121f9SPawel Jakub Dawidek gctl_error(req, "Unknown verb."); 1233b09121f9SPawel Jakub Dawidek } 1234b09121f9SPawel Jakub Dawidek 1235b09121f9SPawel Jakub Dawidek static void 1236b09121f9SPawel Jakub Dawidek g_stripe_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 1237b09121f9SPawel Jakub Dawidek struct g_consumer *cp, struct g_provider *pp) 1238b09121f9SPawel Jakub Dawidek { 1239b09121f9SPawel Jakub Dawidek struct g_stripe_softc *sc; 1240b09121f9SPawel Jakub Dawidek 1241b09121f9SPawel Jakub Dawidek sc = gp->softc; 12421d723f1dSPawel Jakub Dawidek if (sc == NULL) 1243b09121f9SPawel Jakub Dawidek return; 12441d723f1dSPawel Jakub Dawidek if (pp != NULL) { 12451d723f1dSPawel Jakub Dawidek /* Nothing here. */ 12461d723f1dSPawel Jakub Dawidek } else if (cp != NULL) { 1247f0c8658dSPawel Jakub Dawidek sbuf_printf(sb, "%s<Number>%u</Number>\n", indent, 1248f0c8658dSPawel Jakub Dawidek (u_int)cp->index); 12491d723f1dSPawel Jakub Dawidek } else { 12501d723f1dSPawel Jakub Dawidek sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 12516d305ab0SEugene Grosbein sbuf_printf(sb, "%s<Stripesize>%ju</Stripesize>\n", indent, 12526d305ab0SEugene Grosbein (uintmax_t)sc->sc_stripesize); 12531d723f1dSPawel Jakub Dawidek sbuf_printf(sb, "%s<Type>", indent); 1254b09121f9SPawel Jakub Dawidek switch (sc->sc_type) { 1255b09121f9SPawel Jakub Dawidek case G_STRIPE_TYPE_AUTOMATIC: 125649ee0fceSAlexander Motin sbuf_cat(sb, "AUTOMATIC"); 1257b09121f9SPawel Jakub Dawidek break; 1258b09121f9SPawel Jakub Dawidek case G_STRIPE_TYPE_MANUAL: 125949ee0fceSAlexander Motin sbuf_cat(sb, "MANUAL"); 1260b09121f9SPawel Jakub Dawidek break; 1261b09121f9SPawel Jakub Dawidek default: 126249ee0fceSAlexander Motin sbuf_cat(sb, "UNKNOWN"); 1263b09121f9SPawel Jakub Dawidek break; 1264b09121f9SPawel Jakub Dawidek } 126549ee0fceSAlexander Motin sbuf_cat(sb, "</Type>\n"); 12661d723f1dSPawel Jakub Dawidek sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 12671d723f1dSPawel Jakub Dawidek indent, sc->sc_ndisks, g_stripe_nvalid(sc)); 12681d723f1dSPawel Jakub Dawidek sbuf_printf(sb, "%s<State>", indent); 12691d723f1dSPawel Jakub Dawidek if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 127049ee0fceSAlexander Motin sbuf_cat(sb, "UP"); 12713fb17452SPawel Jakub Dawidek else 127249ee0fceSAlexander Motin sbuf_cat(sb, "DOWN"); 127349ee0fceSAlexander Motin sbuf_cat(sb, "</State>\n"); 12741d723f1dSPawel Jakub Dawidek } 1275b09121f9SPawel Jakub Dawidek } 1276b09121f9SPawel Jakub Dawidek 1277b09121f9SPawel Jakub Dawidek DECLARE_GEOM_CLASS(g_stripe_class, g_stripe); 127874d6c131SKyle Evans MODULE_VERSION(geom_stripe, 0); 1279