1e770bc6bSMatt Jacob /*- 2e770bc6bSMatt Jacob * Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org> 3e770bc6bSMatt Jacob * All rights reserved. 4e770bc6bSMatt Jacob * 5e770bc6bSMatt Jacob * Redistribution and use in source and binary forms, with or without 6e770bc6bSMatt Jacob * modification, are permitted provided that the following conditions 7e770bc6bSMatt Jacob * are met: 8e770bc6bSMatt Jacob * 1. Redistributions of source code must retain the above copyright 9e770bc6bSMatt Jacob * notice, this list of conditions and the following disclaimer. 10e770bc6bSMatt Jacob * 2. Redistributions in binary form must reproduce the above copyright 11e770bc6bSMatt Jacob * notice, this list of conditions and the following disclaimer in the 12e770bc6bSMatt Jacob * documentation and/or other materials provided with the distribution. 13e770bc6bSMatt Jacob * 14e770bc6bSMatt Jacob * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15e770bc6bSMatt Jacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16e770bc6bSMatt Jacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17e770bc6bSMatt Jacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18e770bc6bSMatt Jacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19e770bc6bSMatt Jacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20e770bc6bSMatt Jacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21e770bc6bSMatt Jacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22e770bc6bSMatt Jacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23e770bc6bSMatt Jacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24e770bc6bSMatt Jacob * SUCH DAMAGE. 25e770bc6bSMatt Jacob */ 26e770bc6bSMatt Jacob /* 27e770bc6bSMatt Jacob * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the 28e770bc6bSMatt Jacob * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM 29e770bc6bSMatt Jacob * itself, all of which is most gratefully acknowledged. 30e770bc6bSMatt Jacob */ 31e770bc6bSMatt Jacob 32e770bc6bSMatt Jacob #include <sys/cdefs.h> 33e770bc6bSMatt Jacob __FBSDID("$FreeBSD$"); 34e770bc6bSMatt Jacob #include <sys/param.h> 35e770bc6bSMatt Jacob #include <sys/systm.h> 36e770bc6bSMatt Jacob #include <sys/kernel.h> 37e770bc6bSMatt Jacob #include <sys/module.h> 38e770bc6bSMatt Jacob #include <sys/lock.h> 39e770bc6bSMatt Jacob #include <sys/mutex.h> 40e770bc6bSMatt Jacob #include <sys/bio.h> 41e770bc6bSMatt Jacob #include <sys/sysctl.h> 42e770bc6bSMatt Jacob #include <sys/kthread.h> 43e770bc6bSMatt Jacob #include <sys/malloc.h> 44e770bc6bSMatt Jacob #include <geom/geom.h> 45e770bc6bSMatt Jacob #include <geom/multipath/g_multipath.h> 46e770bc6bSMatt Jacob 47e770bc6bSMatt Jacob 48e770bc6bSMatt Jacob SYSCTL_DECL(_kern_geom); 49e770bc6bSMatt Jacob SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0, 50e770bc6bSMatt Jacob "GEOM_MULTIPATH tunables"); 51e770bc6bSMatt Jacob static u_int g_multipath_debug = 0; 52e770bc6bSMatt Jacob SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW, 53e770bc6bSMatt Jacob &g_multipath_debug, 0, "Debug level"); 54e770bc6bSMatt Jacob 55e770bc6bSMatt Jacob static enum { 56e770bc6bSMatt Jacob GKT_NIL, 57e770bc6bSMatt Jacob GKT_RUN, 58e770bc6bSMatt Jacob GKT_DIE 59e770bc6bSMatt Jacob } g_multipath_kt_state; 60e770bc6bSMatt Jacob static struct bio_queue_head gmtbq; 61e770bc6bSMatt Jacob static struct mtx gmtbq_mtx; 62e770bc6bSMatt Jacob 63e770bc6bSMatt Jacob static void g_multipath_orphan(struct g_consumer *); 64e770bc6bSMatt Jacob static void g_multipath_start(struct bio *); 65e770bc6bSMatt Jacob static void g_multipath_done(struct bio *); 66e770bc6bSMatt Jacob static void g_multipath_done_error(struct bio *); 67e770bc6bSMatt Jacob static void g_multipath_kt(void *); 68e770bc6bSMatt Jacob 69e770bc6bSMatt Jacob static int g_multipath_destroy(struct g_geom *); 70e770bc6bSMatt Jacob static int 71e770bc6bSMatt Jacob g_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *); 72e770bc6bSMatt Jacob 73b5dce617SMatt Jacob static int g_multipath_rotate(struct g_geom *); 74b5dce617SMatt Jacob 75e770bc6bSMatt Jacob static g_taste_t g_multipath_taste; 76e770bc6bSMatt Jacob static g_ctl_req_t g_multipath_config; 77e770bc6bSMatt Jacob static g_init_t g_multipath_init; 78e770bc6bSMatt Jacob static g_fini_t g_multipath_fini; 79e770bc6bSMatt Jacob 80e770bc6bSMatt Jacob struct g_class g_multipath_class = { 81e770bc6bSMatt Jacob .name = G_MULTIPATH_CLASS_NAME, 82e770bc6bSMatt Jacob .version = G_VERSION, 83e770bc6bSMatt Jacob .ctlreq = g_multipath_config, 84e770bc6bSMatt Jacob .taste = g_multipath_taste, 85e770bc6bSMatt Jacob .destroy_geom = g_multipath_destroy_geom, 86e770bc6bSMatt Jacob .init = g_multipath_init, 87e770bc6bSMatt Jacob .fini = g_multipath_fini 88e770bc6bSMatt Jacob }; 89e770bc6bSMatt Jacob 90e770bc6bSMatt Jacob #define MP_BAD 0x1 91e770bc6bSMatt Jacob #define MP_POSTED 0x2 92e770bc6bSMatt Jacob 93e770bc6bSMatt Jacob static void 94e770bc6bSMatt Jacob g_mpd(void *arg, int flags __unused) 95e770bc6bSMatt Jacob { 96e770bc6bSMatt Jacob struct g_consumer *cp; 97e770bc6bSMatt Jacob 98e770bc6bSMatt Jacob g_topology_assert(); 99e770bc6bSMatt Jacob cp = arg; 10012f35a61SPawel Jakub Dawidek if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 101e770bc6bSMatt Jacob g_access(cp, -cp->acr, -cp->acw, -cp->ace); 102e770bc6bSMatt Jacob if (cp->provider) { 103e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s removed from %s\n", 104e770bc6bSMatt Jacob cp->provider->name, cp->geom->name); 105e770bc6bSMatt Jacob g_detach(cp); 106e770bc6bSMatt Jacob } 107e770bc6bSMatt Jacob g_destroy_consumer(cp); 108e770bc6bSMatt Jacob } 109e770bc6bSMatt Jacob 110e770bc6bSMatt Jacob static void 111e770bc6bSMatt Jacob g_multipath_orphan(struct g_consumer *cp) 112e770bc6bSMatt Jacob { 113e770bc6bSMatt Jacob if ((cp->index & MP_POSTED) == 0) { 114e770bc6bSMatt Jacob cp->index |= MP_POSTED; 115e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s orphaned in %s\n", 116e770bc6bSMatt Jacob cp->provider->name, cp->geom->name); 117e770bc6bSMatt Jacob g_mpd(cp, 0); 118e770bc6bSMatt Jacob } 119e770bc6bSMatt Jacob } 120e770bc6bSMatt Jacob 121e770bc6bSMatt Jacob static void 122e770bc6bSMatt Jacob g_multipath_start(struct bio *bp) 123e770bc6bSMatt Jacob { 124e770bc6bSMatt Jacob struct g_multipath_softc *sc; 125e770bc6bSMatt Jacob struct g_geom *gp; 126e770bc6bSMatt Jacob struct g_consumer *cp; 127e770bc6bSMatt Jacob struct bio *cbp; 128e770bc6bSMatt Jacob 129e770bc6bSMatt Jacob gp = bp->bio_to->geom; 130e770bc6bSMatt Jacob sc = gp->softc; 131e770bc6bSMatt Jacob KASSERT(sc != NULL, ("NULL sc")); 132e770bc6bSMatt Jacob cp = sc->cp_active; 133e770bc6bSMatt Jacob if (cp == NULL) { 134e770bc6bSMatt Jacob g_io_deliver(bp, ENXIO); 135e770bc6bSMatt Jacob return; 136e770bc6bSMatt Jacob } 137e770bc6bSMatt Jacob cbp = g_clone_bio(bp); 138e770bc6bSMatt Jacob if (cbp == NULL) { 139e770bc6bSMatt Jacob g_io_deliver(bp, ENOMEM); 140e770bc6bSMatt Jacob return; 141e770bc6bSMatt Jacob } 142e770bc6bSMatt Jacob cbp->bio_done = g_multipath_done; 143e770bc6bSMatt Jacob g_io_request(cbp, cp); 144e770bc6bSMatt Jacob } 145e770bc6bSMatt Jacob 146e770bc6bSMatt Jacob static void 147e770bc6bSMatt Jacob g_multipath_done(struct bio *bp) 148e770bc6bSMatt Jacob { 149e770bc6bSMatt Jacob if (bp->bio_error == ENXIO || bp->bio_error == EIO) { 150e770bc6bSMatt Jacob mtx_lock(&gmtbq_mtx); 151e770bc6bSMatt Jacob bioq_insert_tail(&gmtbq, bp); 152e770bc6bSMatt Jacob wakeup(&g_multipath_kt_state); 153e770bc6bSMatt Jacob mtx_unlock(&gmtbq_mtx); 154e770bc6bSMatt Jacob } else { 155e770bc6bSMatt Jacob g_std_done(bp); 156e770bc6bSMatt Jacob } 157e770bc6bSMatt Jacob } 158e770bc6bSMatt Jacob 159e770bc6bSMatt Jacob static void 160e770bc6bSMatt Jacob g_multipath_done_error(struct bio *bp) 161e770bc6bSMatt Jacob { 162e770bc6bSMatt Jacob struct bio *pbp; 163e770bc6bSMatt Jacob struct g_geom *gp; 164e770bc6bSMatt Jacob struct g_multipath_softc *sc; 165e770bc6bSMatt Jacob struct g_consumer *cp; 166e770bc6bSMatt Jacob struct g_provider *pp; 167e770bc6bSMatt Jacob 168e770bc6bSMatt Jacob /* 169e770bc6bSMatt Jacob * If we had a failure, we have to check first to see 170e770bc6bSMatt Jacob * whether the consumer it failed on was the currently 171e770bc6bSMatt Jacob * active consumer (i.e., this is the first in perhaps 172e770bc6bSMatt Jacob * a number of failures). If so, we then switch consumers 173e770bc6bSMatt Jacob * to the next available consumer. 174e770bc6bSMatt Jacob */ 175e770bc6bSMatt Jacob 176e770bc6bSMatt Jacob g_topology_lock(); 177e770bc6bSMatt Jacob pbp = bp->bio_parent; 178e770bc6bSMatt Jacob gp = pbp->bio_to->geom; 179e770bc6bSMatt Jacob sc = gp->softc; 180e770bc6bSMatt Jacob cp = bp->bio_from; 181e770bc6bSMatt Jacob pp = cp->provider; 182e770bc6bSMatt Jacob 183e770bc6bSMatt Jacob cp->index |= MP_BAD; 184e770bc6bSMatt Jacob if (cp->nend == cp->nstart && pp->nend == pp->nstart) { 185e770bc6bSMatt Jacob cp->index |= MP_POSTED; 186e770bc6bSMatt Jacob g_post_event(g_mpd, cp, M_NOWAIT, NULL); 187e770bc6bSMatt Jacob } 188e770bc6bSMatt Jacob if (cp == sc->cp_active) { 189e770bc6bSMatt Jacob struct g_consumer *lcp; 190e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s failed in %s\n", 191e770bc6bSMatt Jacob pp->name, sc->sc_name); 192e770bc6bSMatt Jacob sc->cp_active = NULL; 193e770bc6bSMatt Jacob LIST_FOREACH(lcp, &gp->consumer, consumer) { 194e770bc6bSMatt Jacob if ((lcp->index & MP_BAD) == 0) { 195e770bc6bSMatt Jacob sc->cp_active = lcp; 196e770bc6bSMatt Jacob break; 197e770bc6bSMatt Jacob } 198e770bc6bSMatt Jacob } 199e770bc6bSMatt Jacob if (sc->cp_active == NULL) { 200e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: out of providers for %s\n", 201e770bc6bSMatt Jacob sc->sc_name); 202c16ce31bSPawel Jakub Dawidek g_topology_unlock(); 203e770bc6bSMatt Jacob return; 204e770bc6bSMatt Jacob } else { 205e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s now active path in %s\n", 206e770bc6bSMatt Jacob sc->cp_active->provider->name, sc->sc_name); 207e770bc6bSMatt Jacob } 208e770bc6bSMatt Jacob } 209e770bc6bSMatt Jacob g_topology_unlock(); 210e770bc6bSMatt Jacob 211e770bc6bSMatt Jacob /* 212e770bc6bSMatt Jacob * If we can fruitfully restart the I/O, do so. 213e770bc6bSMatt Jacob */ 214e770bc6bSMatt Jacob if (sc->cp_active) { 215e770bc6bSMatt Jacob g_destroy_bio(bp); 216e770bc6bSMatt Jacob pbp->bio_children--; 217e770bc6bSMatt Jacob g_multipath_start(pbp); 218e770bc6bSMatt Jacob } else { 219e770bc6bSMatt Jacob g_std_done(bp); 220e770bc6bSMatt Jacob } 221e770bc6bSMatt Jacob } 222e770bc6bSMatt Jacob 223e770bc6bSMatt Jacob static void 224e770bc6bSMatt Jacob g_multipath_kt(void *arg) 225e770bc6bSMatt Jacob { 22612f35a61SPawel Jakub Dawidek 227e770bc6bSMatt Jacob g_multipath_kt_state = GKT_RUN; 228e770bc6bSMatt Jacob mtx_lock(&gmtbq_mtx); 229e770bc6bSMatt Jacob while (g_multipath_kt_state == GKT_RUN) { 230e770bc6bSMatt Jacob for (;;) { 231e770bc6bSMatt Jacob struct bio *bp; 23212f35a61SPawel Jakub Dawidek 233e770bc6bSMatt Jacob bp = bioq_takefirst(&gmtbq); 23412f35a61SPawel Jakub Dawidek if (bp == NULL) 235e770bc6bSMatt Jacob break; 236e770bc6bSMatt Jacob mtx_unlock(&gmtbq_mtx); 237e770bc6bSMatt Jacob g_multipath_done_error(bp); 238e770bc6bSMatt Jacob mtx_lock(&gmtbq_mtx); 239e770bc6bSMatt Jacob } 240e770bc6bSMatt Jacob msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 241e770bc6bSMatt Jacob "gkt:wait", hz / 10); 242e770bc6bSMatt Jacob } 243e770bc6bSMatt Jacob mtx_unlock(&gmtbq_mtx); 244e770bc6bSMatt Jacob wakeup(&g_multipath_kt_state); 2453745c395SJulian Elischer kproc_exit(0); 246e770bc6bSMatt Jacob } 247e770bc6bSMatt Jacob 248e770bc6bSMatt Jacob 249e770bc6bSMatt Jacob static int 250e770bc6bSMatt Jacob g_multipath_access(struct g_provider *pp, int dr, int dw, int de) 251e770bc6bSMatt Jacob { 252e770bc6bSMatt Jacob struct g_geom *gp; 253e770bc6bSMatt Jacob struct g_consumer *cp, *badcp = NULL; 254e770bc6bSMatt Jacob int error; 255e770bc6bSMatt Jacob 256e770bc6bSMatt Jacob gp = pp->geom; 257e770bc6bSMatt Jacob 258e770bc6bSMatt Jacob LIST_FOREACH(cp, &gp->consumer, consumer) { 259e770bc6bSMatt Jacob error = g_access(cp, dr, dw, de); 260e770bc6bSMatt Jacob if (error) { 261e770bc6bSMatt Jacob badcp = cp; 262e770bc6bSMatt Jacob goto fail; 263e770bc6bSMatt Jacob } 264e770bc6bSMatt Jacob } 265e770bc6bSMatt Jacob return (0); 266e770bc6bSMatt Jacob 267e770bc6bSMatt Jacob fail: 268e770bc6bSMatt Jacob LIST_FOREACH(cp, &gp->consumer, consumer) { 26912f35a61SPawel Jakub Dawidek if (cp == badcp) 270e770bc6bSMatt Jacob break; 271e770bc6bSMatt Jacob (void) g_access(cp, -dr, -dw, -de); 272e770bc6bSMatt Jacob } 273e770bc6bSMatt Jacob return (error); 274e770bc6bSMatt Jacob } 275e770bc6bSMatt Jacob 276e770bc6bSMatt Jacob static struct g_geom * 277e770bc6bSMatt Jacob g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md) 278e770bc6bSMatt Jacob { 279e770bc6bSMatt Jacob struct g_multipath_softc *sc; 280e770bc6bSMatt Jacob struct g_geom *gp; 281e770bc6bSMatt Jacob struct g_provider *pp; 282e770bc6bSMatt Jacob 283e770bc6bSMatt Jacob g_topology_assert(); 284e770bc6bSMatt Jacob 285e770bc6bSMatt Jacob LIST_FOREACH(gp, &mp->geom, geom) { 286e770bc6bSMatt Jacob if (strcmp(gp->name, md->md_name) == 0) { 287e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: name %s already exists\n", 288e770bc6bSMatt Jacob md->md_name); 289e770bc6bSMatt Jacob return (NULL); 290e770bc6bSMatt Jacob } 291e770bc6bSMatt Jacob } 292e770bc6bSMatt Jacob 293e770bc6bSMatt Jacob gp = g_new_geomf(mp, md->md_name); 29412f35a61SPawel Jakub Dawidek if (gp == NULL) 295e770bc6bSMatt Jacob goto fail; 296e770bc6bSMatt Jacob 297e770bc6bSMatt Jacob sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 298e770bc6bSMatt Jacob gp->softc = sc; 299e770bc6bSMatt Jacob gp->start = g_multipath_start; 300e770bc6bSMatt Jacob gp->orphan = g_multipath_orphan; 301e770bc6bSMatt Jacob gp->access = g_multipath_access; 302e770bc6bSMatt Jacob memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid)); 303e770bc6bSMatt Jacob memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name)); 304e770bc6bSMatt Jacob 305e770bc6bSMatt Jacob pp = g_new_providerf(gp, "multipath/%s", md->md_name); 30612f35a61SPawel Jakub Dawidek if (pp == NULL) 307e770bc6bSMatt Jacob goto fail; 308e770bc6bSMatt Jacob /* limit the provider to not have it stomp on metadata */ 309e770bc6bSMatt Jacob pp->mediasize = md->md_size - md->md_sectorsize; 310e770bc6bSMatt Jacob pp->sectorsize = md->md_sectorsize; 311e770bc6bSMatt Jacob sc->pp = pp; 312e770bc6bSMatt Jacob g_error_provider(pp, 0); 313e770bc6bSMatt Jacob return (gp); 314e770bc6bSMatt Jacob fail: 315e770bc6bSMatt Jacob if (gp != NULL) { 31612f35a61SPawel Jakub Dawidek if (gp->softc != NULL) 317e770bc6bSMatt Jacob g_free(gp->softc); 318e770bc6bSMatt Jacob g_destroy_geom(gp); 319e770bc6bSMatt Jacob } 320e770bc6bSMatt Jacob return (NULL); 321e770bc6bSMatt Jacob } 322e770bc6bSMatt Jacob 323e770bc6bSMatt Jacob static int 324e770bc6bSMatt Jacob g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp) 325e770bc6bSMatt Jacob { 326e770bc6bSMatt Jacob struct g_multipath_softc *sc; 327e770bc6bSMatt Jacob struct g_consumer *cp, *nxtcp; 328e770bc6bSMatt Jacob int error; 329e770bc6bSMatt Jacob 330e770bc6bSMatt Jacob g_topology_assert(); 331e770bc6bSMatt Jacob 332e770bc6bSMatt Jacob sc = gp->softc; 333e770bc6bSMatt Jacob KASSERT(sc, ("no softc")); 334e770bc6bSMatt Jacob 335e770bc6bSMatt Jacob /* 336e770bc6bSMatt Jacob * Make sure that the passed provider isn't already attached 337e770bc6bSMatt Jacob */ 338e770bc6bSMatt Jacob LIST_FOREACH(cp, &gp->consumer, consumer) { 33912f35a61SPawel Jakub Dawidek if (cp->provider == pp) 340e770bc6bSMatt Jacob break; 341e770bc6bSMatt Jacob } 342e770bc6bSMatt Jacob if (cp) { 343e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: provider %s already attached to %s\n", 344e770bc6bSMatt Jacob pp->name, gp->name); 345e770bc6bSMatt Jacob return (EEXIST); 346e770bc6bSMatt Jacob } 347e770bc6bSMatt Jacob nxtcp = LIST_FIRST(&gp->consumer); 348e770bc6bSMatt Jacob cp = g_new_consumer(gp); 34912f35a61SPawel Jakub Dawidek if (cp == NULL) 350e770bc6bSMatt Jacob return (ENOMEM); 351e770bc6bSMatt Jacob error = g_attach(cp, pp); 352e770bc6bSMatt Jacob if (error != 0) { 353e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: cannot attach %s to %s", 354e770bc6bSMatt Jacob pp->name, sc->sc_name); 355e770bc6bSMatt Jacob g_destroy_consumer(cp); 356e770bc6bSMatt Jacob return (error); 357e770bc6bSMatt Jacob } 358e770bc6bSMatt Jacob cp->private = sc; 359e770bc6bSMatt Jacob cp->index = 0; 360e770bc6bSMatt Jacob 361e770bc6bSMatt Jacob /* 362e770bc6bSMatt Jacob * Set access permissions on new consumer to match other consumers 363e770bc6bSMatt Jacob */ 364e770bc6bSMatt Jacob if (nxtcp && (nxtcp->acr + nxtcp->acw + nxtcp->ace)) { 365e770bc6bSMatt Jacob error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace); 366e770bc6bSMatt Jacob if (error) { 367e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: cannot set access in " 368e770bc6bSMatt Jacob "attaching %s to %s/%s (%d)\n", 369e770bc6bSMatt Jacob pp->name, sc->sc_name, sc->sc_uuid, error); 370e770bc6bSMatt Jacob g_detach(cp); 371e770bc6bSMatt Jacob g_destroy_consumer(cp); 372e770bc6bSMatt Jacob return (error); 373e770bc6bSMatt Jacob } 374e770bc6bSMatt Jacob } 375e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: adding %s to %s/%s\n", 376e770bc6bSMatt Jacob pp->name, sc->sc_name, sc->sc_uuid); 377e770bc6bSMatt Jacob if (sc->cp_active == NULL) { 378e770bc6bSMatt Jacob sc->cp_active = cp; 379e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s now active path in %s\n", 380e770bc6bSMatt Jacob pp->name, sc->sc_name); 381e770bc6bSMatt Jacob } 382e770bc6bSMatt Jacob return (0); 383e770bc6bSMatt Jacob } 384e770bc6bSMatt Jacob 385e770bc6bSMatt Jacob static int 386e770bc6bSMatt Jacob g_multipath_destroy(struct g_geom *gp) 387e770bc6bSMatt Jacob { 388e770bc6bSMatt Jacob struct g_provider *pp; 389e770bc6bSMatt Jacob 390e770bc6bSMatt Jacob g_topology_assert(); 39112f35a61SPawel Jakub Dawidek if (gp->softc == NULL) 392e770bc6bSMatt Jacob return (ENXIO); 393e770bc6bSMatt Jacob pp = LIST_FIRST(&gp->provider); 39412f35a61SPawel Jakub Dawidek if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) 395e770bc6bSMatt Jacob return (EBUSY); 396e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: destroying %s\n", gp->name); 397e770bc6bSMatt Jacob g_free(gp->softc); 398e770bc6bSMatt Jacob gp->softc = NULL; 399e770bc6bSMatt Jacob g_wither_geom(gp, ENXIO); 400e770bc6bSMatt Jacob return (0); 401e770bc6bSMatt Jacob } 402e770bc6bSMatt Jacob 403e770bc6bSMatt Jacob static int 404e770bc6bSMatt Jacob g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp, 405e770bc6bSMatt Jacob struct g_geom *gp) 406e770bc6bSMatt Jacob { 40712f35a61SPawel Jakub Dawidek 408e770bc6bSMatt Jacob return (g_multipath_destroy(gp)); 409e770bc6bSMatt Jacob } 410e770bc6bSMatt Jacob 411b5dce617SMatt Jacob static int 412b5dce617SMatt Jacob g_multipath_rotate(struct g_geom *gp) 413b5dce617SMatt Jacob { 414b5dce617SMatt Jacob struct g_consumer *lcp; 415b5dce617SMatt Jacob struct g_multipath_softc *sc = gp->softc; 416b5dce617SMatt Jacob 417b5dce617SMatt Jacob g_topology_assert(); 418b5dce617SMatt Jacob if (sc == NULL) 419b5dce617SMatt Jacob return (ENXIO); 420b5dce617SMatt Jacob LIST_FOREACH(lcp, &gp->consumer, consumer) { 421b5dce617SMatt Jacob if ((lcp->index & MP_BAD) == 0) { 422b5dce617SMatt Jacob if (sc->cp_active != lcp) { 423b5dce617SMatt Jacob break; 424b5dce617SMatt Jacob } 425b5dce617SMatt Jacob } 426b5dce617SMatt Jacob } 427b5dce617SMatt Jacob if (lcp) { 428b5dce617SMatt Jacob sc->cp_active = lcp; 429b5dce617SMatt Jacob printf("GEOM_MULTIPATH: %s now active path in %s\n", 430b5dce617SMatt Jacob lcp->provider->name, sc->sc_name); 431b5dce617SMatt Jacob } 432b5dce617SMatt Jacob return (0); 433b5dce617SMatt Jacob } 434b5dce617SMatt Jacob 435e770bc6bSMatt Jacob static void 436e770bc6bSMatt Jacob g_multipath_init(struct g_class *mp) 437e770bc6bSMatt Jacob { 438e770bc6bSMatt Jacob bioq_init(&gmtbq); 439e770bc6bSMatt Jacob mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF); 44012f35a61SPawel Jakub Dawidek if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0) 441e770bc6bSMatt Jacob g_multipath_kt_state = GKT_RUN; 442e770bc6bSMatt Jacob } 443e770bc6bSMatt Jacob 444e770bc6bSMatt Jacob static void 445e770bc6bSMatt Jacob g_multipath_fini(struct g_class *mp) 446e770bc6bSMatt Jacob { 447e770bc6bSMatt Jacob if (g_multipath_kt_state == GKT_RUN) { 448e770bc6bSMatt Jacob mtx_lock(&gmtbq_mtx); 449e770bc6bSMatt Jacob g_multipath_kt_state = GKT_DIE; 450e770bc6bSMatt Jacob wakeup(&g_multipath_kt_state); 451e770bc6bSMatt Jacob msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 452e770bc6bSMatt Jacob "gmp:fini", 0); 453e770bc6bSMatt Jacob mtx_unlock(&gmtbq_mtx); 454e770bc6bSMatt Jacob } 455e770bc6bSMatt Jacob } 456e770bc6bSMatt Jacob 457e770bc6bSMatt Jacob static int 458e770bc6bSMatt Jacob g_multipath_read_metadata(struct g_consumer *cp, 459e770bc6bSMatt Jacob struct g_multipath_metadata *md) 460e770bc6bSMatt Jacob { 461e770bc6bSMatt Jacob struct g_provider *pp; 462e770bc6bSMatt Jacob u_char *buf; 463e770bc6bSMatt Jacob int error; 464e770bc6bSMatt Jacob 465e770bc6bSMatt Jacob g_topology_assert(); 466e770bc6bSMatt Jacob error = g_access(cp, 1, 0, 0); 46712f35a61SPawel Jakub Dawidek if (error != 0) 468e770bc6bSMatt Jacob return (error); 469e770bc6bSMatt Jacob pp = cp->provider; 470e770bc6bSMatt Jacob g_topology_unlock(); 471e770bc6bSMatt Jacob buf = g_read_data(cp, pp->mediasize - pp->sectorsize, 472e770bc6bSMatt Jacob pp->sectorsize, &error); 473e770bc6bSMatt Jacob g_topology_lock(); 474e770bc6bSMatt Jacob g_access(cp, -1, 0, 0); 47512f35a61SPawel Jakub Dawidek if (buf == NULL) 476e770bc6bSMatt Jacob return (error); 477e770bc6bSMatt Jacob multipath_metadata_decode(buf, md); 478e770bc6bSMatt Jacob g_free(buf); 479e770bc6bSMatt Jacob return (0); 480e770bc6bSMatt Jacob } 481e770bc6bSMatt Jacob 482e770bc6bSMatt Jacob static struct g_geom * 483e770bc6bSMatt Jacob g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 484e770bc6bSMatt Jacob { 485e770bc6bSMatt Jacob struct g_multipath_metadata md; 486e770bc6bSMatt Jacob struct g_multipath_softc *sc; 487e770bc6bSMatt Jacob struct g_consumer *cp; 488e770bc6bSMatt Jacob struct g_geom *gp, *gp1; 489e770bc6bSMatt Jacob int error, isnew; 490e770bc6bSMatt Jacob 491e770bc6bSMatt Jacob g_topology_assert(); 492e770bc6bSMatt Jacob 493e770bc6bSMatt Jacob gp = g_new_geomf(mp, "multipath:taste"); 494e770bc6bSMatt Jacob gp->start = g_multipath_start; 495e770bc6bSMatt Jacob gp->access = g_multipath_access; 496e770bc6bSMatt Jacob gp->orphan = g_multipath_orphan; 497e770bc6bSMatt Jacob cp = g_new_consumer(gp); 498e770bc6bSMatt Jacob g_attach(cp, pp); 499e770bc6bSMatt Jacob error = g_multipath_read_metadata(cp, &md); 500e770bc6bSMatt Jacob g_detach(cp); 501e770bc6bSMatt Jacob g_destroy_consumer(cp); 502e770bc6bSMatt Jacob g_destroy_geom(gp); 50312f35a61SPawel Jakub Dawidek if (error != 0) 504e770bc6bSMatt Jacob return (NULL); 505e770bc6bSMatt Jacob gp = NULL; 506e770bc6bSMatt Jacob 507e770bc6bSMatt Jacob if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) { 50812f35a61SPawel Jakub Dawidek if (g_multipath_debug) 509e770bc6bSMatt Jacob printf("%s is not MULTIPATH\n", pp->name); 510e770bc6bSMatt Jacob return (NULL); 511e770bc6bSMatt Jacob } 512e770bc6bSMatt Jacob if (md.md_version != G_MULTIPATH_VERSION) { 513e770bc6bSMatt Jacob printf("%s has version %d multipath id- this module is version " 514e770bc6bSMatt Jacob " %d: rejecting\n", pp->name, md.md_version, 515e770bc6bSMatt Jacob G_MULTIPATH_VERSION); 516e770bc6bSMatt Jacob return (NULL); 517e770bc6bSMatt Jacob } 51812f35a61SPawel Jakub Dawidek if (g_multipath_debug) 519e770bc6bSMatt Jacob printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid); 520e770bc6bSMatt Jacob 521e770bc6bSMatt Jacob /* 522e770bc6bSMatt Jacob * Let's check if such a device already is present. We check against 523e770bc6bSMatt Jacob * uuid alone first because that's the true distinguishor. If that 524e770bc6bSMatt Jacob * passes, then we check for name conflicts. If there are conflicts, 525e770bc6bSMatt Jacob * modify the name. 526e770bc6bSMatt Jacob * 527e770bc6bSMatt Jacob * The whole purpose of this is to solve the problem that people don't 528e770bc6bSMatt Jacob * pick good unique names, but good unique names (like uuids) are a 529e770bc6bSMatt Jacob * pain to use. So, we allow people to build GEOMs with friendly names 530e770bc6bSMatt Jacob * and uuids, and modify the names in case there's a collision. 531e770bc6bSMatt Jacob */ 532e770bc6bSMatt Jacob sc = NULL; 533e770bc6bSMatt Jacob LIST_FOREACH(gp, &mp->geom, geom) { 534e770bc6bSMatt Jacob sc = gp->softc; 53512f35a61SPawel Jakub Dawidek if (sc == NULL) 536e770bc6bSMatt Jacob continue; 53712f35a61SPawel Jakub Dawidek if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0) 538e770bc6bSMatt Jacob break; 539e770bc6bSMatt Jacob } 540e770bc6bSMatt Jacob 541e770bc6bSMatt Jacob LIST_FOREACH(gp1, &mp->geom, geom) { 54212f35a61SPawel Jakub Dawidek if (gp1 == gp) 543e770bc6bSMatt Jacob continue; 544e770bc6bSMatt Jacob sc = gp1->softc; 54512f35a61SPawel Jakub Dawidek if (sc == NULL) 546e770bc6bSMatt Jacob continue; 54712f35a61SPawel Jakub Dawidek if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0) 548e770bc6bSMatt Jacob break; 549e770bc6bSMatt Jacob } 550e770bc6bSMatt Jacob 551e770bc6bSMatt Jacob /* 552e770bc6bSMatt Jacob * If gp is NULL, we had no extant MULTIPATH geom with this uuid. 553e770bc6bSMatt Jacob * 554e770bc6bSMatt Jacob * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant 555e770bc6bSMatt Jacob * with the same name (but a different UUID). 556e770bc6bSMatt Jacob * 557e770bc6bSMatt Jacob * If gp is NULL, then modify the name with a random number and 558e770bc6bSMatt Jacob * complain, but allow the creation of the geom to continue. 559e770bc6bSMatt Jacob * 560e770bc6bSMatt Jacob * If gp is *not* NULL, just use the geom's name as we're attaching 561e770bc6bSMatt Jacob * this disk to the (previously generated) name. 562e770bc6bSMatt Jacob */ 563e770bc6bSMatt Jacob 564e770bc6bSMatt Jacob if (gp1) { 565e770bc6bSMatt Jacob sc = gp1->softc; 566e770bc6bSMatt Jacob if (gp == NULL) { 567e770bc6bSMatt Jacob char buf[16]; 568e770bc6bSMatt Jacob u_long rand = random(); 569e770bc6bSMatt Jacob 570e770bc6bSMatt Jacob snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand); 571e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: geom %s/%s exists already\n", 572e770bc6bSMatt Jacob sc->sc_name, sc->sc_uuid); 573e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n", 574e770bc6bSMatt Jacob md.md_uuid, buf); 575e770bc6bSMatt Jacob strlcpy(md.md_name, buf, sizeof(md.md_name)); 576e770bc6bSMatt Jacob } else { 577e770bc6bSMatt Jacob strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name)); 578e770bc6bSMatt Jacob } 579e770bc6bSMatt Jacob } 580e770bc6bSMatt Jacob 581e770bc6bSMatt Jacob if (gp == NULL) { 582e770bc6bSMatt Jacob gp = g_multipath_create(mp, &md); 583e770bc6bSMatt Jacob if (gp == NULL) { 584e770bc6bSMatt Jacob printf("GEOM_MULTIPATH: cannot create geom %s/%s\n", 585e770bc6bSMatt Jacob md.md_name, md.md_uuid); 586e770bc6bSMatt Jacob return (NULL); 587e770bc6bSMatt Jacob } 588e770bc6bSMatt Jacob isnew = 1; 589e770bc6bSMatt Jacob } else { 590e770bc6bSMatt Jacob isnew = 0; 591e770bc6bSMatt Jacob } 592e770bc6bSMatt Jacob 593e770bc6bSMatt Jacob sc = gp->softc; 594e770bc6bSMatt Jacob KASSERT(sc != NULL, ("sc is NULL")); 595e770bc6bSMatt Jacob error = g_multipath_add_disk(gp, pp); 596e770bc6bSMatt Jacob if (error != 0) { 59712f35a61SPawel Jakub Dawidek if (isnew) 598e770bc6bSMatt Jacob g_multipath_destroy(gp); 599e770bc6bSMatt Jacob return (NULL); 600e770bc6bSMatt Jacob } 601e770bc6bSMatt Jacob return (gp); 602e770bc6bSMatt Jacob } 603e770bc6bSMatt Jacob 604e770bc6bSMatt Jacob static void 605e770bc6bSMatt Jacob g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp) 606e770bc6bSMatt Jacob { 607e770bc6bSMatt Jacob struct g_geom *gp; 608e770bc6bSMatt Jacob struct g_provider *pp0, *pp1; 609e770bc6bSMatt Jacob struct g_multipath_metadata md; 610e770bc6bSMatt Jacob const char *name, *mpname, *uuid; 611e770bc6bSMatt Jacob static const char devpf[6] = "/dev/"; 612e770bc6bSMatt Jacob int *nargs, error; 613e770bc6bSMatt Jacob 614e770bc6bSMatt Jacob g_topology_assert(); 615e770bc6bSMatt Jacob 616e770bc6bSMatt Jacob mpname = gctl_get_asciiparam(req, "arg0"); 617e770bc6bSMatt Jacob if (mpname == NULL) { 618e770bc6bSMatt Jacob gctl_error(req, "No 'arg0' argument"); 619e770bc6bSMatt Jacob return; 620e770bc6bSMatt Jacob } 621e770bc6bSMatt Jacob 622e770bc6bSMatt Jacob nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 623e770bc6bSMatt Jacob if (nargs == NULL) { 624e770bc6bSMatt Jacob gctl_error(req, "No 'nargs' argument"); 625e770bc6bSMatt Jacob return; 626e770bc6bSMatt Jacob } 627e770bc6bSMatt Jacob if (*nargs != 4) { 628e770bc6bSMatt Jacob gctl_error(req, "missing device or uuid arguments"); 629e770bc6bSMatt Jacob return; 630e770bc6bSMatt Jacob } 631e770bc6bSMatt Jacob 632e770bc6bSMatt Jacob name = gctl_get_asciiparam(req, "arg1"); 633e770bc6bSMatt Jacob if (name == NULL) { 634e770bc6bSMatt Jacob gctl_error(req, "No 'arg1' argument"); 635e770bc6bSMatt Jacob return; 636e770bc6bSMatt Jacob } 63712f35a61SPawel Jakub Dawidek if (strncmp(name, devpf, 5) == 0) 638e770bc6bSMatt Jacob name += 5; 639e770bc6bSMatt Jacob pp0 = g_provider_by_name(name); 640e770bc6bSMatt Jacob if (pp0 == NULL) { 641e770bc6bSMatt Jacob gctl_error(req, "Provider %s is invalid", name); 642e770bc6bSMatt Jacob return; 643e770bc6bSMatt Jacob } 644e770bc6bSMatt Jacob 645e770bc6bSMatt Jacob name = gctl_get_asciiparam(req, "arg2"); 646e770bc6bSMatt Jacob if (name == NULL) { 647e770bc6bSMatt Jacob gctl_error(req, "No 'arg2' argument"); 648e770bc6bSMatt Jacob return; 649e770bc6bSMatt Jacob } 65012f35a61SPawel Jakub Dawidek if (strncmp(name, devpf, 5) == 0) 651e770bc6bSMatt Jacob name += 5; 652e770bc6bSMatt Jacob pp1 = g_provider_by_name(name); 653e770bc6bSMatt Jacob if (pp1 == NULL) { 654e770bc6bSMatt Jacob gctl_error(req, "Provider %s is invalid", name); 655e770bc6bSMatt Jacob return; 656e770bc6bSMatt Jacob } 657e770bc6bSMatt Jacob 658e770bc6bSMatt Jacob uuid = gctl_get_asciiparam(req, "arg3"); 659e770bc6bSMatt Jacob if (uuid == NULL) { 660e770bc6bSMatt Jacob gctl_error(req, "No uuid argument"); 661e770bc6bSMatt Jacob return; 662e770bc6bSMatt Jacob } 663e770bc6bSMatt Jacob if (strlen(uuid) != 36) { 664e770bc6bSMatt Jacob gctl_error(req, "Malformed uuid argument"); 665e770bc6bSMatt Jacob return; 666e770bc6bSMatt Jacob } 667e770bc6bSMatt Jacob 668e770bc6bSMatt Jacob /* 669e770bc6bSMatt Jacob * Check to make sure parameters from the two providers are the same 670e770bc6bSMatt Jacob */ 671e770bc6bSMatt Jacob if (pp0 == pp1) { 672e770bc6bSMatt Jacob gctl_error(req, "providers %s and %s are the same", 673e770bc6bSMatt Jacob pp0->name, pp1->name); 674e770bc6bSMatt Jacob return; 675e770bc6bSMatt Jacob } 676e770bc6bSMatt Jacob if (pp0->mediasize != pp1->mediasize) { 677e770bc6bSMatt Jacob gctl_error(req, "Provider %s is %jd; Provider %s is %jd", 678e770bc6bSMatt Jacob pp0->name, (intmax_t) pp0->mediasize, 679e770bc6bSMatt Jacob pp1->name, (intmax_t) pp1->mediasize); 680e770bc6bSMatt Jacob return; 681e770bc6bSMatt Jacob } 682e770bc6bSMatt Jacob if (pp0->sectorsize != pp1->sectorsize) { 683e770bc6bSMatt Jacob gctl_error(req, "Provider %s has sectorsize %u; Provider %s " 684e770bc6bSMatt Jacob "has sectorsize %u", pp0->name, pp0->sectorsize, 685e770bc6bSMatt Jacob pp1->name, pp1->sectorsize); 686e770bc6bSMatt Jacob return; 687e770bc6bSMatt Jacob } 688e770bc6bSMatt Jacob 689e770bc6bSMatt Jacob /* 690e770bc6bSMatt Jacob * cons up enough of a metadata structure to use. 691e770bc6bSMatt Jacob */ 692e770bc6bSMatt Jacob memset(&md, 0, sizeof(md)); 693e770bc6bSMatt Jacob md.md_size = pp0->mediasize; 694e770bc6bSMatt Jacob md.md_sectorsize = pp0->sectorsize; 69512f35a61SPawel Jakub Dawidek strlcpy(md.md_name, mpname, sizeof(md.md_name)); 69612f35a61SPawel Jakub Dawidek strlcpy(md.md_uuid, uuid, sizeof(md.md_uuid)); 697e770bc6bSMatt Jacob 698e770bc6bSMatt Jacob gp = g_multipath_create(mp, &md); 69912f35a61SPawel Jakub Dawidek if (gp == NULL) 700e770bc6bSMatt Jacob return; 701e770bc6bSMatt Jacob error = g_multipath_add_disk(gp, pp0); 702e770bc6bSMatt Jacob if (error) { 703e770bc6bSMatt Jacob g_multipath_destroy(gp); 704e770bc6bSMatt Jacob return; 705e770bc6bSMatt Jacob } 706e770bc6bSMatt Jacob error = g_multipath_add_disk(gp, pp1); 707e770bc6bSMatt Jacob if (error) { 708e770bc6bSMatt Jacob g_multipath_destroy(gp); 709e770bc6bSMatt Jacob return; 710e770bc6bSMatt Jacob } 711e770bc6bSMatt Jacob } 712e770bc6bSMatt Jacob 713e770bc6bSMatt Jacob static struct g_geom * 714e770bc6bSMatt Jacob g_multipath_find_geom(struct g_class *mp, const char *name) 715e770bc6bSMatt Jacob { 716e770bc6bSMatt Jacob struct g_geom *gp; 717e770bc6bSMatt Jacob 718e770bc6bSMatt Jacob LIST_FOREACH(gp, &mp->geom, geom) { 719e770bc6bSMatt Jacob if (strcmp(gp->name, name) == 0) { 720e770bc6bSMatt Jacob return (gp); 721e770bc6bSMatt Jacob } 722e770bc6bSMatt Jacob } 723e770bc6bSMatt Jacob return (NULL); 724e770bc6bSMatt Jacob } 725e770bc6bSMatt Jacob 726e770bc6bSMatt Jacob static void 727e770bc6bSMatt Jacob g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp) 728e770bc6bSMatt Jacob { 729e770bc6bSMatt Jacob struct g_geom *gp; 730e770bc6bSMatt Jacob const char *name; 731e770bc6bSMatt Jacob int error; 732e770bc6bSMatt Jacob 733e770bc6bSMatt Jacob g_topology_assert(); 734e770bc6bSMatt Jacob 735e770bc6bSMatt Jacob name = gctl_get_asciiparam(req, "arg0"); 736e770bc6bSMatt Jacob if (name == NULL) { 737e770bc6bSMatt Jacob gctl_error(req, "No 'arg0' argument"); 738e770bc6bSMatt Jacob return; 739e770bc6bSMatt Jacob } 740e770bc6bSMatt Jacob gp = g_multipath_find_geom(mp, name); 741e770bc6bSMatt Jacob if (gp == NULL) { 742e770bc6bSMatt Jacob gctl_error(req, "Device %s is invalid", name); 743e770bc6bSMatt Jacob return; 744e770bc6bSMatt Jacob } 745e770bc6bSMatt Jacob error = g_multipath_destroy(gp); 746e770bc6bSMatt Jacob if (error != 0) { 747e770bc6bSMatt Jacob gctl_error(req, "failed to destroy %s (err=%d)", name, error); 748e770bc6bSMatt Jacob } 749e770bc6bSMatt Jacob } 750e770bc6bSMatt Jacob 751e770bc6bSMatt Jacob static void 752b5dce617SMatt Jacob g_multipath_ctl_rotate(struct gctl_req *req, struct g_class *mp) 753b5dce617SMatt Jacob { 754b5dce617SMatt Jacob struct g_geom *gp; 755b5dce617SMatt Jacob const char *name; 756b5dce617SMatt Jacob int error; 757b5dce617SMatt Jacob 758b5dce617SMatt Jacob g_topology_assert(); 759b5dce617SMatt Jacob 760b5dce617SMatt Jacob name = gctl_get_asciiparam(req, "arg0"); 761b5dce617SMatt Jacob if (name == NULL) { 762b5dce617SMatt Jacob gctl_error(req, "No 'arg0' argument"); 763b5dce617SMatt Jacob return; 764b5dce617SMatt Jacob } 765b5dce617SMatt Jacob gp = g_multipath_find_geom(mp, name); 766b5dce617SMatt Jacob if (gp == NULL) { 767b5dce617SMatt Jacob gctl_error(req, "Device %s is invalid", name); 768b5dce617SMatt Jacob return; 769b5dce617SMatt Jacob } 770b5dce617SMatt Jacob error = g_multipath_rotate(gp); 771b5dce617SMatt Jacob if (error != 0) { 772b5dce617SMatt Jacob gctl_error(req, "failed to rotate %s (err=%d)", name, error); 773b5dce617SMatt Jacob } 774b5dce617SMatt Jacob } 775b5dce617SMatt Jacob 776b5dce617SMatt Jacob static void 777b5dce617SMatt Jacob g_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp) 778b5dce617SMatt Jacob { 779b5dce617SMatt Jacob struct sbuf *sb; 780b5dce617SMatt Jacob struct g_geom *gp; 781b5dce617SMatt Jacob struct g_multipath_softc *sc; 782b5dce617SMatt Jacob const char *name; 783b5dce617SMatt Jacob 784b5dce617SMatt Jacob sb = sbuf_new_auto(); 785b5dce617SMatt Jacob 786b5dce617SMatt Jacob g_topology_assert(); 787b5dce617SMatt Jacob name = gctl_get_asciiparam(req, "arg0"); 788b5dce617SMatt Jacob if (name == NULL) { 789b5dce617SMatt Jacob gctl_error(req, "No 'arg0' argument"); 790b5dce617SMatt Jacob return; 791b5dce617SMatt Jacob } 792b5dce617SMatt Jacob gp = g_multipath_find_geom(mp, name); 793b5dce617SMatt Jacob if (gp == NULL) { 794b5dce617SMatt Jacob gctl_error(req, "Device %s is invalid", name); 795b5dce617SMatt Jacob return; 796b5dce617SMatt Jacob } 797b5dce617SMatt Jacob sc = gp->softc; 798b5dce617SMatt Jacob if (sc->cp_active) { 799b5dce617SMatt Jacob sbuf_printf(sb, "%s\n", sc->cp_active->provider->name); 800b5dce617SMatt Jacob } else { 801b5dce617SMatt Jacob sbuf_printf(sb, "none\n"); 802b5dce617SMatt Jacob } 803b5dce617SMatt Jacob sbuf_finish(sb); 804b5dce617SMatt Jacob gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 805b5dce617SMatt Jacob sbuf_delete(sb); 806b5dce617SMatt Jacob } 807b5dce617SMatt Jacob 808b5dce617SMatt Jacob static void 809e770bc6bSMatt Jacob g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb) 810e770bc6bSMatt Jacob { 811e770bc6bSMatt Jacob uint32_t *version; 812e770bc6bSMatt Jacob g_topology_assert(); 813e770bc6bSMatt Jacob version = gctl_get_paraml(req, "version", sizeof(*version)); 814e770bc6bSMatt Jacob if (version == NULL) { 815e770bc6bSMatt Jacob gctl_error(req, "No 'version' argument"); 816e770bc6bSMatt Jacob } else if (*version != G_MULTIPATH_VERSION) { 817e770bc6bSMatt Jacob gctl_error(req, "Userland and kernel parts are out of sync"); 818e770bc6bSMatt Jacob } else if (strcmp(verb, "create") == 0) { 819e770bc6bSMatt Jacob g_multipath_ctl_create(req, mp); 820e770bc6bSMatt Jacob } else if (strcmp(verb, "destroy") == 0) { 821e770bc6bSMatt Jacob g_multipath_ctl_destroy(req, mp); 822b5dce617SMatt Jacob } else if (strcmp(verb, "rotate") == 0) { 823b5dce617SMatt Jacob g_multipath_ctl_rotate(req, mp); 824b5dce617SMatt Jacob } else if (strcmp(verb, "getactive") == 0) { 825b5dce617SMatt Jacob g_multipath_ctl_getactive(req, mp); 826e770bc6bSMatt Jacob } else { 827e770bc6bSMatt Jacob gctl_error(req, "Unknown verb %s", verb); 828e770bc6bSMatt Jacob } 829e770bc6bSMatt Jacob } 830e770bc6bSMatt Jacob DECLARE_GEOM_CLASS(g_multipath_class, g_multipath); 831