1167050Smjacob/*- 2260478Smav * Copyright (c) 2011-2013 Alexander Motin <mav@FreeBSD.org> 3167050Smjacob * Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org> 4167050Smjacob * All rights reserved. 5167050Smjacob * 6167050Smjacob * Redistribution and use in source and binary forms, with or without 7167050Smjacob * modification, are permitted provided that the following conditions 8167050Smjacob * are met: 9167050Smjacob * 1. Redistributions of source code must retain the above copyright 10167050Smjacob * notice, this list of conditions and the following disclaimer. 11167050Smjacob * 2. Redistributions in binary form must reproduce the above copyright 12167050Smjacob * notice, this list of conditions and the following disclaimer in the 13167050Smjacob * documentation and/or other materials provided with the distribution. 14167050Smjacob * 15167050Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16167050Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17167050Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18167050Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19167050Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20167050Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21167050Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22167050Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23167050Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24167050Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25167050Smjacob * SUCH DAMAGE. 26167050Smjacob */ 27167050Smjacob/* 28167050Smjacob * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the 29167050Smjacob * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM 30167050Smjacob * itself, all of which is most gratefully acknowledged. 31167050Smjacob */ 32167050Smjacob 33167050Smjacob#include <sys/cdefs.h> 34167050Smjacob__FBSDID("$FreeBSD: releng/10.3/sys/geom/multipath/g_multipath.c 294709 2016-01-25 09:29:29Z smh $"); 35167050Smjacob#include <sys/param.h> 36167050Smjacob#include <sys/systm.h> 37167050Smjacob#include <sys/kernel.h> 38167050Smjacob#include <sys/module.h> 39260478Smav#include <sys/limits.h> 40167050Smjacob#include <sys/lock.h> 41167050Smjacob#include <sys/mutex.h> 42167050Smjacob#include <sys/bio.h> 43223921Sae#include <sys/sbuf.h> 44167050Smjacob#include <sys/sysctl.h> 45167050Smjacob#include <sys/kthread.h> 46167050Smjacob#include <sys/malloc.h> 47167050Smjacob#include <geom/geom.h> 48167050Smjacob#include <geom/multipath/g_multipath.h> 49167050Smjacob 50219029SnetchildFEATURE(geom_multipath, "GEOM multipath support"); 51167050Smjacob 52167050SmjacobSYSCTL_DECL(_kern_geom); 53227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0, 54167050Smjacob "GEOM_MULTIPATH tunables"); 55167050Smjacobstatic u_int g_multipath_debug = 0; 56167050SmjacobSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW, 57167050Smjacob &g_multipath_debug, 0, "Debug level"); 58227464Smavstatic u_int g_multipath_exclusive = 1; 59227464SmavSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, exclusive, CTLFLAG_RW, 60227464Smav &g_multipath_exclusive, 0, "Exclusively open providers"); 61167050Smjacob 62167050Smjacobstatic enum { 63167050Smjacob GKT_NIL, 64167050Smjacob GKT_RUN, 65167050Smjacob GKT_DIE 66167050Smjacob} g_multipath_kt_state; 67167050Smjacobstatic struct bio_queue_head gmtbq; 68167050Smjacobstatic struct mtx gmtbq_mtx; 69167050Smjacob 70260478Smavstatic int g_multipath_read_metadata(struct g_consumer *cp, 71260478Smav struct g_multipath_metadata *md); 72260478Smavstatic int g_multipath_write_metadata(struct g_consumer *cp, 73260478Smav struct g_multipath_metadata *md); 74260478Smav 75167050Smjacobstatic void g_multipath_orphan(struct g_consumer *); 76260478Smavstatic void g_multipath_resize(struct g_consumer *); 77167050Smjacobstatic void g_multipath_start(struct bio *); 78167050Smjacobstatic void g_multipath_done(struct bio *); 79167050Smjacobstatic void g_multipath_done_error(struct bio *); 80167050Smjacobstatic void g_multipath_kt(void *); 81167050Smjacob 82167050Smjacobstatic int g_multipath_destroy(struct g_geom *); 83167050Smjacobstatic int 84167050Smjacobg_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *); 85167050Smjacob 86205847Smjacobstatic struct g_geom *g_multipath_find_geom(struct g_class *, const char *); 87205412Smjacobstatic int g_multipath_rotate(struct g_geom *); 88205412Smjacob 89167050Smjacobstatic g_taste_t g_multipath_taste; 90167050Smjacobstatic g_ctl_req_t g_multipath_config; 91167050Smjacobstatic g_init_t g_multipath_init; 92167050Smjacobstatic g_fini_t g_multipath_fini; 93227464Smavstatic g_dumpconf_t g_multipath_dumpconf; 94167050Smjacob 95167050Smjacobstruct g_class g_multipath_class = { 96167050Smjacob .name = G_MULTIPATH_CLASS_NAME, 97167050Smjacob .version = G_VERSION, 98167050Smjacob .ctlreq = g_multipath_config, 99167050Smjacob .taste = g_multipath_taste, 100167050Smjacob .destroy_geom = g_multipath_destroy_geom, 101167050Smjacob .init = g_multipath_init, 102167050Smjacob .fini = g_multipath_fini 103167050Smjacob}; 104167050Smjacob 105227464Smav#define MP_FAIL 0x00000001 106227464Smav#define MP_LOST 0x00000002 107227464Smav#define MP_NEW 0x00000004 108227464Smav#define MP_POSTED 0x00000008 109227464Smav#define MP_BAD (MP_FAIL | MP_LOST | MP_NEW) 110294709Ssmh#define MP_WITHER 0x00000010 111294709Ssmh#define MP_IDLE 0x00000020 112294709Ssmh#define MP_IDLE_MASK 0xffffffe0 113167050Smjacob 114227464Smavstatic int 115227464Smavg_multipath_good(struct g_geom *gp) 116227464Smav{ 117227464Smav struct g_consumer *cp; 118227464Smav int n = 0; 119227464Smav 120227464Smav LIST_FOREACH(cp, &gp->consumer, consumer) { 121227464Smav if ((cp->index & MP_BAD) == 0) 122227464Smav n++; 123227464Smav } 124227464Smav return (n); 125227464Smav} 126227464Smav 127167050Smjacobstatic void 128227464Smavg_multipath_fault(struct g_consumer *cp, int cause) 129227464Smav{ 130227464Smav struct g_multipath_softc *sc; 131227464Smav struct g_consumer *lcp; 132227464Smav struct g_geom *gp; 133227464Smav 134227464Smav gp = cp->geom; 135227464Smav sc = gp->softc; 136227464Smav cp->index |= cause; 137227464Smav if (g_multipath_good(gp) == 0 && sc->sc_ndisks > 0) { 138227464Smav LIST_FOREACH(lcp, &gp->consumer, consumer) { 139227464Smav if (lcp->provider == NULL || 140227464Smav (lcp->index & (MP_LOST | MP_NEW))) 141227464Smav continue; 142227464Smav if (sc->sc_ndisks > 1 && lcp == cp) 143227464Smav continue; 144227464Smav printf("GEOM_MULTIPATH: " 145227464Smav "all paths in %s were marked FAIL, restore %s\n", 146227464Smav sc->sc_name, lcp->provider->name); 147227464Smav lcp->index &= ~MP_FAIL; 148227464Smav } 149227464Smav } 150227464Smav if (cp != sc->sc_active) 151227464Smav return; 152227464Smav sc->sc_active = NULL; 153227464Smav LIST_FOREACH(lcp, &gp->consumer, consumer) { 154227464Smav if ((lcp->index & MP_BAD) == 0) { 155227464Smav sc->sc_active = lcp; 156227464Smav break; 157227464Smav } 158227464Smav } 159227464Smav if (sc->sc_active == NULL) { 160227464Smav printf("GEOM_MULTIPATH: out of providers for %s\n", 161227464Smav sc->sc_name); 162234415Smav } else if (sc->sc_active_active != 1) { 163227464Smav printf("GEOM_MULTIPATH: %s is now active path in %s\n", 164227464Smav sc->sc_active->provider->name, sc->sc_name); 165227464Smav } 166227464Smav} 167227464Smav 168227464Smavstatic struct g_consumer * 169234415Smavg_multipath_choose(struct g_geom *gp, struct bio *bp) 170227464Smav{ 171227464Smav struct g_multipath_softc *sc; 172227464Smav struct g_consumer *best, *cp; 173227464Smav 174227464Smav sc = gp->softc; 175234415Smav if (sc->sc_active_active == 0 || 176234415Smav (sc->sc_active_active == 2 && bp->bio_cmd != BIO_READ)) 177227464Smav return (sc->sc_active); 178227464Smav best = NULL; 179227464Smav LIST_FOREACH(cp, &gp->consumer, consumer) { 180227464Smav if (cp->index & MP_BAD) 181227464Smav continue; 182227464Smav cp->index += MP_IDLE; 183227464Smav if (best == NULL || cp->private < best->private || 184227464Smav (cp->private == best->private && cp->index > best->index)) 185227464Smav best = cp; 186227464Smav } 187227464Smav if (best != NULL) 188227464Smav best->index &= ~MP_IDLE_MASK; 189227464Smav return (best); 190227464Smav} 191227464Smav 192227464Smavstatic void 193167050Smjacobg_mpd(void *arg, int flags __unused) 194167050Smjacob{ 195227464Smav struct g_geom *gp; 196227464Smav struct g_multipath_softc *sc; 197167050Smjacob struct g_consumer *cp; 198227464Smav int w; 199167050Smjacob 200167050Smjacob g_topology_assert(); 201167050Smjacob cp = arg; 202227464Smav gp = cp->geom; 203227464Smav if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) { 204227464Smav w = cp->acw; 205167050Smjacob g_access(cp, -cp->acr, -cp->acw, -cp->ace); 206227464Smav if (w > 0 && cp->provider != NULL && 207227464Smav (cp->provider->geom->flags & G_GEOM_WITHER) == 0) { 208294709Ssmh cp->index |= MP_WITHER; 209227464Smav g_post_event(g_mpd, cp, M_WAITOK, NULL); 210227464Smav return; 211227464Smav } 212227464Smav } 213227464Smav sc = gp->softc; 214227464Smav mtx_lock(&sc->sc_mtx); 215167050Smjacob if (cp->provider) { 216167050Smjacob printf("GEOM_MULTIPATH: %s removed from %s\n", 217227464Smav cp->provider->name, gp->name); 218167050Smjacob g_detach(cp); 219167050Smjacob } 220167050Smjacob g_destroy_consumer(cp); 221227464Smav mtx_unlock(&sc->sc_mtx); 222227464Smav if (LIST_EMPTY(&gp->consumer)) 223227464Smav g_multipath_destroy(gp); 224167050Smjacob} 225167050Smjacob 226167050Smjacobstatic void 227167050Smjacobg_multipath_orphan(struct g_consumer *cp) 228167050Smjacob{ 229227464Smav struct g_multipath_softc *sc; 230227464Smav uintptr_t *cnt; 231227464Smav 232227464Smav g_topology_assert(); 233227464Smav printf("GEOM_MULTIPATH: %s in %s was disconnected\n", 234227464Smav cp->provider->name, cp->geom->name); 235227464Smav sc = cp->geom->softc; 236227464Smav cnt = (uintptr_t *)&cp->private; 237227464Smav mtx_lock(&sc->sc_mtx); 238227464Smav sc->sc_ndisks--; 239227464Smav g_multipath_fault(cp, MP_LOST); 240227464Smav if (*cnt == 0 && (cp->index & MP_POSTED) == 0) { 241167050Smjacob cp->index |= MP_POSTED; 242227464Smav mtx_unlock(&sc->sc_mtx); 243167050Smjacob g_mpd(cp, 0); 244227464Smav } else 245227464Smav mtx_unlock(&sc->sc_mtx); 246167050Smjacob} 247167050Smjacob 248167050Smjacobstatic void 249260478Smavg_multipath_resize(struct g_consumer *cp) 250260478Smav{ 251260478Smav struct g_multipath_softc *sc; 252260478Smav struct g_geom *gp; 253260478Smav struct g_consumer *cp1; 254260478Smav struct g_provider *pp; 255260478Smav struct g_multipath_metadata md; 256260478Smav off_t size, psize, ssize; 257260478Smav int error; 258260478Smav 259260478Smav g_topology_assert(); 260260478Smav 261260478Smav gp = cp->geom; 262260478Smav pp = cp->provider; 263260478Smav sc = gp->softc; 264260478Smav 265260478Smav if (sc->sc_stopping) 266260478Smav return; 267260478Smav 268260478Smav if (pp->mediasize < sc->sc_size) { 269260478Smav size = pp->mediasize; 270260478Smav ssize = pp->sectorsize; 271260478Smav } else { 272260478Smav size = ssize = OFF_MAX; 273260478Smav mtx_lock(&sc->sc_mtx); 274260478Smav LIST_FOREACH(cp1, &gp->consumer, consumer) { 275260478Smav pp = cp1->provider; 276260478Smav if (pp == NULL) 277260478Smav continue; 278260478Smav if (pp->mediasize < size) { 279260478Smav size = pp->mediasize; 280260478Smav ssize = pp->sectorsize; 281260478Smav } 282260478Smav } 283260478Smav mtx_unlock(&sc->sc_mtx); 284260478Smav if (size == OFF_MAX || size == sc->sc_size) 285260478Smav return; 286260478Smav } 287260478Smav psize = size - ((sc->sc_uuid[0] != 0) ? ssize : 0); 288260478Smav printf("GEOM_MULTIPATH: %s size changed from %jd to %jd\n", 289260478Smav sc->sc_name, sc->sc_pp->mediasize, psize); 290260478Smav if (sc->sc_uuid[0] != 0 && size < sc->sc_size) { 291260478Smav error = g_multipath_read_metadata(cp, &md); 292260478Smav if (error || 293260478Smav (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) || 294260478Smav (memcmp(md.md_uuid, sc->sc_uuid, sizeof(sc->sc_uuid)) != 0) || 295260478Smav (strcmp(md.md_name, sc->sc_name) != 0) || 296260478Smav (md.md_size != 0 && md.md_size != size) || 297260478Smav (md.md_sectorsize != 0 && md.md_sectorsize != ssize)) { 298260478Smav g_multipath_destroy(gp); 299260478Smav return; 300260478Smav } 301260478Smav } 302260478Smav sc->sc_size = size; 303260478Smav g_resize_provider(sc->sc_pp, psize); 304260478Smav 305260478Smav if (sc->sc_uuid[0] != 0) { 306260478Smav pp = cp->provider; 307260478Smav strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic)); 308260478Smav memcpy(md.md_uuid, sc->sc_uuid, sizeof (sc->sc_uuid)); 309260478Smav strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name)); 310260478Smav md.md_version = G_MULTIPATH_VERSION; 311260478Smav md.md_size = size; 312260478Smav md.md_sectorsize = ssize; 313260478Smav md.md_active_active = sc->sc_active_active; 314260478Smav error = g_multipath_write_metadata(cp, &md); 315260478Smav if (error != 0) 316260478Smav printf("GEOM_MULTIPATH: Can't update metadata on %s " 317260478Smav "(%d)\n", pp->name, error); 318260478Smav } 319260478Smav} 320260478Smav 321260478Smavstatic void 322167050Smjacobg_multipath_start(struct bio *bp) 323167050Smjacob{ 324167050Smjacob struct g_multipath_softc *sc; 325167050Smjacob struct g_geom *gp; 326167050Smjacob struct g_consumer *cp; 327167050Smjacob struct bio *cbp; 328227464Smav uintptr_t *cnt; 329167050Smjacob 330167050Smjacob gp = bp->bio_to->geom; 331167050Smjacob sc = gp->softc; 332167050Smjacob KASSERT(sc != NULL, ("NULL sc")); 333167050Smjacob cbp = g_clone_bio(bp); 334167050Smjacob if (cbp == NULL) { 335167050Smjacob g_io_deliver(bp, ENOMEM); 336167050Smjacob return; 337167050Smjacob } 338227464Smav mtx_lock(&sc->sc_mtx); 339234415Smav cp = g_multipath_choose(gp, bp); 340227464Smav if (cp == NULL) { 341227464Smav mtx_unlock(&sc->sc_mtx); 342227464Smav g_destroy_bio(cbp); 343227464Smav g_io_deliver(bp, ENXIO); 344227464Smav return; 345227464Smav } 346227464Smav if ((uintptr_t)bp->bio_driver1 < sc->sc_ndisks) 347227464Smav bp->bio_driver1 = (void *)(uintptr_t)sc->sc_ndisks; 348227464Smav cnt = (uintptr_t *)&cp->private; 349227464Smav (*cnt)++; 350227464Smav mtx_unlock(&sc->sc_mtx); 351167050Smjacob cbp->bio_done = g_multipath_done; 352167050Smjacob g_io_request(cbp, cp); 353167050Smjacob} 354167050Smjacob 355167050Smjacobstatic void 356167050Smjacobg_multipath_done(struct bio *bp) 357167050Smjacob{ 358227464Smav struct g_multipath_softc *sc; 359227464Smav struct g_consumer *cp; 360227464Smav uintptr_t *cnt; 361227464Smav 362167050Smjacob if (bp->bio_error == ENXIO || bp->bio_error == EIO) { 363167050Smjacob mtx_lock(&gmtbq_mtx); 364167050Smjacob bioq_insert_tail(&gmtbq, bp); 365227464Smav mtx_unlock(&gmtbq_mtx); 366167050Smjacob wakeup(&g_multipath_kt_state); 367167050Smjacob } else { 368227464Smav cp = bp->bio_from; 369227464Smav sc = cp->geom->softc; 370227464Smav cnt = (uintptr_t *)&cp->private; 371227464Smav mtx_lock(&sc->sc_mtx); 372227464Smav (*cnt)--; 373227464Smav if (*cnt == 0 && (cp->index & MP_LOST)) { 374287850Simp if (g_post_event(g_mpd, cp, M_NOWAIT, NULL) == 0) 375287850Simp cp->index |= MP_POSTED; 376227464Smav mtx_unlock(&sc->sc_mtx); 377227464Smav } else 378227464Smav mtx_unlock(&sc->sc_mtx); 379167050Smjacob g_std_done(bp); 380167050Smjacob } 381167050Smjacob} 382167050Smjacob 383167050Smjacobstatic void 384167050Smjacobg_multipath_done_error(struct bio *bp) 385167050Smjacob{ 386167050Smjacob struct bio *pbp; 387167050Smjacob struct g_geom *gp; 388167050Smjacob struct g_multipath_softc *sc; 389167050Smjacob struct g_consumer *cp; 390167050Smjacob struct g_provider *pp; 391227464Smav uintptr_t *cnt; 392167050Smjacob 393167050Smjacob /* 394167050Smjacob * If we had a failure, we have to check first to see 395167050Smjacob * whether the consumer it failed on was the currently 396167050Smjacob * active consumer (i.e., this is the first in perhaps 397167050Smjacob * a number of failures). If so, we then switch consumers 398167050Smjacob * to the next available consumer. 399167050Smjacob */ 400167050Smjacob 401167050Smjacob pbp = bp->bio_parent; 402167050Smjacob gp = pbp->bio_to->geom; 403167050Smjacob sc = gp->softc; 404167050Smjacob cp = bp->bio_from; 405167050Smjacob pp = cp->provider; 406227464Smav cnt = (uintptr_t *)&cp->private; 407167050Smjacob 408227464Smav mtx_lock(&sc->sc_mtx); 409234415Smav if ((cp->index & MP_FAIL) == 0) { 410234415Smav printf("GEOM_MULTIPATH: Error %d, %s in %s marked FAIL\n", 411234415Smav bp->bio_error, pp->name, sc->sc_name); 412234415Smav g_multipath_fault(cp, MP_FAIL); 413234415Smav } 414227464Smav (*cnt)--; 415227464Smav if (*cnt == 0 && (cp->index & (MP_LOST | MP_POSTED)) == MP_LOST) { 416167050Smjacob cp->index |= MP_POSTED; 417227464Smav mtx_unlock(&sc->sc_mtx); 418227464Smav g_post_event(g_mpd, cp, M_WAITOK, NULL); 419227464Smav } else 420227464Smav mtx_unlock(&sc->sc_mtx); 421167050Smjacob 422167050Smjacob /* 423167050Smjacob * If we can fruitfully restart the I/O, do so. 424167050Smjacob */ 425227464Smav if (pbp->bio_children < (uintptr_t)pbp->bio_driver1) { 426227464Smav pbp->bio_inbed++; 427167050Smjacob g_destroy_bio(bp); 428167050Smjacob g_multipath_start(pbp); 429167050Smjacob } else { 430167050Smjacob g_std_done(bp); 431167050Smjacob } 432167050Smjacob} 433167050Smjacob 434167050Smjacobstatic void 435167050Smjacobg_multipath_kt(void *arg) 436167050Smjacob{ 437204071Spjd 438167050Smjacob g_multipath_kt_state = GKT_RUN; 439167050Smjacob mtx_lock(&gmtbq_mtx); 440167050Smjacob while (g_multipath_kt_state == GKT_RUN) { 441167050Smjacob for (;;) { 442167050Smjacob struct bio *bp; 443204071Spjd 444167050Smjacob bp = bioq_takefirst(&gmtbq); 445204071Spjd if (bp == NULL) 446167050Smjacob break; 447167050Smjacob mtx_unlock(&gmtbq_mtx); 448167050Smjacob g_multipath_done_error(bp); 449167050Smjacob mtx_lock(&gmtbq_mtx); 450167050Smjacob } 451234415Smav if (g_multipath_kt_state != GKT_RUN) 452234415Smav break; 453167050Smjacob msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 454234415Smav "gkt:wait", 0); 455167050Smjacob } 456167050Smjacob mtx_unlock(&gmtbq_mtx); 457167050Smjacob wakeup(&g_multipath_kt_state); 458172836Sjulian kproc_exit(0); 459167050Smjacob} 460167050Smjacob 461167050Smjacob 462167050Smjacobstatic int 463167050Smjacobg_multipath_access(struct g_provider *pp, int dr, int dw, int de) 464167050Smjacob{ 465167050Smjacob struct g_geom *gp; 466167050Smjacob struct g_consumer *cp, *badcp = NULL; 467227464Smav struct g_multipath_softc *sc; 468167050Smjacob int error; 469167050Smjacob 470167050Smjacob gp = pp->geom; 471167050Smjacob 472294709Ssmh /* Error used if we have no valid consumers. */ 473294709Ssmh error = ENXIO; 474294709Ssmh 475167050Smjacob LIST_FOREACH(cp, &gp->consumer, consumer) { 476294709Ssmh if (cp->index & MP_WITHER) 477294709Ssmh continue; 478294709Ssmh 479167050Smjacob error = g_access(cp, dr, dw, de); 480167050Smjacob if (error) { 481167050Smjacob badcp = cp; 482167050Smjacob goto fail; 483167050Smjacob } 484167050Smjacob } 485294709Ssmh 486294709Ssmh if (error != 0) 487294709Ssmh return (error); 488294709Ssmh 489227464Smav sc = gp->softc; 490227464Smav sc->sc_opened += dr + dw + de; 491227464Smav if (sc->sc_stopping && sc->sc_opened == 0) 492227464Smav g_multipath_destroy(gp); 493294709Ssmh 494167050Smjacob return (0); 495167050Smjacob 496167050Smjacobfail: 497167050Smjacob LIST_FOREACH(cp, &gp->consumer, consumer) { 498204071Spjd if (cp == badcp) 499167050Smjacob break; 500294709Ssmh if (cp->index & MP_WITHER) 501294709Ssmh continue; 502294709Ssmh 503167050Smjacob (void) g_access(cp, -dr, -dw, -de); 504167050Smjacob } 505167050Smjacob return (error); 506167050Smjacob} 507167050Smjacob 508167050Smjacobstatic struct g_geom * 509167050Smjacobg_multipath_create(struct g_class *mp, struct g_multipath_metadata *md) 510167050Smjacob{ 511167050Smjacob struct g_multipath_softc *sc; 512167050Smjacob struct g_geom *gp; 513167050Smjacob struct g_provider *pp; 514167050Smjacob 515167050Smjacob g_topology_assert(); 516167050Smjacob 517167050Smjacob LIST_FOREACH(gp, &mp->geom, geom) { 518227464Smav sc = gp->softc; 519227464Smav if (sc == NULL || sc->sc_stopping) 520227464Smav continue; 521167050Smjacob if (strcmp(gp->name, md->md_name) == 0) { 522167050Smjacob printf("GEOM_MULTIPATH: name %s already exists\n", 523167050Smjacob md->md_name); 524167050Smjacob return (NULL); 525167050Smjacob } 526167050Smjacob } 527167050Smjacob 528243333Sjh gp = g_new_geomf(mp, "%s", md->md_name); 529167050Smjacob sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 530227464Smav mtx_init(&sc->sc_mtx, "multipath", NULL, MTX_DEF); 531227464Smav memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid)); 532227464Smav memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name)); 533227464Smav sc->sc_active_active = md->md_active_active; 534260478Smav sc->sc_size = md->md_size; 535167050Smjacob gp->softc = sc; 536167050Smjacob gp->start = g_multipath_start; 537167050Smjacob gp->orphan = g_multipath_orphan; 538260478Smav gp->resize = g_multipath_resize; 539167050Smjacob gp->access = g_multipath_access; 540227464Smav gp->dumpconf = g_multipath_dumpconf; 541167050Smjacob 542167050Smjacob pp = g_new_providerf(gp, "multipath/%s", md->md_name); 543260385Sscottl pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; 544227464Smav if (md->md_size != 0) { 545227464Smav pp->mediasize = md->md_size - 546227464Smav ((md->md_uuid[0] != 0) ? md->md_sectorsize : 0); 547227464Smav pp->sectorsize = md->md_sectorsize; 548227464Smav } 549227464Smav sc->sc_pp = pp; 550167050Smjacob g_error_provider(pp, 0); 551227464Smav printf("GEOM_MULTIPATH: %s created\n", gp->name); 552167050Smjacob return (gp); 553167050Smjacob} 554167050Smjacob 555167050Smjacobstatic int 556167050Smjacobg_multipath_add_disk(struct g_geom *gp, struct g_provider *pp) 557167050Smjacob{ 558167050Smjacob struct g_multipath_softc *sc; 559167050Smjacob struct g_consumer *cp, *nxtcp; 560227464Smav int error, acr, acw, ace; 561167050Smjacob 562167050Smjacob g_topology_assert(); 563167050Smjacob 564167050Smjacob sc = gp->softc; 565167050Smjacob KASSERT(sc, ("no softc")); 566167050Smjacob 567167050Smjacob /* 568167050Smjacob * Make sure that the passed provider isn't already attached 569167050Smjacob */ 570167050Smjacob LIST_FOREACH(cp, &gp->consumer, consumer) { 571204071Spjd if (cp->provider == pp) 572167050Smjacob break; 573167050Smjacob } 574167050Smjacob if (cp) { 575167050Smjacob printf("GEOM_MULTIPATH: provider %s already attached to %s\n", 576167050Smjacob pp->name, gp->name); 577167050Smjacob return (EEXIST); 578167050Smjacob } 579167050Smjacob nxtcp = LIST_FIRST(&gp->consumer); 580167050Smjacob cp = g_new_consumer(gp); 581260385Sscottl cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 582227464Smav cp->private = NULL; 583227464Smav cp->index = MP_NEW; 584167050Smjacob error = g_attach(cp, pp); 585167050Smjacob if (error != 0) { 586167050Smjacob printf("GEOM_MULTIPATH: cannot attach %s to %s", 587167050Smjacob pp->name, sc->sc_name); 588167050Smjacob g_destroy_consumer(cp); 589167050Smjacob return (error); 590167050Smjacob } 591167050Smjacob 592167050Smjacob /* 593167050Smjacob * Set access permissions on new consumer to match other consumers 594167050Smjacob */ 595227464Smav if (sc->sc_pp) { 596227464Smav acr = sc->sc_pp->acr; 597227464Smav acw = sc->sc_pp->acw; 598227464Smav ace = sc->sc_pp->ace; 599227464Smav } else 600227464Smav acr = acw = ace = 0; 601227464Smav if (g_multipath_exclusive) { 602227464Smav acr++; 603227464Smav acw++; 604227464Smav ace++; 605167050Smjacob } 606227464Smav error = g_access(cp, acr, acw, ace); 607227464Smav if (error) { 608227464Smav printf("GEOM_MULTIPATH: cannot set access in " 609227464Smav "attaching %s to %s (%d)\n", 610227464Smav pp->name, sc->sc_name, error); 611227464Smav g_detach(cp); 612227464Smav g_destroy_consumer(cp); 613227464Smav return (error); 614167050Smjacob } 615260478Smav if (sc->sc_size == 0) { 616260478Smav sc->sc_size = pp->mediasize - 617227464Smav ((sc->sc_uuid[0] != 0) ? pp->sectorsize : 0); 618260478Smav sc->sc_pp->mediasize = sc->sc_size; 619227464Smav sc->sc_pp->sectorsize = pp->sectorsize; 620227464Smav } 621260478Smav if (sc->sc_pp->stripesize == 0 && sc->sc_pp->stripeoffset == 0) { 622227464Smav sc->sc_pp->stripesize = pp->stripesize; 623227464Smav sc->sc_pp->stripeoffset = pp->stripeoffset; 624227464Smav } 625260478Smav sc->sc_pp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED; 626227464Smav mtx_lock(&sc->sc_mtx); 627227464Smav cp->index = 0; 628227464Smav sc->sc_ndisks++; 629227464Smav mtx_unlock(&sc->sc_mtx); 630227464Smav printf("GEOM_MULTIPATH: %s added to %s\n", 631227464Smav pp->name, sc->sc_name); 632227464Smav if (sc->sc_active == NULL) { 633227464Smav sc->sc_active = cp; 634234415Smav if (sc->sc_active_active != 1) 635227464Smav printf("GEOM_MULTIPATH: %s is now active path in %s\n", 636227464Smav pp->name, sc->sc_name); 637227464Smav } 638167050Smjacob return (0); 639167050Smjacob} 640167050Smjacob 641167050Smjacobstatic int 642167050Smjacobg_multipath_destroy(struct g_geom *gp) 643167050Smjacob{ 644227464Smav struct g_multipath_softc *sc; 645227464Smav struct g_consumer *cp, *cp1; 646167050Smjacob 647167050Smjacob g_topology_assert(); 648204071Spjd if (gp->softc == NULL) 649167050Smjacob return (ENXIO); 650227464Smav sc = gp->softc; 651227464Smav if (!sc->sc_stopping) { 652227464Smav printf("GEOM_MULTIPATH: destroying %s\n", gp->name); 653227464Smav sc->sc_stopping = 1; 654227464Smav } 655227464Smav if (sc->sc_opened != 0) { 656260478Smav g_wither_provider(sc->sc_pp, ENXIO); 657260478Smav sc->sc_pp = NULL; 658227464Smav return (EINPROGRESS); 659227464Smav } 660227464Smav LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 661227464Smav mtx_lock(&sc->sc_mtx); 662227464Smav if ((cp->index & MP_POSTED) == 0) { 663227464Smav cp->index |= MP_POSTED; 664227464Smav mtx_unlock(&sc->sc_mtx); 665227464Smav g_mpd(cp, 0); 666227464Smav if (cp1 == NULL) 667227464Smav return(0); /* Recursion happened. */ 668227464Smav } else 669227464Smav mtx_unlock(&sc->sc_mtx); 670227464Smav } 671227464Smav if (!LIST_EMPTY(&gp->consumer)) 672227464Smav return (EINPROGRESS); 673227464Smav mtx_destroy(&sc->sc_mtx); 674167050Smjacob g_free(gp->softc); 675167050Smjacob gp->softc = NULL; 676227464Smav printf("GEOM_MULTIPATH: %s destroyed\n", gp->name); 677167050Smjacob g_wither_geom(gp, ENXIO); 678167050Smjacob return (0); 679167050Smjacob} 680167050Smjacob 681167050Smjacobstatic int 682167050Smjacobg_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp, 683167050Smjacob struct g_geom *gp) 684167050Smjacob{ 685204071Spjd 686167050Smjacob return (g_multipath_destroy(gp)); 687167050Smjacob} 688167050Smjacob 689205412Smjacobstatic int 690205412Smjacobg_multipath_rotate(struct g_geom *gp) 691205412Smjacob{ 692239673Sthomas struct g_consumer *lcp, *first_good_cp = NULL; 693205412Smjacob struct g_multipath_softc *sc = gp->softc; 694239673Sthomas int active_cp_seen = 0; 695205412Smjacob 696205412Smjacob g_topology_assert(); 697205412Smjacob if (sc == NULL) 698205412Smjacob return (ENXIO); 699205412Smjacob LIST_FOREACH(lcp, &gp->consumer, consumer) { 700205412Smjacob if ((lcp->index & MP_BAD) == 0) { 701239673Sthomas if (first_good_cp == NULL) 702239673Sthomas first_good_cp = lcp; 703239673Sthomas if (active_cp_seen) 704205412Smjacob break; 705205412Smjacob } 706239673Sthomas if (sc->sc_active == lcp) 707239673Sthomas active_cp_seen = 1; 708205412Smjacob } 709239673Sthomas if (lcp == NULL) 710239673Sthomas lcp = first_good_cp; 711239673Sthomas if (lcp && lcp != sc->sc_active) { 712227464Smav sc->sc_active = lcp; 713234415Smav if (sc->sc_active_active != 1) 714227464Smav printf("GEOM_MULTIPATH: %s is now active path in %s\n", 715227464Smav lcp->provider->name, sc->sc_name); 716205412Smjacob } 717205412Smjacob return (0); 718205412Smjacob} 719205412Smjacob 720167050Smjacobstatic void 721167050Smjacobg_multipath_init(struct g_class *mp) 722167050Smjacob{ 723167050Smjacob bioq_init(&gmtbq); 724167050Smjacob mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF); 725234415Smav kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt"); 726167050Smjacob} 727167050Smjacob 728167050Smjacobstatic void 729167050Smjacobg_multipath_fini(struct g_class *mp) 730167050Smjacob{ 731167050Smjacob if (g_multipath_kt_state == GKT_RUN) { 732167050Smjacob mtx_lock(&gmtbq_mtx); 733167050Smjacob g_multipath_kt_state = GKT_DIE; 734167050Smjacob wakeup(&g_multipath_kt_state); 735167050Smjacob msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 736167050Smjacob "gmp:fini", 0); 737167050Smjacob mtx_unlock(&gmtbq_mtx); 738167050Smjacob } 739167050Smjacob} 740167050Smjacob 741167050Smjacobstatic int 742167050Smjacobg_multipath_read_metadata(struct g_consumer *cp, 743167050Smjacob struct g_multipath_metadata *md) 744167050Smjacob{ 745167050Smjacob struct g_provider *pp; 746167050Smjacob u_char *buf; 747167050Smjacob int error; 748167050Smjacob 749167050Smjacob g_topology_assert(); 750167050Smjacob error = g_access(cp, 1, 0, 0); 751204071Spjd if (error != 0) 752167050Smjacob return (error); 753167050Smjacob pp = cp->provider; 754167050Smjacob g_topology_unlock(); 755167050Smjacob buf = g_read_data(cp, pp->mediasize - pp->sectorsize, 756167050Smjacob pp->sectorsize, &error); 757167050Smjacob g_topology_lock(); 758167050Smjacob g_access(cp, -1, 0, 0); 759204071Spjd if (buf == NULL) 760167050Smjacob return (error); 761167050Smjacob multipath_metadata_decode(buf, md); 762167050Smjacob g_free(buf); 763167050Smjacob return (0); 764167050Smjacob} 765167050Smjacob 766260478Smavstatic int 767260478Smavg_multipath_write_metadata(struct g_consumer *cp, 768260478Smav struct g_multipath_metadata *md) 769260478Smav{ 770260478Smav struct g_provider *pp; 771260478Smav u_char *buf; 772260478Smav int error; 773260478Smav 774260478Smav g_topology_assert(); 775260478Smav error = g_access(cp, 1, 1, 1); 776260478Smav if (error != 0) 777260478Smav return (error); 778260478Smav pp = cp->provider; 779260478Smav g_topology_unlock(); 780260478Smav buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 781260478Smav multipath_metadata_encode(md, buf); 782260478Smav error = g_write_data(cp, pp->mediasize - pp->sectorsize, 783260478Smav buf, pp->sectorsize); 784260478Smav g_topology_lock(); 785260478Smav g_access(cp, -1, -1, -1); 786260478Smav g_free(buf); 787260478Smav return (error); 788260478Smav} 789260478Smav 790167050Smjacobstatic struct g_geom * 791167050Smjacobg_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 792167050Smjacob{ 793167050Smjacob struct g_multipath_metadata md; 794167050Smjacob struct g_multipath_softc *sc; 795167050Smjacob struct g_consumer *cp; 796167050Smjacob struct g_geom *gp, *gp1; 797167050Smjacob int error, isnew; 798167050Smjacob 799167050Smjacob g_topology_assert(); 800167050Smjacob 801167050Smjacob gp = g_new_geomf(mp, "multipath:taste"); 802167050Smjacob gp->start = g_multipath_start; 803167050Smjacob gp->access = g_multipath_access; 804167050Smjacob gp->orphan = g_multipath_orphan; 805167050Smjacob cp = g_new_consumer(gp); 806167050Smjacob g_attach(cp, pp); 807167050Smjacob error = g_multipath_read_metadata(cp, &md); 808167050Smjacob g_detach(cp); 809167050Smjacob g_destroy_consumer(cp); 810167050Smjacob g_destroy_geom(gp); 811204071Spjd if (error != 0) 812167050Smjacob return (NULL); 813167050Smjacob gp = NULL; 814167050Smjacob 815167050Smjacob if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) { 816204071Spjd if (g_multipath_debug) 817167050Smjacob printf("%s is not MULTIPATH\n", pp->name); 818167050Smjacob return (NULL); 819167050Smjacob } 820167050Smjacob if (md.md_version != G_MULTIPATH_VERSION) { 821167050Smjacob printf("%s has version %d multipath id- this module is version " 822167050Smjacob " %d: rejecting\n", pp->name, md.md_version, 823167050Smjacob G_MULTIPATH_VERSION); 824167050Smjacob return (NULL); 825167050Smjacob } 826227464Smav if (md.md_size != 0 && md.md_size != pp->mediasize) 827227464Smav return (NULL); 828227464Smav if (md.md_sectorsize != 0 && md.md_sectorsize != pp->sectorsize) 829227464Smav return (NULL); 830204071Spjd if (g_multipath_debug) 831167050Smjacob printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid); 832167050Smjacob 833167050Smjacob /* 834167050Smjacob * Let's check if such a device already is present. We check against 835167050Smjacob * uuid alone first because that's the true distinguishor. If that 836167050Smjacob * passes, then we check for name conflicts. If there are conflicts, 837167050Smjacob * modify the name. 838167050Smjacob * 839167050Smjacob * The whole purpose of this is to solve the problem that people don't 840167050Smjacob * pick good unique names, but good unique names (like uuids) are a 841167050Smjacob * pain to use. So, we allow people to build GEOMs with friendly names 842167050Smjacob * and uuids, and modify the names in case there's a collision. 843167050Smjacob */ 844167050Smjacob sc = NULL; 845167050Smjacob LIST_FOREACH(gp, &mp->geom, geom) { 846167050Smjacob sc = gp->softc; 847227464Smav if (sc == NULL || sc->sc_stopping) 848167050Smjacob continue; 849204071Spjd if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0) 850167050Smjacob break; 851167050Smjacob } 852167050Smjacob 853167050Smjacob LIST_FOREACH(gp1, &mp->geom, geom) { 854204071Spjd if (gp1 == gp) 855167050Smjacob continue; 856167050Smjacob sc = gp1->softc; 857227464Smav if (sc == NULL || sc->sc_stopping) 858167050Smjacob continue; 859204071Spjd if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0) 860167050Smjacob break; 861167050Smjacob } 862167050Smjacob 863167050Smjacob /* 864167050Smjacob * If gp is NULL, we had no extant MULTIPATH geom with this uuid. 865167050Smjacob * 866167050Smjacob * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant 867167050Smjacob * with the same name (but a different UUID). 868167050Smjacob * 869167050Smjacob * If gp is NULL, then modify the name with a random number and 870167050Smjacob * complain, but allow the creation of the geom to continue. 871167050Smjacob * 872167050Smjacob * If gp is *not* NULL, just use the geom's name as we're attaching 873167050Smjacob * this disk to the (previously generated) name. 874167050Smjacob */ 875167050Smjacob 876167050Smjacob if (gp1) { 877167050Smjacob sc = gp1->softc; 878167050Smjacob if (gp == NULL) { 879167050Smjacob char buf[16]; 880167050Smjacob u_long rand = random(); 881167050Smjacob 882167050Smjacob snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand); 883167050Smjacob printf("GEOM_MULTIPATH: geom %s/%s exists already\n", 884167050Smjacob sc->sc_name, sc->sc_uuid); 885167050Smjacob printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n", 886167050Smjacob md.md_uuid, buf); 887204071Spjd strlcpy(md.md_name, buf, sizeof(md.md_name)); 888167050Smjacob } else { 889204071Spjd strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name)); 890167050Smjacob } 891167050Smjacob } 892167050Smjacob 893167050Smjacob if (gp == NULL) { 894167050Smjacob gp = g_multipath_create(mp, &md); 895167050Smjacob if (gp == NULL) { 896167050Smjacob printf("GEOM_MULTIPATH: cannot create geom %s/%s\n", 897167050Smjacob md.md_name, md.md_uuid); 898167050Smjacob return (NULL); 899167050Smjacob } 900167050Smjacob isnew = 1; 901167050Smjacob } else { 902167050Smjacob isnew = 0; 903167050Smjacob } 904167050Smjacob 905167050Smjacob sc = gp->softc; 906167050Smjacob KASSERT(sc != NULL, ("sc is NULL")); 907167050Smjacob error = g_multipath_add_disk(gp, pp); 908167050Smjacob if (error != 0) { 909204071Spjd if (isnew) 910167050Smjacob g_multipath_destroy(gp); 911167050Smjacob return (NULL); 912167050Smjacob } 913167050Smjacob return (gp); 914167050Smjacob} 915167050Smjacob 916167050Smjacobstatic void 917227464Smavg_multipath_ctl_add_name(struct gctl_req *req, struct g_class *mp, 918227464Smav const char *name) 919167050Smjacob{ 920227464Smav struct g_multipath_softc *sc; 921167050Smjacob struct g_geom *gp; 922205847Smjacob struct g_consumer *cp; 923227464Smav struct g_provider *pp; 924227464Smav const char *mpname; 925167050Smjacob static const char devpf[6] = "/dev/"; 926167050Smjacob 927167050Smjacob g_topology_assert(); 928167050Smjacob 929167050Smjacob mpname = gctl_get_asciiparam(req, "arg0"); 930167050Smjacob if (mpname == NULL) { 931167050Smjacob gctl_error(req, "No 'arg0' argument"); 932167050Smjacob return; 933167050Smjacob } 934205847Smjacob gp = g_multipath_find_geom(mp, mpname); 935205847Smjacob if (gp == NULL) { 936205847Smjacob gctl_error(req, "Device %s is invalid", mpname); 937167050Smjacob return; 938167050Smjacob } 939227464Smav sc = gp->softc; 940167050Smjacob 941204071Spjd if (strncmp(name, devpf, 5) == 0) 942167050Smjacob name += 5; 943205847Smjacob pp = g_provider_by_name(name); 944205847Smjacob if (pp == NULL) { 945167050Smjacob gctl_error(req, "Provider %s is invalid", name); 946167050Smjacob return; 947167050Smjacob } 948167050Smjacob 949167050Smjacob /* 950227464Smav * Check to make sure parameters match. 951167050Smjacob */ 952227464Smav LIST_FOREACH(cp, &gp->consumer, consumer) { 953227464Smav if (cp->provider == pp) { 954227464Smav gctl_error(req, "provider %s is already there", 955227464Smav pp->name); 956205847Smjacob return; 957205847Smjacob } 958167050Smjacob } 959260478Smav if (sc->sc_pp->mediasize != 0 && 960227464Smav sc->sc_pp->mediasize + (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0) 961227464Smav != pp->mediasize) { 962227464Smav gctl_error(req, "Providers size mismatch %jd != %jd", 963227464Smav (intmax_t) sc->sc_pp->mediasize + 964227464Smav (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0), 965227464Smav (intmax_t) pp->mediasize); 966227464Smav return; 967227464Smav } 968260478Smav if (sc->sc_pp->sectorsize != 0 && 969227464Smav sc->sc_pp->sectorsize != pp->sectorsize) { 970227464Smav gctl_error(req, "Providers sectorsize mismatch %u != %u", 971227464Smav sc->sc_pp->sectorsize, pp->sectorsize); 972227464Smav return; 973227464Smav } 974167050Smjacob 975167050Smjacob /* 976205847Smjacob * Now add.... 977167050Smjacob */ 978205847Smjacob (void) g_multipath_add_disk(gp, pp); 979167050Smjacob} 980167050Smjacob 981227464Smavstatic void 982239012Sthomasg_multipath_ctl_prefer(struct gctl_req *req, struct g_class *mp) 983239012Sthomas{ 984239012Sthomas struct g_geom *gp; 985239012Sthomas struct g_multipath_softc *sc; 986239012Sthomas struct g_consumer *cp; 987239012Sthomas const char *name, *mpname; 988239012Sthomas static const char devpf[6] = "/dev/"; 989239012Sthomas int *nargs; 990239012Sthomas 991239012Sthomas g_topology_assert(); 992239012Sthomas 993239012Sthomas mpname = gctl_get_asciiparam(req, "arg0"); 994239012Sthomas if (mpname == NULL) { 995239012Sthomas gctl_error(req, "No 'arg0' argument"); 996239012Sthomas return; 997239012Sthomas } 998239012Sthomas gp = g_multipath_find_geom(mp, mpname); 999239012Sthomas if (gp == NULL) { 1000239012Sthomas gctl_error(req, "Device %s is invalid", mpname); 1001239012Sthomas return; 1002239012Sthomas } 1003239012Sthomas sc = gp->softc; 1004239012Sthomas 1005239012Sthomas nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1006239012Sthomas if (nargs == NULL) { 1007239012Sthomas gctl_error(req, "No 'nargs' argument"); 1008239012Sthomas return; 1009239012Sthomas } 1010239012Sthomas if (*nargs != 2) { 1011239012Sthomas gctl_error(req, "missing device"); 1012239012Sthomas return; 1013239012Sthomas } 1014239012Sthomas 1015239012Sthomas name = gctl_get_asciiparam(req, "arg1"); 1016239012Sthomas if (name == NULL) { 1017239012Sthomas gctl_error(req, "No 'arg1' argument"); 1018239012Sthomas return; 1019239012Sthomas } 1020239012Sthomas if (strncmp(name, devpf, 5) == 0) { 1021239012Sthomas name += 5; 1022239012Sthomas } 1023239012Sthomas 1024239012Sthomas LIST_FOREACH(cp, &gp->consumer, consumer) { 1025239012Sthomas if (cp->provider != NULL 1026239012Sthomas && strcmp(cp->provider->name, name) == 0) 1027239012Sthomas break; 1028239012Sthomas } 1029239012Sthomas 1030239012Sthomas if (cp == NULL) { 1031239012Sthomas gctl_error(req, "Provider %s not found", name); 1032239012Sthomas return; 1033239012Sthomas } 1034239012Sthomas 1035239012Sthomas mtx_lock(&sc->sc_mtx); 1036239012Sthomas 1037239012Sthomas if (cp->index & MP_BAD) { 1038239012Sthomas gctl_error(req, "Consumer %s is invalid", name); 1039239012Sthomas mtx_unlock(&sc->sc_mtx); 1040239012Sthomas return; 1041239012Sthomas } 1042239012Sthomas 1043239012Sthomas /* Here when the consumer is present and in good shape */ 1044239012Sthomas 1045239012Sthomas sc->sc_active = cp; 1046239012Sthomas if (!sc->sc_active_active) 1047239012Sthomas printf("GEOM_MULTIPATH: %s now active path in %s\n", 1048239012Sthomas sc->sc_active->provider->name, sc->sc_name); 1049239012Sthomas 1050239012Sthomas mtx_unlock(&sc->sc_mtx); 1051239012Sthomas} 1052239012Sthomas 1053239012Sthomasstatic void 1054227464Smavg_multipath_ctl_add(struct gctl_req *req, struct g_class *mp) 1055227464Smav{ 1056227464Smav struct g_multipath_softc *sc; 1057227464Smav struct g_geom *gp; 1058227464Smav const char *mpname, *name; 1059227464Smav 1060227464Smav mpname = gctl_get_asciiparam(req, "arg0"); 1061227464Smav if (mpname == NULL) { 1062227464Smav gctl_error(req, "No 'arg0' argument"); 1063227464Smav return; 1064227464Smav } 1065227464Smav gp = g_multipath_find_geom(mp, mpname); 1066227464Smav if (gp == NULL) { 1067227464Smav gctl_error(req, "Device %s not found", mpname); 1068227464Smav return; 1069227464Smav } 1070227464Smav sc = gp->softc; 1071227464Smav 1072227464Smav name = gctl_get_asciiparam(req, "arg1"); 1073227464Smav if (name == NULL) { 1074227464Smav gctl_error(req, "No 'arg1' argument"); 1075227464Smav return; 1076227464Smav } 1077227464Smav g_multipath_ctl_add_name(req, mp, name); 1078227464Smav} 1079227464Smav 1080227464Smavstatic void 1081227464Smavg_multipath_ctl_create(struct gctl_req *req, struct g_class *mp) 1082227464Smav{ 1083227464Smav struct g_multipath_metadata md; 1084227464Smav struct g_multipath_softc *sc; 1085227464Smav struct g_geom *gp; 1086227464Smav const char *mpname, *name; 1087227464Smav char param[16]; 1088234415Smav int *nargs, i, *val; 1089227464Smav 1090227464Smav g_topology_assert(); 1091227464Smav 1092227464Smav nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1093227464Smav if (*nargs < 2) { 1094227464Smav gctl_error(req, "wrong number of arguments."); 1095227464Smav return; 1096227464Smav } 1097227464Smav 1098227464Smav mpname = gctl_get_asciiparam(req, "arg0"); 1099227464Smav if (mpname == NULL) { 1100227464Smav gctl_error(req, "No 'arg0' argument"); 1101227464Smav return; 1102227464Smav } 1103227464Smav gp = g_multipath_find_geom(mp, mpname); 1104227464Smav if (gp != NULL) { 1105227464Smav gctl_error(req, "Device %s already exist", mpname); 1106227464Smav return; 1107227464Smav } 1108227464Smav 1109227464Smav memset(&md, 0, sizeof(md)); 1110227464Smav strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic)); 1111227464Smav md.md_version = G_MULTIPATH_VERSION; 1112227464Smav strlcpy(md.md_name, mpname, sizeof(md.md_name)); 1113227464Smav md.md_size = 0; 1114227464Smav md.md_sectorsize = 0; 1115227464Smav md.md_uuid[0] = 0; 1116234415Smav md.md_active_active = 0; 1117234415Smav val = gctl_get_paraml(req, "active_active", sizeof(*val)); 1118234415Smav if (val != NULL && *val != 0) 1119234415Smav md.md_active_active = 1; 1120234415Smav val = gctl_get_paraml(req, "active_read", sizeof(*val)); 1121234415Smav if (val != NULL && *val != 0) 1122234415Smav md.md_active_active = 2; 1123227464Smav gp = g_multipath_create(mp, &md); 1124227464Smav if (gp == NULL) { 1125227464Smav gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n", 1126227464Smav md.md_name, md.md_uuid); 1127227464Smav return; 1128227464Smav } 1129227464Smav sc = gp->softc; 1130227464Smav 1131227464Smav for (i = 1; i < *nargs; i++) { 1132227464Smav snprintf(param, sizeof(param), "arg%d", i); 1133227464Smav name = gctl_get_asciiparam(req, param); 1134227464Smav g_multipath_ctl_add_name(req, mp, name); 1135227464Smav } 1136227464Smav 1137227464Smav if (sc->sc_ndisks != (*nargs - 1)) 1138227464Smav g_multipath_destroy(gp); 1139227464Smav} 1140227464Smav 1141227464Smavstatic void 1142234415Smavg_multipath_ctl_configure(struct gctl_req *req, struct g_class *mp) 1143234415Smav{ 1144234415Smav struct g_multipath_softc *sc; 1145234415Smav struct g_geom *gp; 1146234415Smav struct g_consumer *cp; 1147234415Smav struct g_provider *pp; 1148235069Smav struct g_multipath_metadata md; 1149234415Smav const char *name; 1150234415Smav int error, *val; 1151234415Smav 1152234415Smav g_topology_assert(); 1153234415Smav 1154234415Smav name = gctl_get_asciiparam(req, "arg0"); 1155234415Smav if (name == NULL) { 1156234415Smav gctl_error(req, "No 'arg0' argument"); 1157234415Smav return; 1158234415Smav } 1159234415Smav gp = g_multipath_find_geom(mp, name); 1160234415Smav if (gp == NULL) { 1161234415Smav gctl_error(req, "Device %s is invalid", name); 1162234415Smav return; 1163234415Smav } 1164234415Smav sc = gp->softc; 1165234415Smav val = gctl_get_paraml(req, "active_active", sizeof(*val)); 1166234415Smav if (val != NULL && *val != 0) 1167234415Smav sc->sc_active_active = 1; 1168234415Smav val = gctl_get_paraml(req, "active_read", sizeof(*val)); 1169234415Smav if (val != NULL && *val != 0) 1170234415Smav sc->sc_active_active = 2; 1171234415Smav val = gctl_get_paraml(req, "active_passive", sizeof(*val)); 1172234415Smav if (val != NULL && *val != 0) 1173234415Smav sc->sc_active_active = 0; 1174234415Smav if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) { 1175234415Smav cp = sc->sc_active; 1176234415Smav pp = cp->provider; 1177235069Smav strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic)); 1178235069Smav memcpy(md.md_uuid, sc->sc_uuid, sizeof (sc->sc_uuid)); 1179235069Smav strlcpy(md.md_name, name, sizeof(md.md_name)); 1180235069Smav md.md_version = G_MULTIPATH_VERSION; 1181235069Smav md.md_size = pp->mediasize; 1182235069Smav md.md_sectorsize = pp->sectorsize; 1183235069Smav md.md_active_active = sc->sc_active_active; 1184260478Smav error = g_multipath_write_metadata(cp, &md); 1185234415Smav if (error != 0) 1186234415Smav gctl_error(req, "Can't update metadata on %s (%d)", 1187234415Smav pp->name, error); 1188234415Smav } 1189234415Smav} 1190234415Smav 1191234415Smavstatic void 1192227464Smavg_multipath_ctl_fail(struct gctl_req *req, struct g_class *mp, int fail) 1193227464Smav{ 1194227464Smav struct g_multipath_softc *sc; 1195227464Smav struct g_geom *gp; 1196227464Smav struct g_consumer *cp; 1197227464Smav const char *mpname, *name; 1198227464Smav int found; 1199227464Smav 1200227464Smav mpname = gctl_get_asciiparam(req, "arg0"); 1201227464Smav if (mpname == NULL) { 1202227464Smav gctl_error(req, "No 'arg0' argument"); 1203227464Smav return; 1204227464Smav } 1205227464Smav gp = g_multipath_find_geom(mp, mpname); 1206227464Smav if (gp == NULL) { 1207227464Smav gctl_error(req, "Device %s not found", mpname); 1208227464Smav return; 1209227464Smav } 1210227464Smav sc = gp->softc; 1211227464Smav 1212227464Smav name = gctl_get_asciiparam(req, "arg1"); 1213227464Smav if (name == NULL) { 1214227464Smav gctl_error(req, "No 'arg1' argument"); 1215227464Smav return; 1216227464Smav } 1217227464Smav 1218227464Smav found = 0; 1219227464Smav mtx_lock(&sc->sc_mtx); 1220227464Smav LIST_FOREACH(cp, &gp->consumer, consumer) { 1221227464Smav if (cp->provider != NULL && 1222227464Smav strcmp(cp->provider->name, name) == 0 && 1223227464Smav (cp->index & MP_LOST) == 0) { 1224227464Smav found = 1; 1225234415Smav if (!fail == !(cp->index & MP_FAIL)) 1226234415Smav continue; 1227227464Smav printf("GEOM_MULTIPATH: %s in %s is marked %s.\n", 1228227464Smav name, sc->sc_name, fail ? "FAIL" : "OK"); 1229227464Smav if (fail) { 1230227464Smav g_multipath_fault(cp, MP_FAIL); 1231227464Smav } else { 1232227464Smav cp->index &= ~MP_FAIL; 1233227464Smav } 1234227464Smav } 1235227464Smav } 1236227464Smav mtx_unlock(&sc->sc_mtx); 1237227464Smav if (found == 0) 1238227464Smav gctl_error(req, "Provider %s not found", name); 1239227464Smav} 1240227464Smav 1241227464Smavstatic void 1242227464Smavg_multipath_ctl_remove(struct gctl_req *req, struct g_class *mp) 1243227464Smav{ 1244227464Smav struct g_multipath_softc *sc; 1245227464Smav struct g_geom *gp; 1246227464Smav struct g_consumer *cp, *cp1; 1247227464Smav const char *mpname, *name; 1248227464Smav uintptr_t *cnt; 1249227464Smav int found; 1250227464Smav 1251227464Smav mpname = gctl_get_asciiparam(req, "arg0"); 1252227464Smav if (mpname == NULL) { 1253227464Smav gctl_error(req, "No 'arg0' argument"); 1254227464Smav return; 1255227464Smav } 1256227464Smav gp = g_multipath_find_geom(mp, mpname); 1257227464Smav if (gp == NULL) { 1258227464Smav gctl_error(req, "Device %s not found", mpname); 1259227464Smav return; 1260227464Smav } 1261227464Smav sc = gp->softc; 1262227464Smav 1263227464Smav name = gctl_get_asciiparam(req, "arg1"); 1264227464Smav if (name == NULL) { 1265227464Smav gctl_error(req, "No 'arg1' argument"); 1266227464Smav return; 1267227464Smav } 1268227464Smav 1269227464Smav found = 0; 1270227464Smav mtx_lock(&sc->sc_mtx); 1271227464Smav LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { 1272227464Smav if (cp->provider != NULL && 1273227464Smav strcmp(cp->provider->name, name) == 0 && 1274227464Smav (cp->index & MP_LOST) == 0) { 1275227464Smav found = 1; 1276227464Smav printf("GEOM_MULTIPATH: removing %s from %s\n", 1277227464Smav cp->provider->name, cp->geom->name); 1278227464Smav sc->sc_ndisks--; 1279227464Smav g_multipath_fault(cp, MP_LOST); 1280227464Smav cnt = (uintptr_t *)&cp->private; 1281227464Smav if (*cnt == 0 && (cp->index & MP_POSTED) == 0) { 1282227464Smav cp->index |= MP_POSTED; 1283227464Smav mtx_unlock(&sc->sc_mtx); 1284227464Smav g_mpd(cp, 0); 1285227464Smav if (cp1 == NULL) 1286227464Smav return; /* Recursion happened. */ 1287227464Smav mtx_lock(&sc->sc_mtx); 1288227464Smav } 1289227464Smav } 1290227464Smav } 1291227464Smav mtx_unlock(&sc->sc_mtx); 1292227464Smav if (found == 0) 1293227464Smav gctl_error(req, "Provider %s not found", name); 1294227464Smav} 1295227464Smav 1296167050Smjacobstatic struct g_geom * 1297167050Smjacobg_multipath_find_geom(struct g_class *mp, const char *name) 1298167050Smjacob{ 1299167050Smjacob struct g_geom *gp; 1300227464Smav struct g_multipath_softc *sc; 1301167050Smjacob 1302167050Smjacob LIST_FOREACH(gp, &mp->geom, geom) { 1303227464Smav sc = gp->softc; 1304227464Smav if (sc == NULL || sc->sc_stopping) 1305227464Smav continue; 1306227464Smav if (strcmp(gp->name, name) == 0) 1307167050Smjacob return (gp); 1308167050Smjacob } 1309167050Smjacob return (NULL); 1310167050Smjacob} 1311167050Smjacob 1312167050Smjacobstatic void 1313227464Smavg_multipath_ctl_stop(struct gctl_req *req, struct g_class *mp) 1314227464Smav{ 1315227464Smav struct g_geom *gp; 1316227464Smav const char *name; 1317227464Smav int error; 1318227464Smav 1319227464Smav g_topology_assert(); 1320227464Smav 1321227464Smav name = gctl_get_asciiparam(req, "arg0"); 1322227464Smav if (name == NULL) { 1323227464Smav gctl_error(req, "No 'arg0' argument"); 1324227464Smav return; 1325227464Smav } 1326227464Smav gp = g_multipath_find_geom(mp, name); 1327227464Smav if (gp == NULL) { 1328227464Smav gctl_error(req, "Device %s is invalid", name); 1329227464Smav return; 1330227464Smav } 1331227464Smav error = g_multipath_destroy(gp); 1332227464Smav if (error != 0 && error != EINPROGRESS) 1333227464Smav gctl_error(req, "failed to stop %s (err=%d)", name, error); 1334227464Smav} 1335227464Smav 1336227464Smavstatic void 1337167050Smjacobg_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp) 1338167050Smjacob{ 1339167050Smjacob struct g_geom *gp; 1340227464Smav struct g_multipath_softc *sc; 1341227464Smav struct g_consumer *cp; 1342227464Smav struct g_provider *pp; 1343167050Smjacob const char *name; 1344227464Smav uint8_t *buf; 1345167050Smjacob int error; 1346167050Smjacob 1347167050Smjacob g_topology_assert(); 1348167050Smjacob 1349167050Smjacob name = gctl_get_asciiparam(req, "arg0"); 1350167050Smjacob if (name == NULL) { 1351167050Smjacob gctl_error(req, "No 'arg0' argument"); 1352167050Smjacob return; 1353167050Smjacob } 1354167050Smjacob gp = g_multipath_find_geom(mp, name); 1355167050Smjacob if (gp == NULL) { 1356167050Smjacob gctl_error(req, "Device %s is invalid", name); 1357167050Smjacob return; 1358167050Smjacob } 1359227464Smav sc = gp->softc; 1360227464Smav 1361227464Smav if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) { 1362227464Smav cp = sc->sc_active; 1363227464Smav pp = cp->provider; 1364227464Smav error = g_access(cp, 1, 1, 1); 1365227464Smav if (error != 0) { 1366227464Smav gctl_error(req, "Can't open %s (%d)", pp->name, error); 1367227464Smav goto destroy; 1368227464Smav } 1369227464Smav g_topology_unlock(); 1370227464Smav buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 1371227464Smav error = g_write_data(cp, pp->mediasize - pp->sectorsize, 1372227464Smav buf, pp->sectorsize); 1373227464Smav g_topology_lock(); 1374227464Smav g_access(cp, -1, -1, -1); 1375227464Smav if (error != 0) 1376227464Smav gctl_error(req, "Can't erase metadata on %s (%d)", 1377227464Smav pp->name, error); 1378227464Smav } 1379227464Smav 1380227464Smavdestroy: 1381167050Smjacob error = g_multipath_destroy(gp); 1382227464Smav if (error != 0 && error != EINPROGRESS) 1383167050Smjacob gctl_error(req, "failed to destroy %s (err=%d)", name, error); 1384167050Smjacob} 1385167050Smjacob 1386167050Smjacobstatic void 1387205412Smjacobg_multipath_ctl_rotate(struct gctl_req *req, struct g_class *mp) 1388205412Smjacob{ 1389205412Smjacob struct g_geom *gp; 1390205412Smjacob const char *name; 1391205412Smjacob int error; 1392205412Smjacob 1393205412Smjacob g_topology_assert(); 1394205412Smjacob 1395205412Smjacob name = gctl_get_asciiparam(req, "arg0"); 1396205412Smjacob if (name == NULL) { 1397205412Smjacob gctl_error(req, "No 'arg0' argument"); 1398205412Smjacob return; 1399205412Smjacob } 1400205412Smjacob gp = g_multipath_find_geom(mp, name); 1401205412Smjacob if (gp == NULL) { 1402205412Smjacob gctl_error(req, "Device %s is invalid", name); 1403205412Smjacob return; 1404205412Smjacob } 1405205412Smjacob error = g_multipath_rotate(gp); 1406205412Smjacob if (error != 0) { 1407205412Smjacob gctl_error(req, "failed to rotate %s (err=%d)", name, error); 1408205412Smjacob } 1409205412Smjacob} 1410205412Smjacob 1411205412Smjacobstatic void 1412205412Smjacobg_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp) 1413205412Smjacob{ 1414205412Smjacob struct sbuf *sb; 1415205412Smjacob struct g_geom *gp; 1416205412Smjacob struct g_multipath_softc *sc; 1417227464Smav struct g_consumer *cp; 1418205412Smjacob const char *name; 1419227464Smav int empty; 1420205412Smjacob 1421205412Smjacob sb = sbuf_new_auto(); 1422205412Smjacob 1423205412Smjacob g_topology_assert(); 1424205412Smjacob name = gctl_get_asciiparam(req, "arg0"); 1425205412Smjacob if (name == NULL) { 1426205412Smjacob gctl_error(req, "No 'arg0' argument"); 1427205412Smjacob return; 1428205412Smjacob } 1429205412Smjacob gp = g_multipath_find_geom(mp, name); 1430205412Smjacob if (gp == NULL) { 1431205412Smjacob gctl_error(req, "Device %s is invalid", name); 1432205412Smjacob return; 1433205412Smjacob } 1434205412Smjacob sc = gp->softc; 1435234415Smav if (sc->sc_active_active == 1) { 1436227464Smav empty = 1; 1437227464Smav LIST_FOREACH(cp, &gp->consumer, consumer) { 1438227464Smav if (cp->index & MP_BAD) 1439227464Smav continue; 1440227464Smav if (!empty) 1441227464Smav sbuf_cat(sb, " "); 1442227464Smav sbuf_cat(sb, cp->provider->name); 1443227464Smav empty = 0; 1444227464Smav } 1445227464Smav if (empty) 1446227464Smav sbuf_cat(sb, "none"); 1447227464Smav sbuf_cat(sb, "\n"); 1448227464Smav } else if (sc->sc_active && sc->sc_active->provider) { 1449227464Smav sbuf_printf(sb, "%s\n", sc->sc_active->provider->name); 1450205412Smjacob } else { 1451205412Smjacob sbuf_printf(sb, "none\n"); 1452205412Smjacob } 1453205412Smjacob sbuf_finish(sb); 1454205412Smjacob gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 1455205412Smjacob sbuf_delete(sb); 1456205412Smjacob} 1457205412Smjacob 1458205412Smjacobstatic void 1459167050Smjacobg_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb) 1460167050Smjacob{ 1461167050Smjacob uint32_t *version; 1462167050Smjacob g_topology_assert(); 1463167050Smjacob version = gctl_get_paraml(req, "version", sizeof(*version)); 1464167050Smjacob if (version == NULL) { 1465167050Smjacob gctl_error(req, "No 'version' argument"); 1466167050Smjacob } else if (*version != G_MULTIPATH_VERSION) { 1467167050Smjacob gctl_error(req, "Userland and kernel parts are out of sync"); 1468205847Smjacob } else if (strcmp(verb, "add") == 0) { 1469205847Smjacob g_multipath_ctl_add(req, mp); 1470239012Sthomas } else if (strcmp(verb, "prefer") == 0) { 1471239012Sthomas g_multipath_ctl_prefer(req, mp); 1472227464Smav } else if (strcmp(verb, "create") == 0) { 1473227464Smav g_multipath_ctl_create(req, mp); 1474234415Smav } else if (strcmp(verb, "configure") == 0) { 1475234415Smav g_multipath_ctl_configure(req, mp); 1476227464Smav } else if (strcmp(verb, "stop") == 0) { 1477227464Smav g_multipath_ctl_stop(req, mp); 1478167050Smjacob } else if (strcmp(verb, "destroy") == 0) { 1479167050Smjacob g_multipath_ctl_destroy(req, mp); 1480227464Smav } else if (strcmp(verb, "fail") == 0) { 1481227464Smav g_multipath_ctl_fail(req, mp, 1); 1482227464Smav } else if (strcmp(verb, "restore") == 0) { 1483227464Smav g_multipath_ctl_fail(req, mp, 0); 1484227464Smav } else if (strcmp(verb, "remove") == 0) { 1485227464Smav g_multipath_ctl_remove(req, mp); 1486205412Smjacob } else if (strcmp(verb, "rotate") == 0) { 1487205412Smjacob g_multipath_ctl_rotate(req, mp); 1488205412Smjacob } else if (strcmp(verb, "getactive") == 0) { 1489205412Smjacob g_multipath_ctl_getactive(req, mp); 1490167050Smjacob } else { 1491167050Smjacob gctl_error(req, "Unknown verb %s", verb); 1492167050Smjacob } 1493167050Smjacob} 1494227464Smav 1495227464Smavstatic void 1496227464Smavg_multipath_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 1497227464Smav struct g_consumer *cp, struct g_provider *pp) 1498227464Smav{ 1499227464Smav struct g_multipath_softc *sc; 1500227464Smav int good; 1501227464Smav 1502227464Smav g_topology_assert(); 1503227464Smav 1504227464Smav sc = gp->softc; 1505227464Smav if (sc == NULL) 1506227464Smav return; 1507227464Smav if (cp != NULL) { 1508236619Smav sbuf_printf(sb, "%s<State>%s</State>\n", indent, 1509227464Smav (cp->index & MP_NEW) ? "NEW" : 1510227464Smav (cp->index & MP_LOST) ? "LOST" : 1511227464Smav (cp->index & MP_FAIL) ? "FAIL" : 1512234415Smav (sc->sc_active_active == 1 || sc->sc_active == cp) ? 1513234415Smav "ACTIVE" : 1514234415Smav sc->sc_active_active == 2 ? "READ" : "PASSIVE"); 1515227464Smav } else { 1516227464Smav good = g_multipath_good(gp); 1517236619Smav sbuf_printf(sb, "%s<State>%s</State>\n", indent, 1518227464Smav good == 0 ? "BROKEN" : 1519227464Smav (good != sc->sc_ndisks || sc->sc_ndisks == 1) ? 1520227464Smav "DEGRADED" : "OPTIMAL"); 1521227464Smav } 1522227464Smav if (cp == NULL && pp == NULL) { 1523236619Smav sbuf_printf(sb, "%s<UUID>%s</UUID>\n", indent, sc->sc_uuid); 1524236619Smav sbuf_printf(sb, "%s<Mode>Active/%s</Mode>\n", indent, 1525234415Smav sc->sc_active_active == 2 ? "Read" : 1526234415Smav sc->sc_active_active == 1 ? "Active" : "Passive"); 1527236619Smav sbuf_printf(sb, "%s<Type>%s</Type>\n", indent, 1528227464Smav sc->sc_uuid[0] == 0 ? "MANUAL" : "AUTOMATIC"); 1529227464Smav } 1530227464Smav} 1531227464Smav 1532167050SmjacobDECLARE_GEOM_CLASS(g_multipath_class, g_multipath); 1533