1fa4a1febSPawel Jakub Dawidek /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 33728855aSPedro F. Giffuni * 49bfdf598SPawel Jakub Dawidek * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5fa4a1febSPawel Jakub Dawidek * All rights reserved. 6fa4a1febSPawel Jakub Dawidek * 7fa4a1febSPawel Jakub Dawidek * Redistribution and use in source and binary forms, with or without 8fa4a1febSPawel Jakub Dawidek * modification, are permitted provided that the following conditions 9fa4a1febSPawel Jakub Dawidek * are met: 10fa4a1febSPawel Jakub Dawidek * 1. Redistributions of source code must retain the above copyright 11fa4a1febSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer. 12fa4a1febSPawel Jakub Dawidek * 2. Redistributions in binary form must reproduce the above copyright 13fa4a1febSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer in the 14fa4a1febSPawel Jakub Dawidek * documentation and/or other materials provided with the distribution. 15fa4a1febSPawel Jakub Dawidek * 16fa4a1febSPawel Jakub Dawidek * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17fa4a1febSPawel Jakub Dawidek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18fa4a1febSPawel Jakub Dawidek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19fa4a1febSPawel Jakub Dawidek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20fa4a1febSPawel Jakub Dawidek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21fa4a1febSPawel Jakub Dawidek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22fa4a1febSPawel Jakub Dawidek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23fa4a1febSPawel Jakub Dawidek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24fa4a1febSPawel Jakub Dawidek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25fa4a1febSPawel Jakub Dawidek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26fa4a1febSPawel Jakub Dawidek * SUCH DAMAGE. 27fa4a1febSPawel Jakub Dawidek */ 28fa4a1febSPawel Jakub Dawidek 29fa4a1febSPawel Jakub Dawidek #include <sys/param.h> 30fa4a1febSPawel Jakub Dawidek #include <sys/systm.h> 318b0a00b7SMark Johnston #include <sys/bio.h> 328b0a00b7SMark Johnston #include <sys/eventhandler.h> 3340c5032dSMark Johnston #include <sys/fail.h> 34fa4a1febSPawel Jakub Dawidek #include <sys/kernel.h> 358b0a00b7SMark Johnston #include <sys/kthread.h> 36fa4a1febSPawel Jakub Dawidek #include <sys/limits.h> 37fa4a1febSPawel Jakub Dawidek #include <sys/lock.h> 38fa4a1febSPawel Jakub Dawidek #include <sys/malloc.h> 398b0a00b7SMark Johnston #include <sys/mutex.h> 40fa4a1febSPawel Jakub Dawidek #include <sys/proc.h> 414eb861d3SMitchell Horne #include <sys/reboot.h> 428b0a00b7SMark Johnston #include <sys/sbuf.h> 4363710c4dSJohn Baldwin #include <sys/sched.h> 448b0a00b7SMark Johnston #include <sys/sx.h> 458b0a00b7SMark Johnston #include <sys/sysctl.h> 468b0a00b7SMark Johnston 478b0a00b7SMark Johnston #include <geom/geom.h> 48ac03832eSConrad Meyer #include <geom/geom_dbg.h> 499309a460SAlan Somers #include <geom/geom_disk.h> 50fa4a1febSPawel Jakub Dawidek #include <geom/mirror/g_mirror.h> 51fa4a1febSPawel Jakub Dawidek 52cb08c2ccSAlexander Leidinger FEATURE(geom_mirror, "GEOM mirroring support"); 53fa4a1febSPawel Jakub Dawidek 545bb84bc8SRobert Watson static MALLOC_DEFINE(M_MIRROR, "mirror_data", "GEOM_MIRROR Data"); 55fa4a1febSPawel Jakub Dawidek 56fa4a1febSPawel Jakub Dawidek SYSCTL_DECL(_kern_geom); 577029da5cSPawel Biernacki static SYSCTL_NODE(_kern_geom, OID_AUTO, mirror, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 586472ac3dSEd Schouten "GEOM_MIRROR stuff"); 5903498171SMark Johnston int g_mirror_debug = 0; 6003498171SMark Johnston SYSCTL_INT(_kern_geom_mirror, OID_AUTO, debug, CTLFLAG_RWTUN, &g_mirror_debug, 0, 61fa4a1febSPawel Jakub Dawidek "Debug level"); 62af7dcae0SConrad Meyer bool g_launch_mirror_before_timeout = true; 63af7dcae0SConrad Meyer SYSCTL_BOOL(_kern_geom_mirror, OID_AUTO, launch_mirror_before_timeout, 64af7dcae0SConrad Meyer CTLFLAG_RWTUN, &g_launch_mirror_before_timeout, 0, 65af7dcae0SConrad Meyer "If false, force gmirror to wait out the full kern.geom.mirror.timeout " 66af7dcae0SConrad Meyer "before launching mirrors"); 67cf41526bSPawel Jakub Dawidek static u_int g_mirror_timeout = 4; 68af3b2549SHans Petter Selasky SYSCTL_UINT(_kern_geom_mirror, OID_AUTO, timeout, CTLFLAG_RWTUN, &g_mirror_timeout, 69fa4a1febSPawel Jakub Dawidek 0, "Time to wait on all mirror components"); 7014089daeSPawel Jakub Dawidek static u_int g_mirror_idletime = 5; 71af3b2549SHans Petter Selasky SYSCTL_UINT(_kern_geom_mirror, OID_AUTO, idletime, CTLFLAG_RWTUN, 7214089daeSPawel Jakub Dawidek &g_mirror_idletime, 0, "Mark components as clean when idling"); 73d4b0268aSPawel Jakub Dawidek static u_int g_mirror_disconnect_on_failure = 1; 74af3b2549SHans Petter Selasky SYSCTL_UINT(_kern_geom_mirror, OID_AUTO, disconnect_on_failure, CTLFLAG_RWTUN, 75d4b0268aSPawel Jakub Dawidek &g_mirror_disconnect_on_failure, 0, "Disconnect component on I/O failure."); 7618d370acSPawel Jakub Dawidek static u_int g_mirror_syncreqs = 2; 77855761d5SPawel Jakub Dawidek SYSCTL_UINT(_kern_geom_mirror, OID_AUTO, sync_requests, CTLFLAG_RDTUN, 78855761d5SPawel Jakub Dawidek &g_mirror_syncreqs, 0, "Parallel synchronization I/O requests."); 792ceafb77SMark Johnston static u_int g_mirror_sync_period = 5; 802ceafb77SMark Johnston SYSCTL_UINT(_kern_geom_mirror, OID_AUTO, sync_update_period, CTLFLAG_RWTUN, 812ceafb77SMark Johnston &g_mirror_sync_period, 0, 82204d94f1SMark Johnston "Metadata update period during synchronization, in seconds"); 83fa4a1febSPawel Jakub Dawidek 84fa4a1febSPawel Jakub Dawidek #define MSLEEP(ident, mtx, priority, wmesg, timeout) do { \ 85fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Sleeping %p.", __func__, (ident)); \ 86fa4a1febSPawel Jakub Dawidek msleep((ident), (mtx), (priority), (wmesg), (timeout)); \ 87fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Woken up %p.", __func__, (ident)); \ 88fa4a1febSPawel Jakub Dawidek } while (0) 89fa4a1febSPawel Jakub Dawidek 90cbab6161SAlexander Motin static eventhandler_tag g_mirror_post_sync = NULL; 91cbab6161SAlexander Motin static int g_mirror_shutdown = 0; 92fa4a1febSPawel Jakub Dawidek 93dc399583SAlexander Motin static g_ctl_destroy_geom_t g_mirror_destroy_geom; 94fa4a1febSPawel Jakub Dawidek static g_taste_t g_mirror_taste; 95dc399583SAlexander Motin static g_init_t g_mirror_init; 96dc399583SAlexander Motin static g_fini_t g_mirror_fini; 97dc399583SAlexander Motin static g_provgone_t g_mirror_providergone; 9832cea4caSAndrey V. Elsukov static g_resize_t g_mirror_resize; 99fa4a1febSPawel Jakub Dawidek 100fa4a1febSPawel Jakub Dawidek struct g_class g_mirror_class = { 101fa4a1febSPawel Jakub Dawidek .name = G_MIRROR_CLASS_NAME, 1025721c9c7SPoul-Henning Kamp .version = G_VERSION, 103fa4a1febSPawel Jakub Dawidek .ctlreq = g_mirror_config, 104fa4a1febSPawel Jakub Dawidek .taste = g_mirror_taste, 1056349471bSPawel Jakub Dawidek .destroy_geom = g_mirror_destroy_geom, 1066349471bSPawel Jakub Dawidek .init = g_mirror_init, 10732cea4caSAndrey V. Elsukov .fini = g_mirror_fini, 108dc399583SAlexander Motin .providergone = g_mirror_providergone, 10932cea4caSAndrey V. Elsukov .resize = g_mirror_resize 110fa4a1febSPawel Jakub Dawidek }; 111fa4a1febSPawel Jakub Dawidek 112fa4a1febSPawel Jakub Dawidek static void g_mirror_destroy_provider(struct g_mirror_softc *sc); 1139eec299fSPawel Jakub Dawidek static int g_mirror_update_disk(struct g_mirror_disk *disk, u_int state); 114fff048e4SMark Johnston static void g_mirror_update_device(struct g_mirror_softc *sc, bool force); 115fa4a1febSPawel Jakub Dawidek static void g_mirror_dumpconf(struct sbuf *sb, const char *indent, 116fa4a1febSPawel Jakub Dawidek struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); 1172f1cfb7fSMark Johnston static void g_mirror_timeout_drain(struct g_mirror_softc *sc); 118af7dcae0SConrad Meyer static int g_mirror_refresh_device(struct g_mirror_softc *sc, 119af7dcae0SConrad Meyer const struct g_provider *pp, const struct g_mirror_metadata *md); 120762f440fSMark Johnston static void g_mirror_sync_reinit(const struct g_mirror_disk *disk, 121762f440fSMark Johnston struct bio *bp, off_t offset); 122fa4a1febSPawel Jakub Dawidek static void g_mirror_sync_stop(struct g_mirror_disk *disk, int type); 1231787c3feSMark Johnston static void g_mirror_register_request(struct g_mirror_softc *sc, 1241787c3feSMark Johnston struct bio *bp); 125855761d5SPawel Jakub Dawidek static void g_mirror_sync_release(struct g_mirror_softc *sc); 126fa4a1febSPawel Jakub Dawidek 127fa4a1febSPawel Jakub Dawidek static const char * 128fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(int state) 129fa4a1febSPawel Jakub Dawidek { 130fa4a1febSPawel Jakub Dawidek 131fa4a1febSPawel Jakub Dawidek switch (state) { 132fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_NONE: 133fa4a1febSPawel Jakub Dawidek return ("NONE"); 134fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_NEW: 135fa4a1febSPawel Jakub Dawidek return ("NEW"); 136fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_ACTIVE: 137fa4a1febSPawel Jakub Dawidek return ("ACTIVE"); 138fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_STALE: 139fa4a1febSPawel Jakub Dawidek return ("STALE"); 140fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_SYNCHRONIZING: 141fa4a1febSPawel Jakub Dawidek return ("SYNCHRONIZING"); 142fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_DISCONNECTED: 143fa4a1febSPawel Jakub Dawidek return ("DISCONNECTED"); 144fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_DESTROY: 145fa4a1febSPawel Jakub Dawidek return ("DESTROY"); 146fa4a1febSPawel Jakub Dawidek default: 147fa4a1febSPawel Jakub Dawidek return ("INVALID"); 148fa4a1febSPawel Jakub Dawidek } 149fa4a1febSPawel Jakub Dawidek } 150fa4a1febSPawel Jakub Dawidek 151fa4a1febSPawel Jakub Dawidek static const char * 152fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(int state) 153fa4a1febSPawel Jakub Dawidek { 154fa4a1febSPawel Jakub Dawidek 155fa4a1febSPawel Jakub Dawidek switch (state) { 156fa4a1febSPawel Jakub Dawidek case G_MIRROR_DEVICE_STATE_STARTING: 157fa4a1febSPawel Jakub Dawidek return ("STARTING"); 158fa4a1febSPawel Jakub Dawidek case G_MIRROR_DEVICE_STATE_RUNNING: 159fa4a1febSPawel Jakub Dawidek return ("RUNNING"); 160fa4a1febSPawel Jakub Dawidek default: 161fa4a1febSPawel Jakub Dawidek return ("INVALID"); 162fa4a1febSPawel Jakub Dawidek } 163fa4a1febSPawel Jakub Dawidek } 164fa4a1febSPawel Jakub Dawidek 165fa4a1febSPawel Jakub Dawidek static const char * 166fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(struct g_mirror_disk *disk) 167fa4a1febSPawel Jakub Dawidek { 168fa4a1febSPawel Jakub Dawidek 169fa4a1febSPawel Jakub Dawidek if (disk->d_consumer == NULL || disk->d_consumer->provider == NULL) 170fa4a1febSPawel Jakub Dawidek return ("[unknown]"); 171fa4a1febSPawel Jakub Dawidek return (disk->d_name); 172fa4a1febSPawel Jakub Dawidek } 173fa4a1febSPawel Jakub Dawidek 174fa4a1febSPawel Jakub Dawidek /* 175fa4a1febSPawel Jakub Dawidek * --- Events handling functions --- 176fa4a1febSPawel Jakub Dawidek * Events in geom_mirror are used to maintain disks and device status 177fa4a1febSPawel Jakub Dawidek * from one thread to simplify locking. 178fa4a1febSPawel Jakub Dawidek */ 179fa4a1febSPawel Jakub Dawidek static void 180fa4a1febSPawel Jakub Dawidek g_mirror_event_free(struct g_mirror_event *ep) 181fa4a1febSPawel Jakub Dawidek { 182fa4a1febSPawel Jakub Dawidek 183fa4a1febSPawel Jakub Dawidek free(ep, M_MIRROR); 184fa4a1febSPawel Jakub Dawidek } 185fa4a1febSPawel Jakub Dawidek 1862f1cfb7fSMark Johnston static int 1872f1cfb7fSMark Johnston g_mirror_event_dispatch(struct g_mirror_event *ep, void *arg, int state, 1882f1cfb7fSMark Johnston int flags) 189fa4a1febSPawel Jakub Dawidek { 190fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 191fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 192fa4a1febSPawel Jakub Dawidek int error; 193fa4a1febSPawel Jakub Dawidek 194fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Sending event %p.", __func__, ep); 195fa4a1febSPawel Jakub Dawidek if ((flags & G_MIRROR_EVENT_DEVICE) != 0) { 196fa4a1febSPawel Jakub Dawidek disk = NULL; 197fa4a1febSPawel Jakub Dawidek sc = arg; 198fa4a1febSPawel Jakub Dawidek } else { 199fa4a1febSPawel Jakub Dawidek disk = arg; 200fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 201fa4a1febSPawel Jakub Dawidek } 202fa4a1febSPawel Jakub Dawidek ep->e_disk = disk; 203fa4a1febSPawel Jakub Dawidek ep->e_state = state; 204fa4a1febSPawel Jakub Dawidek ep->e_flags = flags; 205fa4a1febSPawel Jakub Dawidek ep->e_error = 0; 206fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 207fa4a1febSPawel Jakub Dawidek TAILQ_INSERT_TAIL(&sc->sc_events, ep, e_next); 208fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 209fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); 210fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 211fa4a1febSPawel Jakub Dawidek wakeup(sc); 212fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 213fa4a1febSPawel Jakub Dawidek if ((flags & G_MIRROR_EVENT_DONTWAIT) != 0) 214fa4a1febSPawel Jakub Dawidek return (0); 215fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Sleeping %p.", __func__, ep); 216855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 217fa4a1febSPawel Jakub Dawidek while ((ep->e_flags & G_MIRROR_EVENT_DONE) == 0) { 218fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 219fa4a1febSPawel Jakub Dawidek MSLEEP(ep, &sc->sc_events_mtx, PRIBIO | PDROP, "m:event", 220fa4a1febSPawel Jakub Dawidek hz * 5); 221fa4a1febSPawel Jakub Dawidek } 222fa4a1febSPawel Jakub Dawidek error = ep->e_error; 223fa4a1febSPawel Jakub Dawidek g_mirror_event_free(ep); 224855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 225fa4a1febSPawel Jakub Dawidek return (error); 226fa4a1febSPawel Jakub Dawidek } 227fa4a1febSPawel Jakub Dawidek 2282f1cfb7fSMark Johnston int 2292f1cfb7fSMark Johnston g_mirror_event_send(void *arg, int state, int flags) 2302f1cfb7fSMark Johnston { 2312f1cfb7fSMark Johnston struct g_mirror_event *ep; 2322f1cfb7fSMark Johnston 2332f1cfb7fSMark Johnston ep = malloc(sizeof(*ep), M_MIRROR, M_WAITOK); 2342f1cfb7fSMark Johnston return (g_mirror_event_dispatch(ep, arg, state, flags)); 2352f1cfb7fSMark Johnston } 2362f1cfb7fSMark Johnston 237fa4a1febSPawel Jakub Dawidek static struct g_mirror_event * 238b634781eSMark Johnston g_mirror_event_first(struct g_mirror_softc *sc) 239fa4a1febSPawel Jakub Dawidek { 240fa4a1febSPawel Jakub Dawidek struct g_mirror_event *ep; 241fa4a1febSPawel Jakub Dawidek 242fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 243fa4a1febSPawel Jakub Dawidek ep = TAILQ_FIRST(&sc->sc_events); 244fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 245fa4a1febSPawel Jakub Dawidek return (ep); 246fa4a1febSPawel Jakub Dawidek } 247fa4a1febSPawel Jakub Dawidek 248f663832bSPawel Jakub Dawidek static void 249f663832bSPawel Jakub Dawidek g_mirror_event_remove(struct g_mirror_softc *sc, struct g_mirror_event *ep) 250f663832bSPawel Jakub Dawidek { 251f663832bSPawel Jakub Dawidek 252f663832bSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 253f663832bSPawel Jakub Dawidek TAILQ_REMOVE(&sc->sc_events, ep, e_next); 254f663832bSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 255f663832bSPawel Jakub Dawidek } 256f663832bSPawel Jakub Dawidek 257fa4a1febSPawel Jakub Dawidek static void 258fa4a1febSPawel Jakub Dawidek g_mirror_event_cancel(struct g_mirror_disk *disk) 259fa4a1febSPawel Jakub Dawidek { 260fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 261fa4a1febSPawel Jakub Dawidek struct g_mirror_event *ep, *tmpep; 262fa4a1febSPawel Jakub Dawidek 263fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 264855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 265855761d5SPawel Jakub Dawidek 266fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 267fa4a1febSPawel Jakub Dawidek TAILQ_FOREACH_SAFE(ep, &sc->sc_events, e_next, tmpep) { 268fa4a1febSPawel Jakub Dawidek if ((ep->e_flags & G_MIRROR_EVENT_DEVICE) != 0) 269fa4a1febSPawel Jakub Dawidek continue; 270fa4a1febSPawel Jakub Dawidek if (ep->e_disk != disk) 271fa4a1febSPawel Jakub Dawidek continue; 272fa4a1febSPawel Jakub Dawidek TAILQ_REMOVE(&sc->sc_events, ep, e_next); 273fa4a1febSPawel Jakub Dawidek if ((ep->e_flags & G_MIRROR_EVENT_DONTWAIT) != 0) 274fa4a1febSPawel Jakub Dawidek g_mirror_event_free(ep); 275fa4a1febSPawel Jakub Dawidek else { 276fa4a1febSPawel Jakub Dawidek ep->e_error = ECANCELED; 277fa4a1febSPawel Jakub Dawidek wakeup(ep); 278fa4a1febSPawel Jakub Dawidek } 279fa4a1febSPawel Jakub Dawidek } 280fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 281fa4a1febSPawel Jakub Dawidek } 282fa4a1febSPawel Jakub Dawidek 283fa4a1febSPawel Jakub Dawidek /* 284fa4a1febSPawel Jakub Dawidek * Return the number of disks in given state. 285fa4a1febSPawel Jakub Dawidek * If state is equal to -1, count all connected disks. 286fa4a1febSPawel Jakub Dawidek */ 287fa4a1febSPawel Jakub Dawidek u_int 288fa4a1febSPawel Jakub Dawidek g_mirror_ndisks(struct g_mirror_softc *sc, int state) 289fa4a1febSPawel Jakub Dawidek { 290fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 291fa4a1febSPawel Jakub Dawidek u_int n = 0; 292fa4a1febSPawel Jakub Dawidek 293fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 294fa4a1febSPawel Jakub Dawidek if (state == -1 || disk->d_state == state) 295fa4a1febSPawel Jakub Dawidek n++; 296fa4a1febSPawel Jakub Dawidek } 297fa4a1febSPawel Jakub Dawidek return (n); 298fa4a1febSPawel Jakub Dawidek } 299fa4a1febSPawel Jakub Dawidek 300fa4a1febSPawel Jakub Dawidek /* 301fa4a1febSPawel Jakub Dawidek * Find a disk in mirror by its disk ID. 302fa4a1febSPawel Jakub Dawidek */ 303fa4a1febSPawel Jakub Dawidek static struct g_mirror_disk * 304fa4a1febSPawel Jakub Dawidek g_mirror_id2disk(struct g_mirror_softc *sc, uint32_t id) 305fa4a1febSPawel Jakub Dawidek { 306fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 307fa4a1febSPawel Jakub Dawidek 308855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 309fa4a1febSPawel Jakub Dawidek 310fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 311fa4a1febSPawel Jakub Dawidek if (disk->d_id == id) 312fa4a1febSPawel Jakub Dawidek return (disk); 313fa4a1febSPawel Jakub Dawidek } 314fa4a1febSPawel Jakub Dawidek return (NULL); 315fa4a1febSPawel Jakub Dawidek } 316fa4a1febSPawel Jakub Dawidek 317fa4a1febSPawel Jakub Dawidek static u_int 318fa4a1febSPawel Jakub Dawidek g_mirror_nrequests(struct g_mirror_softc *sc, struct g_consumer *cp) 319fa4a1febSPawel Jakub Dawidek { 320fa4a1febSPawel Jakub Dawidek struct bio *bp; 321fa4a1febSPawel Jakub Dawidek u_int nreqs = 0; 322fa4a1febSPawel Jakub Dawidek 323fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 3249abe2e7eSMark Johnston TAILQ_FOREACH(bp, &sc->sc_queue, bio_queue) { 325fa4a1febSPawel Jakub Dawidek if (bp->bio_from == cp) 326fa4a1febSPawel Jakub Dawidek nreqs++; 327fa4a1febSPawel Jakub Dawidek } 328fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 329fa4a1febSPawel Jakub Dawidek return (nreqs); 330fa4a1febSPawel Jakub Dawidek } 331fa4a1febSPawel Jakub Dawidek 3326d8fb92dSPawel Jakub Dawidek static int 3336d8fb92dSPawel Jakub Dawidek g_mirror_is_busy(struct g_mirror_softc *sc, struct g_consumer *cp) 3346d8fb92dSPawel Jakub Dawidek { 3356d8fb92dSPawel Jakub Dawidek 3362fdf5be1SPawel Jakub Dawidek if (cp->index > 0) { 3376d8fb92dSPawel Jakub Dawidek G_MIRROR_DEBUG(2, 3386d8fb92dSPawel Jakub Dawidek "I/O requests for %s exist, can't destroy it now.", 3396d8fb92dSPawel Jakub Dawidek cp->provider->name); 3406d8fb92dSPawel Jakub Dawidek return (1); 3416d8fb92dSPawel Jakub Dawidek } 3426d8fb92dSPawel Jakub Dawidek if (g_mirror_nrequests(sc, cp) > 0) { 3436d8fb92dSPawel Jakub Dawidek G_MIRROR_DEBUG(2, 3446d8fb92dSPawel Jakub Dawidek "I/O requests for %s in queue, can't destroy it now.", 3456d8fb92dSPawel Jakub Dawidek cp->provider->name); 3466d8fb92dSPawel Jakub Dawidek return (1); 3476d8fb92dSPawel Jakub Dawidek } 3486d8fb92dSPawel Jakub Dawidek return (0); 3496d8fb92dSPawel Jakub Dawidek } 3506d8fb92dSPawel Jakub Dawidek 351fa4a1febSPawel Jakub Dawidek static void 352a2a7b44dSPawel Jakub Dawidek g_mirror_destroy_consumer(void *arg, int flags __unused) 3539eec299fSPawel Jakub Dawidek { 3549eec299fSPawel Jakub Dawidek struct g_consumer *cp; 3559eec299fSPawel Jakub Dawidek 356855761d5SPawel Jakub Dawidek g_topology_assert(); 357855761d5SPawel Jakub Dawidek 3589eec299fSPawel Jakub Dawidek cp = arg; 3599eec299fSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Consumer %s destroyed.", cp->provider->name); 3609eec299fSPawel Jakub Dawidek g_detach(cp); 3619eec299fSPawel Jakub Dawidek g_destroy_consumer(cp); 3629eec299fSPawel Jakub Dawidek } 3639eec299fSPawel Jakub Dawidek 3649eec299fSPawel Jakub Dawidek static void 365fa4a1febSPawel Jakub Dawidek g_mirror_kill_consumer(struct g_mirror_softc *sc, struct g_consumer *cp) 366fa4a1febSPawel Jakub Dawidek { 3679eec299fSPawel Jakub Dawidek struct g_provider *pp; 3689eec299fSPawel Jakub Dawidek int retaste_wait; 369fa4a1febSPawel Jakub Dawidek 370fa4a1febSPawel Jakub Dawidek g_topology_assert(); 371fa4a1febSPawel Jakub Dawidek 372fa4a1febSPawel Jakub Dawidek cp->private = NULL; 3736d8fb92dSPawel Jakub Dawidek if (g_mirror_is_busy(sc, cp)) 374fa4a1febSPawel Jakub Dawidek return; 3759eec299fSPawel Jakub Dawidek pp = cp->provider; 3769eec299fSPawel Jakub Dawidek retaste_wait = 0; 3779eec299fSPawel Jakub Dawidek if (cp->acw == 1) { 3789eec299fSPawel Jakub Dawidek if ((pp->geom->flags & G_GEOM_WITHER) == 0) 3799eec299fSPawel Jakub Dawidek retaste_wait = 1; 3809eec299fSPawel Jakub Dawidek } 3819eec299fSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Access %s r%dw%de%d = %d", pp->name, -cp->acr, 3829eec299fSPawel Jakub Dawidek -cp->acw, -cp->ace, 0); 383a2a7b44dSPawel Jakub Dawidek if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 3849eec299fSPawel Jakub Dawidek g_access(cp, -cp->acr, -cp->acw, -cp->ace); 3859eec299fSPawel Jakub Dawidek if (retaste_wait) { 3869eec299fSPawel Jakub Dawidek /* 3879eec299fSPawel Jakub Dawidek * After retaste event was send (inside g_access()), we can send 3889eec299fSPawel Jakub Dawidek * event to detach and destroy consumer. 3899eec299fSPawel Jakub Dawidek * A class, which has consumer to the given provider connected 3909eec299fSPawel Jakub Dawidek * will not receive retaste event for the provider. 3919eec299fSPawel Jakub Dawidek * This is the way how I ignore retaste events when I close 3929eec299fSPawel Jakub Dawidek * consumers opened for write: I detach and destroy consumer 3939eec299fSPawel Jakub Dawidek * after retaste event is sent. 3949eec299fSPawel Jakub Dawidek */ 3959eec299fSPawel Jakub Dawidek g_post_event(g_mirror_destroy_consumer, cp, M_WAITOK, NULL); 3969eec299fSPawel Jakub Dawidek return; 3979eec299fSPawel Jakub Dawidek } 3989eec299fSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Consumer %s destroyed.", pp->name); 399fa4a1febSPawel Jakub Dawidek g_detach(cp); 400fa4a1febSPawel Jakub Dawidek g_destroy_consumer(cp); 401fa4a1febSPawel Jakub Dawidek } 402fa4a1febSPawel Jakub Dawidek 403fa4a1febSPawel Jakub Dawidek static int 404fa4a1febSPawel Jakub Dawidek g_mirror_connect_disk(struct g_mirror_disk *disk, struct g_provider *pp) 405fa4a1febSPawel Jakub Dawidek { 406c2ca1093SPawel Jakub Dawidek struct g_consumer *cp; 407fa4a1febSPawel Jakub Dawidek int error; 408fa4a1febSPawel Jakub Dawidek 409855761d5SPawel Jakub Dawidek g_topology_assert_not(); 410fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_consumer == NULL, 411fa4a1febSPawel Jakub Dawidek ("Disk already connected (device %s).", disk->d_softc->sc_name)); 412fa4a1febSPawel Jakub Dawidek 413855761d5SPawel Jakub Dawidek g_topology_lock(); 414c2ca1093SPawel Jakub Dawidek cp = g_new_consumer(disk->d_softc->sc_geom); 41540ea77a0SAlexander Motin cp->flags |= G_CF_DIRECT_RECEIVE; 416c2ca1093SPawel Jakub Dawidek error = g_attach(cp, pp); 4179eec299fSPawel Jakub Dawidek if (error != 0) { 418c2ca1093SPawel Jakub Dawidek g_destroy_consumer(cp); 419855761d5SPawel Jakub Dawidek g_topology_unlock(); 420c2ca1093SPawel Jakub Dawidek return (error); 421c2ca1093SPawel Jakub Dawidek } 422c2ca1093SPawel Jakub Dawidek error = g_access(cp, 1, 1, 1); 423c2ca1093SPawel Jakub Dawidek if (error != 0) { 424c2ca1093SPawel Jakub Dawidek g_detach(cp); 425c2ca1093SPawel Jakub Dawidek g_destroy_consumer(cp); 426855761d5SPawel Jakub Dawidek g_topology_unlock(); 4279eec299fSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Cannot open consumer %s (error=%d).", 4289eec299fSPawel Jakub Dawidek pp->name, error); 4299eec299fSPawel Jakub Dawidek return (error); 4309eec299fSPawel Jakub Dawidek } 431855761d5SPawel Jakub Dawidek g_topology_unlock(); 432c2ca1093SPawel Jakub Dawidek disk->d_consumer = cp; 433c2ca1093SPawel Jakub Dawidek disk->d_consumer->private = disk; 434c2ca1093SPawel Jakub Dawidek disk->d_consumer->index = 0; 4359eec299fSPawel Jakub Dawidek 436fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Disk %s connected.", g_mirror_get_diskname(disk)); 437fa4a1febSPawel Jakub Dawidek return (0); 438fa4a1febSPawel Jakub Dawidek } 439fa4a1febSPawel Jakub Dawidek 440fa4a1febSPawel Jakub Dawidek static void 4416d8fb92dSPawel Jakub Dawidek g_mirror_disconnect_consumer(struct g_mirror_softc *sc, struct g_consumer *cp) 442fa4a1febSPawel Jakub Dawidek { 443fa4a1febSPawel Jakub Dawidek 444fa4a1febSPawel Jakub Dawidek g_topology_assert(); 445fa4a1febSPawel Jakub Dawidek 446fa4a1febSPawel Jakub Dawidek if (cp == NULL) 447fa4a1febSPawel Jakub Dawidek return; 4489eec299fSPawel Jakub Dawidek if (cp->provider != NULL) 4496d8fb92dSPawel Jakub Dawidek g_mirror_kill_consumer(sc, cp); 4509eec299fSPawel Jakub Dawidek else 451fa4a1febSPawel Jakub Dawidek g_destroy_consumer(cp); 452fa4a1febSPawel Jakub Dawidek } 453fa4a1febSPawel Jakub Dawidek 454fa4a1febSPawel Jakub Dawidek /* 455fa4a1febSPawel Jakub Dawidek * Initialize disk. This means allocate memory, create consumer, attach it 456fa4a1febSPawel Jakub Dawidek * to the provider and open access (r1w1e1) to it. 457fa4a1febSPawel Jakub Dawidek */ 458fa4a1febSPawel Jakub Dawidek static struct g_mirror_disk * 459fa4a1febSPawel Jakub Dawidek g_mirror_init_disk(struct g_mirror_softc *sc, struct g_provider *pp, 460fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata *md, int *errorp) 461fa4a1febSPawel Jakub Dawidek { 462fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 463d89862acSGleb Smirnoff int i, error; 464fa4a1febSPawel Jakub Dawidek 465fa4a1febSPawel Jakub Dawidek disk = malloc(sizeof(*disk), M_MIRROR, M_NOWAIT | M_ZERO); 466fa4a1febSPawel Jakub Dawidek if (disk == NULL) { 467fa4a1febSPawel Jakub Dawidek error = ENOMEM; 468fa4a1febSPawel Jakub Dawidek goto fail; 469fa4a1febSPawel Jakub Dawidek } 470fa4a1febSPawel Jakub Dawidek disk->d_softc = sc; 471fa4a1febSPawel Jakub Dawidek error = g_mirror_connect_disk(disk, pp); 472fa4a1febSPawel Jakub Dawidek if (error != 0) 473fa4a1febSPawel Jakub Dawidek goto fail; 474fa4a1febSPawel Jakub Dawidek disk->d_id = md->md_did; 475fa4a1febSPawel Jakub Dawidek disk->d_state = G_MIRROR_DISK_STATE_NONE; 476fa4a1febSPawel Jakub Dawidek disk->d_priority = md->md_priority; 477fa4a1febSPawel Jakub Dawidek disk->d_flags = md->md_dflags; 478d89862acSGleb Smirnoff error = g_getattr("GEOM::candelete", disk->d_consumer, &i); 4791f1088b8SAndriy Gapon if (error == 0 && i != 0) 480d89862acSGleb Smirnoff disk->d_flags |= G_MIRROR_DISK_FLAG_CANDELETE; 4819309a460SAlan Somers error = g_getattr("GEOM::rotation_rate", disk->d_consumer, 4829309a460SAlan Somers &disk->d_rotation_rate); 4839309a460SAlan Somers if (error) 4849309a460SAlan Somers disk->d_rotation_rate = DISK_RR_UNKNOWN; 4856c74f517SPawel Jakub Dawidek if (md->md_provider[0] != '\0') 4866c74f517SPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED; 487fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_consumer = NULL; 488fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset = md->md_sync_offset; 489fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset_done = md->md_sync_offset; 4902ceafb77SMark Johnston disk->d_sync.ds_update_ts = time_uptime; 4919a9f5041SPawel Jakub Dawidek disk->d_genid = md->md_genid; 492fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid = md->md_syncid; 493af7dcae0SConrad Meyer disk->d_init_ndisks = md->md_all; 494af7dcae0SConrad Meyer disk->d_init_slice = md->md_slice; 495af7dcae0SConrad Meyer disk->d_init_balance = md->md_balance; 496af7dcae0SConrad Meyer disk->d_init_mediasize = md->md_mediasize; 497fa4a1febSPawel Jakub Dawidek if (errorp != NULL) 498fa4a1febSPawel Jakub Dawidek *errorp = 0; 499fa4a1febSPawel Jakub Dawidek return (disk); 500fa4a1febSPawel Jakub Dawidek fail: 501fa4a1febSPawel Jakub Dawidek if (errorp != NULL) 502fa4a1febSPawel Jakub Dawidek *errorp = error; 503c2ca1093SPawel Jakub Dawidek if (disk != NULL) 504fa4a1febSPawel Jakub Dawidek free(disk, M_MIRROR); 505fa4a1febSPawel Jakub Dawidek return (NULL); 506fa4a1febSPawel Jakub Dawidek } 507fa4a1febSPawel Jakub Dawidek 508fa4a1febSPawel Jakub Dawidek static void 509fa4a1febSPawel Jakub Dawidek g_mirror_destroy_disk(struct g_mirror_disk *disk) 510fa4a1febSPawel Jakub Dawidek { 511fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 512fa4a1febSPawel Jakub Dawidek 513855761d5SPawel Jakub Dawidek g_topology_assert_not(); 514855761d5SPawel Jakub Dawidek sc = disk->d_softc; 515855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 516fa4a1febSPawel Jakub Dawidek 51740e80522SMark Johnston g_topology_lock(); 518fa4a1febSPawel Jakub Dawidek LIST_REMOVE(disk, d_next); 51940e80522SMark Johnston g_topology_unlock(); 520fa4a1febSPawel Jakub Dawidek g_mirror_event_cancel(disk); 521fa4a1febSPawel Jakub Dawidek if (sc->sc_hint == disk) 522fa4a1febSPawel Jakub Dawidek sc->sc_hint = NULL; 523fa4a1febSPawel Jakub Dawidek switch (disk->d_state) { 524fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_SYNCHRONIZING: 525fa4a1febSPawel Jakub Dawidek g_mirror_sync_stop(disk, 1); 526fa4a1febSPawel Jakub Dawidek /* FALLTHROUGH */ 527fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_NEW: 528fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_STALE: 529fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_ACTIVE: 530855761d5SPawel Jakub Dawidek g_topology_lock(); 5316d8fb92dSPawel Jakub Dawidek g_mirror_disconnect_consumer(sc, disk->d_consumer); 532855761d5SPawel Jakub Dawidek g_topology_unlock(); 533e1efe7edSPawel Jakub Dawidek free(disk, M_MIRROR); 534fa4a1febSPawel Jakub Dawidek break; 535fa4a1febSPawel Jakub Dawidek default: 536fa4a1febSPawel Jakub Dawidek KASSERT(0 == 1, ("Wrong disk state (%s, %s).", 537fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 538fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 539fa4a1febSPawel Jakub Dawidek } 540fa4a1febSPawel Jakub Dawidek } 541fa4a1febSPawel Jakub Dawidek 542fa4a1febSPawel Jakub Dawidek static void 543dc399583SAlexander Motin g_mirror_free_device(struct g_mirror_softc *sc) 544dc399583SAlexander Motin { 545dc399583SAlexander Motin 54640e80522SMark Johnston g_topology_assert(); 54740e80522SMark Johnston 548dc399583SAlexander Motin mtx_destroy(&sc->sc_queue_mtx); 549dc399583SAlexander Motin mtx_destroy(&sc->sc_events_mtx); 550dc399583SAlexander Motin mtx_destroy(&sc->sc_done_mtx); 551dc399583SAlexander Motin sx_destroy(&sc->sc_lock); 552dc399583SAlexander Motin free(sc, M_MIRROR); 553dc399583SAlexander Motin } 554dc399583SAlexander Motin 555dc399583SAlexander Motin static void 556dc399583SAlexander Motin g_mirror_providergone(struct g_provider *pp) 557dc399583SAlexander Motin { 558dc399583SAlexander Motin struct g_mirror_softc *sc = pp->private; 559dc399583SAlexander Motin 560dc399583SAlexander Motin if ((--sc->sc_refcnt) == 0) 561dc399583SAlexander Motin g_mirror_free_device(sc); 562dc399583SAlexander Motin } 563dc399583SAlexander Motin 564dc399583SAlexander Motin static void 565fa4a1febSPawel Jakub Dawidek g_mirror_destroy_device(struct g_mirror_softc *sc) 566fa4a1febSPawel Jakub Dawidek { 567fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 568fa4a1febSPawel Jakub Dawidek struct g_mirror_event *ep; 569fa4a1febSPawel Jakub Dawidek struct g_geom *gp; 5706d8fb92dSPawel Jakub Dawidek struct g_consumer *cp, *tmpcp; 571fa4a1febSPawel Jakub Dawidek 572855761d5SPawel Jakub Dawidek g_topology_assert_not(); 573855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 574fa4a1febSPawel Jakub Dawidek 575fa4a1febSPawel Jakub Dawidek gp = sc->sc_geom; 576fa4a1febSPawel Jakub Dawidek if (sc->sc_provider != NULL) 577fa4a1febSPawel Jakub Dawidek g_mirror_destroy_provider(sc); 578fa4a1febSPawel Jakub Dawidek for (disk = LIST_FIRST(&sc->sc_disks); disk != NULL; 579fa4a1febSPawel Jakub Dawidek disk = LIST_FIRST(&sc->sc_disks)) { 5806349471bSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 5816349471bSPawel Jakub Dawidek g_mirror_update_metadata(disk); 582fa4a1febSPawel Jakub Dawidek g_mirror_destroy_disk(disk); 583fa4a1febSPawel Jakub Dawidek } 584b634781eSMark Johnston while ((ep = g_mirror_event_first(sc)) != NULL) { 585f663832bSPawel Jakub Dawidek g_mirror_event_remove(sc, ep); 586fa4a1febSPawel Jakub Dawidek if ((ep->e_flags & G_MIRROR_EVENT_DONTWAIT) != 0) 587fa4a1febSPawel Jakub Dawidek g_mirror_event_free(ep); 588fa4a1febSPawel Jakub Dawidek else { 589fa4a1febSPawel Jakub Dawidek ep->e_error = ECANCELED; 590fa4a1febSPawel Jakub Dawidek ep->e_flags |= G_MIRROR_EVENT_DONE; 591fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, ep); 592fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 593fa4a1febSPawel Jakub Dawidek wakeup(ep); 594fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 595fa4a1febSPawel Jakub Dawidek } 596fa4a1febSPawel Jakub Dawidek } 5972f1cfb7fSMark Johnston g_mirror_timeout_drain(sc); 5986d8fb92dSPawel Jakub Dawidek 599855761d5SPawel Jakub Dawidek g_topology_lock(); 6006d8fb92dSPawel Jakub Dawidek LIST_FOREACH_SAFE(cp, &sc->sc_sync.ds_geom->consumer, consumer, tmpcp) { 6016d8fb92dSPawel Jakub Dawidek g_mirror_disconnect_consumer(sc, cp); 6022d53f423SPawel Jakub Dawidek } 6036d8fb92dSPawel Jakub Dawidek g_wither_geom(sc->sc_sync.ds_geom, ENXIO); 604fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s destroyed.", gp->name); 605fa4a1febSPawel Jakub Dawidek g_wither_geom(gp, ENXIO); 606855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 607dc399583SAlexander Motin if ((--sc->sc_refcnt) == 0) 608dc399583SAlexander Motin g_mirror_free_device(sc); 609dc399583SAlexander Motin g_topology_unlock(); 610fa4a1febSPawel Jakub Dawidek } 611fa4a1febSPawel Jakub Dawidek 612fa4a1febSPawel Jakub Dawidek static void 613fa4a1febSPawel Jakub Dawidek g_mirror_orphan(struct g_consumer *cp) 614fa4a1febSPawel Jakub Dawidek { 615fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 616fa4a1febSPawel Jakub Dawidek 617fa4a1febSPawel Jakub Dawidek g_topology_assert(); 618fa4a1febSPawel Jakub Dawidek 619fa4a1febSPawel Jakub Dawidek disk = cp->private; 620fa4a1febSPawel Jakub Dawidek if (disk == NULL) 621fa4a1febSPawel Jakub Dawidek return; 622da844167SPawel Jakub Dawidek disk->d_softc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 6234b2e596eSPawel Jakub Dawidek g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 6244b2e596eSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT); 6254b2e596eSPawel Jakub Dawidek } 6264b2e596eSPawel Jakub Dawidek 627fa4a1febSPawel Jakub Dawidek /* 628fa4a1febSPawel Jakub Dawidek * Function should return the next active disk on the list. 629fa4a1febSPawel Jakub Dawidek * It is possible that it will be the same disk as given. 630fa4a1febSPawel Jakub Dawidek * If there are no active disks on list, NULL is returned. 631fa4a1febSPawel Jakub Dawidek */ 632fa4a1febSPawel Jakub Dawidek static __inline struct g_mirror_disk * 633fa4a1febSPawel Jakub Dawidek g_mirror_find_next(struct g_mirror_softc *sc, struct g_mirror_disk *disk) 634fa4a1febSPawel Jakub Dawidek { 635fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *dp; 636fa4a1febSPawel Jakub Dawidek 637fa4a1febSPawel Jakub Dawidek for (dp = LIST_NEXT(disk, d_next); dp != disk; 638fa4a1febSPawel Jakub Dawidek dp = LIST_NEXT(dp, d_next)) { 639fa4a1febSPawel Jakub Dawidek if (dp == NULL) 640fa4a1febSPawel Jakub Dawidek dp = LIST_FIRST(&sc->sc_disks); 641fa4a1febSPawel Jakub Dawidek if (dp->d_state == G_MIRROR_DISK_STATE_ACTIVE) 642fa4a1febSPawel Jakub Dawidek break; 643fa4a1febSPawel Jakub Dawidek } 644fa4a1febSPawel Jakub Dawidek if (dp->d_state != G_MIRROR_DISK_STATE_ACTIVE) 645fa4a1febSPawel Jakub Dawidek return (NULL); 646fa4a1febSPawel Jakub Dawidek return (dp); 647fa4a1febSPawel Jakub Dawidek } 648fa4a1febSPawel Jakub Dawidek 649fa4a1febSPawel Jakub Dawidek static struct g_mirror_disk * 650fa4a1febSPawel Jakub Dawidek g_mirror_get_disk(struct g_mirror_softc *sc) 651fa4a1febSPawel Jakub Dawidek { 652fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 653fa4a1febSPawel Jakub Dawidek 654fa4a1febSPawel Jakub Dawidek if (sc->sc_hint == NULL) { 655fa4a1febSPawel Jakub Dawidek sc->sc_hint = LIST_FIRST(&sc->sc_disks); 656fa4a1febSPawel Jakub Dawidek if (sc->sc_hint == NULL) 657fa4a1febSPawel Jakub Dawidek return (NULL); 658fa4a1febSPawel Jakub Dawidek } 659fa4a1febSPawel Jakub Dawidek disk = sc->sc_hint; 660fa4a1febSPawel Jakub Dawidek if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) { 661fa4a1febSPawel Jakub Dawidek disk = g_mirror_find_next(sc, disk); 662fa4a1febSPawel Jakub Dawidek if (disk == NULL) 663fa4a1febSPawel Jakub Dawidek return (NULL); 664fa4a1febSPawel Jakub Dawidek } 665fa4a1febSPawel Jakub Dawidek sc->sc_hint = g_mirror_find_next(sc, disk); 666fa4a1febSPawel Jakub Dawidek return (disk); 667fa4a1febSPawel Jakub Dawidek } 668fa4a1febSPawel Jakub Dawidek 669fa4a1febSPawel Jakub Dawidek static int 670f62d59dfSPawel Jakub Dawidek g_mirror_write_metadata(struct g_mirror_disk *disk, 671f62d59dfSPawel Jakub Dawidek struct g_mirror_metadata *md) 672fa4a1febSPawel Jakub Dawidek { 673fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 674fa4a1febSPawel Jakub Dawidek struct g_consumer *cp; 675fa4a1febSPawel Jakub Dawidek off_t offset, length; 676fa4a1febSPawel Jakub Dawidek u_char *sector; 6779eec299fSPawel Jakub Dawidek int error = 0; 678fa4a1febSPawel Jakub Dawidek 679855761d5SPawel Jakub Dawidek g_topology_assert_not(); 680fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 681855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 682855761d5SPawel Jakub Dawidek 683fa4a1febSPawel Jakub Dawidek cp = disk->d_consumer; 684fa4a1febSPawel Jakub Dawidek KASSERT(cp != NULL, ("NULL consumer (%s).", sc->sc_name)); 685fa4a1febSPawel Jakub Dawidek KASSERT(cp->provider != NULL, ("NULL provider (%s).", sc->sc_name)); 686855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 6879eec299fSPawel Jakub Dawidek ("Consumer %s closed? (r%dw%de%d).", cp->provider->name, cp->acr, 6889eec299fSPawel Jakub Dawidek cp->acw, cp->ace)); 689fa4a1febSPawel Jakub Dawidek length = cp->provider->sectorsize; 690fa4a1febSPawel Jakub Dawidek offset = cp->provider->mediasize - length; 691fa4a1febSPawel Jakub Dawidek sector = malloc((size_t)length, M_MIRROR, M_WAITOK | M_ZERO); 692ae3bc0acSAndrey V. Elsukov if (md != NULL && 693ae3bc0acSAndrey V. Elsukov (sc->sc_flags & G_MIRROR_DEVICE_FLAG_WIPE) == 0) { 69432cea4caSAndrey V. Elsukov /* 69532cea4caSAndrey V. Elsukov * Handle the case, when the size of parent provider reduced. 69632cea4caSAndrey V. Elsukov */ 69732cea4caSAndrey V. Elsukov if (offset < md->md_mediasize) 69832cea4caSAndrey V. Elsukov error = ENOSPC; 69932cea4caSAndrey V. Elsukov else 700f62d59dfSPawel Jakub Dawidek mirror_metadata_encode(md, sector); 70132cea4caSAndrey V. Elsukov } 70240c5032dSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_metadata_write, error); 70332cea4caSAndrey V. Elsukov if (error == 0) 704fa4a1febSPawel Jakub Dawidek error = g_write_data(cp, offset, sector, length); 705fa4a1febSPawel Jakub Dawidek free(sector, M_MIRROR); 706fa4a1febSPawel Jakub Dawidek if (error != 0) { 707d4b0268aSPawel Jakub Dawidek if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 0) { 708d4b0268aSPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_BROKEN; 709d4b0268aSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Cannot write metadata on %s " 710d4b0268aSPawel Jakub Dawidek "(device=%s, error=%d).", 711d4b0268aSPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name, error); 712d4b0268aSPawel Jakub Dawidek } else { 713d4b0268aSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Cannot write metadata on %s " 714d4b0268aSPawel Jakub Dawidek "(device=%s, error=%d).", 715d4b0268aSPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name, error); 716d4b0268aSPawel Jakub Dawidek } 717d4b0268aSPawel Jakub Dawidek if (g_mirror_disconnect_on_failure && 718d4b0268aSPawel Jakub Dawidek g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 1) { 719d4b0268aSPawel Jakub Dawidek sc->sc_bump_id |= G_MIRROR_BUMP_GENID; 720d4b0268aSPawel Jakub Dawidek g_mirror_event_send(disk, 721d4b0268aSPawel Jakub Dawidek G_MIRROR_DISK_STATE_DISCONNECTED, 722fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT); 723f62d59dfSPawel Jakub Dawidek } 724d4b0268aSPawel Jakub Dawidek } 725fa4a1febSPawel Jakub Dawidek return (error); 726fa4a1febSPawel Jakub Dawidek } 727f62d59dfSPawel Jakub Dawidek 728f62d59dfSPawel Jakub Dawidek static int 729f62d59dfSPawel Jakub Dawidek g_mirror_clear_metadata(struct g_mirror_disk *disk) 730f62d59dfSPawel Jakub Dawidek { 731f62d59dfSPawel Jakub Dawidek int error; 732f62d59dfSPawel Jakub Dawidek 733855761d5SPawel Jakub Dawidek g_topology_assert_not(); 734855761d5SPawel Jakub Dawidek sx_assert(&disk->d_softc->sc_lock, SX_LOCKED); 735855761d5SPawel Jakub Dawidek 736b6fe583cSAlexander Motin if (disk->d_softc->sc_type != G_MIRROR_TYPE_AUTOMATIC) 737b6fe583cSAlexander Motin return (0); 738f62d59dfSPawel Jakub Dawidek error = g_mirror_write_metadata(disk, NULL); 739f62d59dfSPawel Jakub Dawidek if (error == 0) { 740fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Metadata on %s cleared.", 741fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk)); 742f62d59dfSPawel Jakub Dawidek } else { 743f62d59dfSPawel Jakub Dawidek G_MIRROR_DEBUG(0, 744f62d59dfSPawel Jakub Dawidek "Cannot clear metadata on disk %s (error=%d).", 745f62d59dfSPawel Jakub Dawidek g_mirror_get_diskname(disk), error); 746f62d59dfSPawel Jakub Dawidek } 747f62d59dfSPawel Jakub Dawidek return (error); 748fa4a1febSPawel Jakub Dawidek } 749fa4a1febSPawel Jakub Dawidek 750fa4a1febSPawel Jakub Dawidek void 751fa4a1febSPawel Jakub Dawidek g_mirror_fill_metadata(struct g_mirror_softc *sc, struct g_mirror_disk *disk, 752fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata *md) 753fa4a1febSPawel Jakub Dawidek { 754fa4a1febSPawel Jakub Dawidek 7557f053a44SMark Johnston bzero(md, sizeof(*md)); 756fa4a1febSPawel Jakub Dawidek strlcpy(md->md_magic, G_MIRROR_MAGIC, sizeof(md->md_magic)); 757fa4a1febSPawel Jakub Dawidek md->md_version = G_MIRROR_VERSION; 758fa4a1febSPawel Jakub Dawidek strlcpy(md->md_name, sc->sc_name, sizeof(md->md_name)); 759fa4a1febSPawel Jakub Dawidek md->md_mid = sc->sc_id; 760fa4a1febSPawel Jakub Dawidek md->md_all = sc->sc_ndisks; 761fa4a1febSPawel Jakub Dawidek md->md_slice = sc->sc_slice; 762fa4a1febSPawel Jakub Dawidek md->md_balance = sc->sc_balance; 7639a9f5041SPawel Jakub Dawidek md->md_genid = sc->sc_genid; 764fa4a1febSPawel Jakub Dawidek md->md_mediasize = sc->sc_mediasize; 765fa4a1febSPawel Jakub Dawidek md->md_sectorsize = sc->sc_sectorsize; 766fa4a1febSPawel Jakub Dawidek md->md_mflags = (sc->sc_flags & G_MIRROR_DEVICE_FLAG_MASK); 767fa4a1febSPawel Jakub Dawidek if (disk == NULL) { 768fa4a1febSPawel Jakub Dawidek md->md_did = arc4random(); 769fa4a1febSPawel Jakub Dawidek } else { 770fa4a1febSPawel Jakub Dawidek md->md_did = disk->d_id; 771fa4a1febSPawel Jakub Dawidek md->md_priority = disk->d_priority; 772fa4a1febSPawel Jakub Dawidek md->md_syncid = disk->d_sync.ds_syncid; 773fa4a1febSPawel Jakub Dawidek md->md_dflags = (disk->d_flags & G_MIRROR_DISK_FLAG_MASK); 774fa4a1febSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) 775fa4a1febSPawel Jakub Dawidek md->md_sync_offset = disk->d_sync.ds_offset_done; 7766c74f517SPawel Jakub Dawidek if ((disk->d_flags & G_MIRROR_DISK_FLAG_HARDCODED) != 0) { 7776c74f517SPawel Jakub Dawidek strlcpy(md->md_provider, 7786c74f517SPawel Jakub Dawidek disk->d_consumer->provider->name, 7796c74f517SPawel Jakub Dawidek sizeof(md->md_provider)); 7806c74f517SPawel Jakub Dawidek } 781e6890985SPawel Jakub Dawidek md->md_provsize = disk->d_consumer->provider->mediasize; 782fa4a1febSPawel Jakub Dawidek } 783fa4a1febSPawel Jakub Dawidek } 784fa4a1febSPawel Jakub Dawidek 785fa4a1febSPawel Jakub Dawidek void 786fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(struct g_mirror_disk *disk) 787fa4a1febSPawel Jakub Dawidek { 788855761d5SPawel Jakub Dawidek struct g_mirror_softc *sc; 789fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata md; 790f62d59dfSPawel Jakub Dawidek int error; 791fa4a1febSPawel Jakub Dawidek 792855761d5SPawel Jakub Dawidek g_topology_assert_not(); 793855761d5SPawel Jakub Dawidek sc = disk->d_softc; 794855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 795855761d5SPawel Jakub Dawidek 796b6fe583cSAlexander Motin if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) 797b6fe583cSAlexander Motin return; 798ae3bc0acSAndrey V. Elsukov if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_WIPE) == 0) 799855761d5SPawel Jakub Dawidek g_mirror_fill_metadata(sc, disk, &md); 800f62d59dfSPawel Jakub Dawidek error = g_mirror_write_metadata(disk, &md); 801fa4a1febSPawel Jakub Dawidek if (error == 0) { 802f62d59dfSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Metadata on %s updated.", 803f62d59dfSPawel Jakub Dawidek g_mirror_get_diskname(disk)); 804f62d59dfSPawel Jakub Dawidek } else { 805fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, 806fa4a1febSPawel Jakub Dawidek "Cannot update metadata on disk %s (error=%d).", 807fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), error); 808fa4a1febSPawel Jakub Dawidek } 809fa4a1febSPawel Jakub Dawidek } 810fa4a1febSPawel Jakub Dawidek 811fa4a1febSPawel Jakub Dawidek static void 8129eec299fSPawel Jakub Dawidek g_mirror_bump_syncid(struct g_mirror_softc *sc) 813fa4a1febSPawel Jakub Dawidek { 814fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 815fa4a1febSPawel Jakub Dawidek 816855761d5SPawel Jakub Dawidek g_topology_assert_not(); 817855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 818fa4a1febSPawel Jakub Dawidek KASSERT(g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 0, 819fa4a1febSPawel Jakub Dawidek ("%s called with no active disks (device=%s).", __func__, 820fa4a1febSPawel Jakub Dawidek sc->sc_name)); 821fa4a1febSPawel Jakub Dawidek 822fa4a1febSPawel Jakub Dawidek sc->sc_syncid++; 8234084e6aaSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Device %s: syncid bumped to %u.", sc->sc_name, 8244084e6aaSPawel Jakub Dawidek sc->sc_syncid); 825fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 826fa4a1febSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE || 827fa4a1febSPawel Jakub Dawidek disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) { 828fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid = sc->sc_syncid; 829fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(disk); 830fa4a1febSPawel Jakub Dawidek } 831fa4a1febSPawel Jakub Dawidek } 832fa4a1febSPawel Jakub Dawidek } 833fa4a1febSPawel Jakub Dawidek 8342fdf5be1SPawel Jakub Dawidek static void 8359a9f5041SPawel Jakub Dawidek g_mirror_bump_genid(struct g_mirror_softc *sc) 8369a9f5041SPawel Jakub Dawidek { 8379a9f5041SPawel Jakub Dawidek struct g_mirror_disk *disk; 8389a9f5041SPawel Jakub Dawidek 839855761d5SPawel Jakub Dawidek g_topology_assert_not(); 840855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 8419a9f5041SPawel Jakub Dawidek KASSERT(g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 0, 8429a9f5041SPawel Jakub Dawidek ("%s called with no active disks (device=%s).", __func__, 8439a9f5041SPawel Jakub Dawidek sc->sc_name)); 8449a9f5041SPawel Jakub Dawidek 8459a9f5041SPawel Jakub Dawidek sc->sc_genid++; 8469a9f5041SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Device %s: genid bumped to %u.", sc->sc_name, 8479a9f5041SPawel Jakub Dawidek sc->sc_genid); 8489a9f5041SPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 8499a9f5041SPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE || 8509a9f5041SPawel Jakub Dawidek disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) { 851538ff5eeSPawel Jakub Dawidek disk->d_genid = sc->sc_genid; 8529a9f5041SPawel Jakub Dawidek g_mirror_update_metadata(disk); 8539a9f5041SPawel Jakub Dawidek } 8549a9f5041SPawel Jakub Dawidek } 8559a9f5041SPawel Jakub Dawidek } 8569a9f5041SPawel Jakub Dawidek 857fe6f94eaSPawel Jakub Dawidek static int 858855761d5SPawel Jakub Dawidek g_mirror_idle(struct g_mirror_softc *sc, int acw) 8592fdf5be1SPawel Jakub Dawidek { 8602fdf5be1SPawel Jakub Dawidek struct g_mirror_disk *disk; 861fe6f94eaSPawel Jakub Dawidek int timeout; 8622fdf5be1SPawel Jakub Dawidek 863855761d5SPawel Jakub Dawidek g_topology_assert_not(); 864855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 865855761d5SPawel Jakub Dawidek 866fe6f94eaSPawel Jakub Dawidek if (sc->sc_provider == NULL) 867fe6f94eaSPawel Jakub Dawidek return (0); 868501250baSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) 869501250baSPawel Jakub Dawidek return (0); 870fe6f94eaSPawel Jakub Dawidek if (sc->sc_idle) 871fe6f94eaSPawel Jakub Dawidek return (0); 872fe6f94eaSPawel Jakub Dawidek if (sc->sc_writes > 0) 873fe6f94eaSPawel Jakub Dawidek return (0); 874855761d5SPawel Jakub Dawidek if (acw > 0 || (acw == -1 && sc->sc_provider->acw > 0)) { 87501f1f41cSPawel Jakub Dawidek timeout = g_mirror_idletime - (time_uptime - sc->sc_last_write); 876cbab6161SAlexander Motin if (!g_mirror_shutdown && timeout > 0) 877fe6f94eaSPawel Jakub Dawidek return (timeout); 878fe6f94eaSPawel Jakub Dawidek } 8792fdf5be1SPawel Jakub Dawidek sc->sc_idle = 1; 8802fdf5be1SPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 8812fdf5be1SPawel Jakub Dawidek if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) 8822fdf5be1SPawel Jakub Dawidek continue; 8837d31c393SMark Johnston G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as clean.", 8842fdf5be1SPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name); 8852fdf5be1SPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 8862fdf5be1SPawel Jakub Dawidek g_mirror_update_metadata(disk); 8872fdf5be1SPawel Jakub Dawidek } 888fe6f94eaSPawel Jakub Dawidek return (0); 8892fdf5be1SPawel Jakub Dawidek } 8902fdf5be1SPawel Jakub Dawidek 8912fdf5be1SPawel Jakub Dawidek static void 8922fdf5be1SPawel Jakub Dawidek g_mirror_unidle(struct g_mirror_softc *sc) 8932fdf5be1SPawel Jakub Dawidek { 8942fdf5be1SPawel Jakub Dawidek struct g_mirror_disk *disk; 8952fdf5be1SPawel Jakub Dawidek 896855761d5SPawel Jakub Dawidek g_topology_assert_not(); 897855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 898855761d5SPawel Jakub Dawidek 899501250baSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) 900501250baSPawel Jakub Dawidek return; 9012fdf5be1SPawel Jakub Dawidek sc->sc_idle = 0; 90201f1f41cSPawel Jakub Dawidek sc->sc_last_write = time_uptime; 9032fdf5be1SPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 9042fdf5be1SPawel Jakub Dawidek if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) 9052fdf5be1SPawel Jakub Dawidek continue; 9067d31c393SMark Johnston G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as dirty.", 9072fdf5be1SPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name); 9082fdf5be1SPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY; 9092fdf5be1SPawel Jakub Dawidek g_mirror_update_metadata(disk); 9102fdf5be1SPawel Jakub Dawidek } 9112fdf5be1SPawel Jakub Dawidek } 9122fdf5be1SPawel Jakub Dawidek 913fa4a1febSPawel Jakub Dawidek static void 914fa4a1febSPawel Jakub Dawidek g_mirror_done(struct bio *bp) 915fa4a1febSPawel Jakub Dawidek { 916fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 917fa4a1febSPawel Jakub Dawidek 918fa4a1febSPawel Jakub Dawidek sc = bp->bio_from->geom->softc; 9198e007c52SPawel Jakub Dawidek bp->bio_cflags = G_MIRROR_BIO_FLAG_REGULAR; 920fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 9219abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue); 922fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 92386de0ca5SAlexander Motin wakeup(sc); 924fa4a1febSPawel Jakub Dawidek } 925fa4a1febSPawel Jakub Dawidek 926fa4a1febSPawel Jakub Dawidek static void 927aed882a9SMark Johnston g_mirror_regular_request_error(struct g_mirror_softc *sc, 928aed882a9SMark Johnston struct g_mirror_disk *disk, struct bio *bp) 929fa4a1febSPawel Jakub Dawidek { 9301787c3feSMark Johnston 9318b522bdaSWarner Losh if ((bp->bio_cmd == BIO_FLUSH || bp->bio_cmd == BIO_SPEEDUP) && 9328b522bdaSWarner Losh bp->bio_error == EOPNOTSUPP) 9331787c3feSMark Johnston return; 9341787c3feSMark Johnston 9351787c3feSMark Johnston if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 0) { 9361787c3feSMark Johnston disk->d_flags |= G_MIRROR_DISK_FLAG_BROKEN; 9371787c3feSMark Johnston G_MIRROR_LOGREQ(0, bp, "Request failed (error=%d).", 9381787c3feSMark Johnston bp->bio_error); 9391787c3feSMark Johnston } else { 9401787c3feSMark Johnston G_MIRROR_LOGREQ(1, bp, "Request failed (error=%d).", 9411787c3feSMark Johnston bp->bio_error); 9421787c3feSMark Johnston } 9431787c3feSMark Johnston if (g_mirror_disconnect_on_failure && 9441787c3feSMark Johnston g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 1) { 9451787c3feSMark Johnston if (bp->bio_error == ENXIO && 9461787c3feSMark Johnston bp->bio_cmd == BIO_READ) 9471787c3feSMark Johnston sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 9481787c3feSMark Johnston else if (bp->bio_error == ENXIO) 9491787c3feSMark Johnston sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_NOW; 9501787c3feSMark Johnston else 9511787c3feSMark Johnston sc->sc_bump_id |= G_MIRROR_BUMP_GENID; 9521787c3feSMark Johnston g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 9531787c3feSMark Johnston G_MIRROR_EVENT_DONTWAIT); 9541787c3feSMark Johnston } 9551787c3feSMark Johnston } 9561787c3feSMark Johnston 9571787c3feSMark Johnston static void 9581787c3feSMark Johnston g_mirror_regular_request(struct g_mirror_softc *sc, struct bio *bp) 9591787c3feSMark Johnston { 960aed882a9SMark Johnston struct g_mirror_disk *disk; 961fa4a1febSPawel Jakub Dawidek struct bio *pbp; 962fa4a1febSPawel Jakub Dawidek 963fa4a1febSPawel Jakub Dawidek g_topology_assert_not(); 9641787c3feSMark Johnston KASSERT(sc->sc_provider == bp->bio_parent->bio_to, 9651787c3feSMark Johnston ("regular request %p with unexpected origin", bp)); 966fa4a1febSPawel Jakub Dawidek 967fa4a1febSPawel Jakub Dawidek pbp = bp->bio_parent; 968fe6f94eaSPawel Jakub Dawidek bp->bio_from->index--; 969a3584ee3SMark Johnston if (bp->bio_cmd == BIO_WRITE || bp->bio_cmd == BIO_DELETE) 970fe6f94eaSPawel Jakub Dawidek sc->sc_writes--; 971aed882a9SMark Johnston disk = bp->bio_from->private; 972aed882a9SMark Johnston if (disk == NULL) { 973fa4a1febSPawel Jakub Dawidek g_topology_lock(); 974fa4a1febSPawel Jakub Dawidek g_mirror_kill_consumer(sc, bp->bio_from); 975fa4a1febSPawel Jakub Dawidek g_topology_unlock(); 976fa4a1febSPawel Jakub Dawidek } 977fa4a1febSPawel Jakub Dawidek 9781787c3feSMark Johnston switch (bp->bio_cmd) { 9791787c3feSMark Johnston case BIO_READ: 98040c5032dSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_read, 98140c5032dSMark Johnston bp->bio_error); 9821787c3feSMark Johnston break; 9831787c3feSMark Johnston case BIO_WRITE: 98440c5032dSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_write, 98540c5032dSMark Johnston bp->bio_error); 9861787c3feSMark Johnston break; 9871787c3feSMark Johnston case BIO_DELETE: 9881787c3feSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_delete, 9891787c3feSMark Johnston bp->bio_error); 9901787c3feSMark Johnston break; 9911787c3feSMark Johnston case BIO_FLUSH: 9921787c3feSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_flush, 9931787c3feSMark Johnston bp->bio_error); 9941787c3feSMark Johnston break; 9958b522bdaSWarner Losh case BIO_SPEEDUP: 9968b522bdaSWarner Losh KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_speedup, 9978b522bdaSWarner Losh bp->bio_error); 9988b522bdaSWarner Losh break; 9991787c3feSMark Johnston } 100040c5032dSMark Johnston 1001fa4a1febSPawel Jakub Dawidek pbp->bio_inbed++; 1002fa4a1febSPawel Jakub Dawidek KASSERT(pbp->bio_inbed <= pbp->bio_children, 1003fa4a1febSPawel Jakub Dawidek ("bio_inbed (%u) is bigger than bio_children (%u).", pbp->bio_inbed, 1004fa4a1febSPawel Jakub Dawidek pbp->bio_children)); 1005fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0 && pbp->bio_error == 0) { 1006fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Request delivered."); 1007fa4a1febSPawel Jakub Dawidek g_destroy_bio(bp); 1008fa4a1febSPawel Jakub Dawidek if (pbp->bio_children == pbp->bio_inbed) { 1009fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, pbp, "Request delivered."); 1010fa4a1febSPawel Jakub Dawidek pbp->bio_completed = pbp->bio_length; 1011b0ae63caSGleb Smirnoff if (pbp->bio_cmd == BIO_WRITE || 1012b0ae63caSGleb Smirnoff pbp->bio_cmd == BIO_DELETE) { 10139abe2e7eSMark Johnston TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); 1014855761d5SPawel Jakub Dawidek /* Release delayed sync requests if possible. */ 1015855761d5SPawel Jakub Dawidek g_mirror_sync_release(sc); 1016855761d5SPawel Jakub Dawidek } 1017fa4a1febSPawel Jakub Dawidek g_io_deliver(pbp, pbp->bio_error); 1018fa4a1febSPawel Jakub Dawidek } 1019fa4a1febSPawel Jakub Dawidek return; 1020fa4a1febSPawel Jakub Dawidek } else if (bp->bio_error != 0) { 1021fa4a1febSPawel Jakub Dawidek if (pbp->bio_error == 0) 1022fa4a1febSPawel Jakub Dawidek pbp->bio_error = bp->bio_error; 1023aed882a9SMark Johnston if (disk != NULL) 1024aed882a9SMark Johnston g_mirror_regular_request_error(sc, disk, bp); 1025fa4a1febSPawel Jakub Dawidek switch (pbp->bio_cmd) { 1026fa4a1febSPawel Jakub Dawidek case BIO_DELETE: 1027fa4a1febSPawel Jakub Dawidek case BIO_WRITE: 10281787c3feSMark Johnston case BIO_FLUSH: 10298b522bdaSWarner Losh case BIO_SPEEDUP: 1030fa4a1febSPawel Jakub Dawidek pbp->bio_inbed--; 1031fa4a1febSPawel Jakub Dawidek pbp->bio_children--; 1032fa4a1febSPawel Jakub Dawidek break; 1033fa4a1febSPawel Jakub Dawidek } 1034fa4a1febSPawel Jakub Dawidek } 1035fa4a1febSPawel Jakub Dawidek g_destroy_bio(bp); 1036fa4a1febSPawel Jakub Dawidek 1037fa4a1febSPawel Jakub Dawidek switch (pbp->bio_cmd) { 1038fa4a1febSPawel Jakub Dawidek case BIO_READ: 1039d4b0268aSPawel Jakub Dawidek if (pbp->bio_inbed < pbp->bio_children) 1040d4b0268aSPawel Jakub Dawidek break; 10415d5f4462SAndriy Gapon 10425d5f4462SAndriy Gapon /* 10435d5f4462SAndriy Gapon * If there is only one active disk we want to double-check that 10445d5f4462SAndriy Gapon * it is, in fact, the disk that we already tried. This is 10455d5f4462SAndriy Gapon * necessary because we might have just lost a race with a 10465d5f4462SAndriy Gapon * removal of the tried disk (likely because of the same error) 10475d5f4462SAndriy Gapon * and the only remaining disk is still viable for a retry. 10485d5f4462SAndriy Gapon */ 10495d5f4462SAndriy Gapon if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 && 10505d5f4462SAndriy Gapon disk != NULL && 10515d5f4462SAndriy Gapon disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 1052d4b0268aSPawel Jakub Dawidek g_io_deliver(pbp, pbp->bio_error); 10535d5f4462SAndriy Gapon } else { 1054fa4a1febSPawel Jakub Dawidek pbp->bio_error = 0; 1055fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 10569abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_queue, pbp, bio_queue); 105786de0ca5SAlexander Motin mtx_unlock(&sc->sc_queue_mtx); 1058fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); 1059fa4a1febSPawel Jakub Dawidek wakeup(sc); 1060fa4a1febSPawel Jakub Dawidek } 1061fa4a1febSPawel Jakub Dawidek break; 1062fa4a1febSPawel Jakub Dawidek case BIO_DELETE: 1063fa4a1febSPawel Jakub Dawidek case BIO_WRITE: 10641787c3feSMark Johnston case BIO_FLUSH: 10658b522bdaSWarner Losh case BIO_SPEEDUP: 1066fa4a1febSPawel Jakub Dawidek if (pbp->bio_children == 0) { 1067fa4a1febSPawel Jakub Dawidek /* 1068fa4a1febSPawel Jakub Dawidek * All requests failed. 1069fa4a1febSPawel Jakub Dawidek */ 1070fa4a1febSPawel Jakub Dawidek } else if (pbp->bio_inbed < pbp->bio_children) { 1071fa4a1febSPawel Jakub Dawidek /* Do nothing. */ 1072fa4a1febSPawel Jakub Dawidek break; 1073fa4a1febSPawel Jakub Dawidek } else if (pbp->bio_children == pbp->bio_inbed) { 1074fa4a1febSPawel Jakub Dawidek /* Some requests succeeded. */ 1075fa4a1febSPawel Jakub Dawidek pbp->bio_error = 0; 1076fa4a1febSPawel Jakub Dawidek pbp->bio_completed = pbp->bio_length; 1077fa4a1febSPawel Jakub Dawidek } 10781787c3feSMark Johnston if (pbp->bio_cmd == BIO_WRITE || pbp->bio_cmd == BIO_DELETE) { 10799abe2e7eSMark Johnston TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); 1080855761d5SPawel Jakub Dawidek /* Release delayed sync requests if possible. */ 1081855761d5SPawel Jakub Dawidek g_mirror_sync_release(sc); 10821787c3feSMark Johnston } 1083fa4a1febSPawel Jakub Dawidek g_io_deliver(pbp, pbp->bio_error); 1084fa4a1febSPawel Jakub Dawidek break; 1085fa4a1febSPawel Jakub Dawidek default: 1086fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("Invalid request: %u.", pbp->bio_cmd)); 1087fa4a1febSPawel Jakub Dawidek break; 1088fa4a1febSPawel Jakub Dawidek } 1089fa4a1febSPawel Jakub Dawidek } 1090fa4a1febSPawel Jakub Dawidek 1091fa4a1febSPawel Jakub Dawidek static void 1092fa4a1febSPawel Jakub Dawidek g_mirror_sync_done(struct bio *bp) 1093fa4a1febSPawel Jakub Dawidek { 1094fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 1095fa4a1febSPawel Jakub Dawidek 1096fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Synchronization request delivered."); 1097fa4a1febSPawel Jakub Dawidek sc = bp->bio_from->geom->softc; 10988e007c52SPawel Jakub Dawidek bp->bio_cflags = G_MIRROR_BIO_FLAG_SYNC; 1099fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 11009abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue); 1101fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 110286de0ca5SAlexander Motin wakeup(sc); 1103fa4a1febSPawel Jakub Dawidek } 1104fa4a1febSPawel Jakub Dawidek 1105fa4a1febSPawel Jakub Dawidek static void 11067715befdSAlexander Motin g_mirror_candelete(struct bio *bp) 11077715befdSAlexander Motin { 11087715befdSAlexander Motin struct g_mirror_softc *sc; 11097715befdSAlexander Motin struct g_mirror_disk *disk; 1110438622afSMark Johnston int val; 11117715befdSAlexander Motin 1112dc399583SAlexander Motin sc = bp->bio_to->private; 11137715befdSAlexander Motin LIST_FOREACH(disk, &sc->sc_disks, d_next) { 11147715befdSAlexander Motin if (disk->d_flags & G_MIRROR_DISK_FLAG_CANDELETE) 11157715befdSAlexander Motin break; 11167715befdSAlexander Motin } 1117438622afSMark Johnston val = disk != NULL; 1118438622afSMark Johnston g_handleattr(bp, "GEOM::candelete", &val, sizeof(val)); 11197715befdSAlexander Motin } 11207715befdSAlexander Motin 11217715befdSAlexander Motin static void 112246861875SPawel Jakub Dawidek g_mirror_kernel_dump(struct bio *bp) 112346861875SPawel Jakub Dawidek { 112446861875SPawel Jakub Dawidek struct g_mirror_softc *sc; 112546861875SPawel Jakub Dawidek struct g_mirror_disk *disk; 112646861875SPawel Jakub Dawidek struct bio *cbp; 112746861875SPawel Jakub Dawidek struct g_kerneldump *gkd; 112846861875SPawel Jakub Dawidek 112946861875SPawel Jakub Dawidek /* 113046861875SPawel Jakub Dawidek * We configure dumping to the first component, because this component 113146861875SPawel Jakub Dawidek * will be used for reading with 'prefer' balance algorithm. 1132e8d57122SPedro F. Giffuni * If the component with the highest priority is currently disconnected 113346861875SPawel Jakub Dawidek * we will not be able to read the dump after the reboot if it will be 113446861875SPawel Jakub Dawidek * connected and synchronized later. Can we do something better? 113546861875SPawel Jakub Dawidek */ 1136dc399583SAlexander Motin sc = bp->bio_to->private; 113746861875SPawel Jakub Dawidek disk = LIST_FIRST(&sc->sc_disks); 113846861875SPawel Jakub Dawidek 113946861875SPawel Jakub Dawidek gkd = (struct g_kerneldump *)bp->bio_data; 114046861875SPawel Jakub Dawidek if (gkd->length > bp->bio_to->mediasize) 114146861875SPawel Jakub Dawidek gkd->length = bp->bio_to->mediasize; 114246861875SPawel Jakub Dawidek cbp = g_clone_bio(bp); 114346861875SPawel Jakub Dawidek if (cbp == NULL) { 114446861875SPawel Jakub Dawidek g_io_deliver(bp, ENOMEM); 114546861875SPawel Jakub Dawidek return; 114646861875SPawel Jakub Dawidek } 114746861875SPawel Jakub Dawidek cbp->bio_done = g_std_done; 114846861875SPawel Jakub Dawidek g_io_request(cbp, disk->d_consumer); 114946861875SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Kernel dump will go to %s.", 115046861875SPawel Jakub Dawidek g_mirror_get_diskname(disk)); 115146861875SPawel Jakub Dawidek } 115246861875SPawel Jakub Dawidek 115346861875SPawel Jakub Dawidek static void 11549309a460SAlan Somers g_mirror_rotation_rate(struct bio *bp) 11559309a460SAlan Somers { 11569309a460SAlan Somers struct g_mirror_softc *sc; 11579309a460SAlan Somers struct g_mirror_disk *disk; 11589309a460SAlan Somers bool first = true; 11599309a460SAlan Somers uint16_t rr = DISK_RR_UNKNOWN; 11609309a460SAlan Somers 11619309a460SAlan Somers sc = bp->bio_to->private; 11629309a460SAlan Somers LIST_FOREACH(disk, &sc->sc_disks, d_next) { 11639309a460SAlan Somers if (first) 11649309a460SAlan Somers rr = disk->d_rotation_rate; 11659309a460SAlan Somers else if (rr != disk->d_rotation_rate) { 11669309a460SAlan Somers rr = DISK_RR_UNKNOWN; 11679309a460SAlan Somers break; 11689309a460SAlan Somers } 11699309a460SAlan Somers first = false; 11709309a460SAlan Somers } 11719309a460SAlan Somers g_handleattr(bp, "GEOM::rotation_rate", &rr, sizeof(rr)); 11729309a460SAlan Somers } 11739309a460SAlan Somers 11749309a460SAlan Somers static void 1175fa4a1febSPawel Jakub Dawidek g_mirror_start(struct bio *bp) 1176fa4a1febSPawel Jakub Dawidek { 1177fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 1178fa4a1febSPawel Jakub Dawidek 1179dc399583SAlexander Motin sc = bp->bio_to->private; 1180fa4a1febSPawel Jakub Dawidek /* 1181fa4a1febSPawel Jakub Dawidek * If sc == NULL or there are no valid disks, provider's error 1182fa4a1febSPawel Jakub Dawidek * should be set and g_mirror_start() should not be called at all. 1183fa4a1febSPawel Jakub Dawidek */ 1184fa4a1febSPawel Jakub Dawidek KASSERT(sc != NULL && sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 1185fa4a1febSPawel Jakub Dawidek ("Provider's error should be set (error=%d)(mirror=%s).", 1186fa4a1febSPawel Jakub Dawidek bp->bio_to->error, bp->bio_to->name)); 1187fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Request received."); 1188fa4a1febSPawel Jakub Dawidek 1189fa4a1febSPawel Jakub Dawidek switch (bp->bio_cmd) { 1190fa4a1febSPawel Jakub Dawidek case BIO_READ: 1191fa4a1febSPawel Jakub Dawidek case BIO_WRITE: 1192fa4a1febSPawel Jakub Dawidek case BIO_DELETE: 11938b522bdaSWarner Losh case BIO_SPEEDUP: 119442461fbaSPawel Jakub Dawidek case BIO_FLUSH: 11951787c3feSMark Johnston break; 1196fa4a1febSPawel Jakub Dawidek case BIO_GETATTR: 11977715befdSAlexander Motin if (!strcmp(bp->bio_attribute, "GEOM::candelete")) { 11987715befdSAlexander Motin g_mirror_candelete(bp); 1199d89862acSGleb Smirnoff return; 12007715befdSAlexander Motin } else if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { 120146861875SPawel Jakub Dawidek g_mirror_kernel_dump(bp); 120246861875SPawel Jakub Dawidek return; 12039309a460SAlan Somers } else if (!strcmp(bp->bio_attribute, "GEOM::rotation_rate")) { 12049309a460SAlan Somers g_mirror_rotation_rate(bp); 12059309a460SAlan Somers return; 120646861875SPawel Jakub Dawidek } 120746861875SPawel Jakub Dawidek /* FALLTHROUGH */ 1208fa4a1febSPawel Jakub Dawidek default: 1209fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, EOPNOTSUPP); 1210fa4a1febSPawel Jakub Dawidek return; 1211fa4a1febSPawel Jakub Dawidek } 1212fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 121377011eacSMark Johnston if (bp->bio_to->error != 0) { 121477011eacSMark Johnston mtx_unlock(&sc->sc_queue_mtx); 121577011eacSMark Johnston g_io_deliver(bp, bp->bio_to->error); 121677011eacSMark Johnston return; 121777011eacSMark Johnston } 12189abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue); 121986de0ca5SAlexander Motin mtx_unlock(&sc->sc_queue_mtx); 1220fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); 1221fa4a1febSPawel Jakub Dawidek wakeup(sc); 1222fa4a1febSPawel Jakub Dawidek } 1223fa4a1febSPawel Jakub Dawidek 1224fa4a1febSPawel Jakub Dawidek /* 1225*01e18673SEd Maste * Return true if the given request is colliding with a in-progress 1226855761d5SPawel Jakub Dawidek * synchronization request. 1227fa4a1febSPawel Jakub Dawidek */ 122868eadcecSMark Johnston static bool 1229855761d5SPawel Jakub Dawidek g_mirror_sync_collision(struct g_mirror_softc *sc, struct bio *bp) 1230fa4a1febSPawel Jakub Dawidek { 1231855761d5SPawel Jakub Dawidek struct g_mirror_disk *disk; 1232855761d5SPawel Jakub Dawidek struct bio *sbp; 1233855761d5SPawel Jakub Dawidek off_t rstart, rend, sstart, send; 1234b99bce73SPedro F. Giffuni u_int i; 1235855761d5SPawel Jakub Dawidek 1236855761d5SPawel Jakub Dawidek if (sc->sc_sync.ds_ndisks == 0) 123768eadcecSMark Johnston return (false); 1238855761d5SPawel Jakub Dawidek rstart = bp->bio_offset; 1239855761d5SPawel Jakub Dawidek rend = bp->bio_offset + bp->bio_length; 1240855761d5SPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 1241855761d5SPawel Jakub Dawidek if (disk->d_state != G_MIRROR_DISK_STATE_SYNCHRONIZING) 1242855761d5SPawel Jakub Dawidek continue; 1243855761d5SPawel Jakub Dawidek for (i = 0; i < g_mirror_syncreqs; i++) { 1244855761d5SPawel Jakub Dawidek sbp = disk->d_sync.ds_bios[i]; 1245855761d5SPawel Jakub Dawidek if (sbp == NULL) 1246855761d5SPawel Jakub Dawidek continue; 1247855761d5SPawel Jakub Dawidek sstart = sbp->bio_offset; 1248855761d5SPawel Jakub Dawidek send = sbp->bio_offset + sbp->bio_length; 1249855761d5SPawel Jakub Dawidek if (rend > sstart && rstart < send) 125068eadcecSMark Johnston return (true); 1251855761d5SPawel Jakub Dawidek } 1252855761d5SPawel Jakub Dawidek } 125368eadcecSMark Johnston return (false); 1254855761d5SPawel Jakub Dawidek } 1255855761d5SPawel Jakub Dawidek 1256855761d5SPawel Jakub Dawidek /* 1257*01e18673SEd Maste * Return true if the given sync request is colliding with a in-progress regular 1258855761d5SPawel Jakub Dawidek * request. 1259855761d5SPawel Jakub Dawidek */ 126068eadcecSMark Johnston static bool 1261855761d5SPawel Jakub Dawidek g_mirror_regular_collision(struct g_mirror_softc *sc, struct bio *sbp) 1262855761d5SPawel Jakub Dawidek { 1263855761d5SPawel Jakub Dawidek off_t rstart, rend, sstart, send; 1264fa4a1febSPawel Jakub Dawidek struct bio *bp; 1265fa4a1febSPawel Jakub Dawidek 1266855761d5SPawel Jakub Dawidek if (sc->sc_sync.ds_ndisks == 0) 126768eadcecSMark Johnston return (false); 1268855761d5SPawel Jakub Dawidek sstart = sbp->bio_offset; 1269855761d5SPawel Jakub Dawidek send = sbp->bio_offset + sbp->bio_length; 12709abe2e7eSMark Johnston TAILQ_FOREACH(bp, &sc->sc_inflight, bio_queue) { 1271855761d5SPawel Jakub Dawidek rstart = bp->bio_offset; 1272855761d5SPawel Jakub Dawidek rend = bp->bio_offset + bp->bio_length; 1273855761d5SPawel Jakub Dawidek if (rend > sstart && rstart < send) 127468eadcecSMark Johnston return (true); 1275fa4a1febSPawel Jakub Dawidek } 127668eadcecSMark Johnston return (false); 1277fa4a1febSPawel Jakub Dawidek } 1278fa4a1febSPawel Jakub Dawidek 1279855761d5SPawel Jakub Dawidek /* 12801787c3feSMark Johnston * Puts regular request onto delayed queue. 1281855761d5SPawel Jakub Dawidek */ 1282855761d5SPawel Jakub Dawidek static void 1283855761d5SPawel Jakub Dawidek g_mirror_regular_delay(struct g_mirror_softc *sc, struct bio *bp) 1284855761d5SPawel Jakub Dawidek { 1285855761d5SPawel Jakub Dawidek 1286855761d5SPawel Jakub Dawidek G_MIRROR_LOGREQ(2, bp, "Delaying request."); 12871787c3feSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_regular_delayed, bp, bio_queue); 1288855761d5SPawel Jakub Dawidek } 1289855761d5SPawel Jakub Dawidek 1290855761d5SPawel Jakub Dawidek /* 1291855761d5SPawel Jakub Dawidek * Puts synchronization request onto delayed queue. 1292855761d5SPawel Jakub Dawidek */ 1293855761d5SPawel Jakub Dawidek static void 1294855761d5SPawel Jakub Dawidek g_mirror_sync_delay(struct g_mirror_softc *sc, struct bio *bp) 1295855761d5SPawel Jakub Dawidek { 1296855761d5SPawel Jakub Dawidek 1297855761d5SPawel Jakub Dawidek G_MIRROR_LOGREQ(2, bp, "Delaying synchronization request."); 12989abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_sync_delayed, bp, bio_queue); 1299855761d5SPawel Jakub Dawidek } 1300855761d5SPawel Jakub Dawidek 1301855761d5SPawel Jakub Dawidek /* 13021787c3feSMark Johnston * Requeue delayed regular requests. 1303855761d5SPawel Jakub Dawidek */ 1304855761d5SPawel Jakub Dawidek static void 1305855761d5SPawel Jakub Dawidek g_mirror_regular_release(struct g_mirror_softc *sc) 1306855761d5SPawel Jakub Dawidek { 13071787c3feSMark Johnston struct bio *bp; 1308855761d5SPawel Jakub Dawidek 13091787c3feSMark Johnston if ((bp = TAILQ_FIRST(&sc->sc_regular_delayed)) == NULL) 13101787c3feSMark Johnston return; 1311855761d5SPawel Jakub Dawidek if (g_mirror_sync_collision(sc, bp)) 13121787c3feSMark Johnston return; 13131787c3feSMark Johnston 13141787c3feSMark Johnston G_MIRROR_DEBUG(2, "Requeuing regular requests after collision."); 1315855761d5SPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 13161787c3feSMark Johnston TAILQ_CONCAT(&sc->sc_regular_delayed, &sc->sc_queue, bio_queue); 13171787c3feSMark Johnston TAILQ_SWAP(&sc->sc_regular_delayed, &sc->sc_queue, bio, bio_queue); 1318855761d5SPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 1319855761d5SPawel Jakub Dawidek } 1320855761d5SPawel Jakub Dawidek 1321855761d5SPawel Jakub Dawidek /* 1322855761d5SPawel Jakub Dawidek * Releases delayed sync requests which don't collide anymore with regular 1323855761d5SPawel Jakub Dawidek * requests. 1324855761d5SPawel Jakub Dawidek */ 1325855761d5SPawel Jakub Dawidek static void 1326855761d5SPawel Jakub Dawidek g_mirror_sync_release(struct g_mirror_softc *sc) 1327855761d5SPawel Jakub Dawidek { 1328855761d5SPawel Jakub Dawidek struct bio *bp, *bp2; 1329855761d5SPawel Jakub Dawidek 13309abe2e7eSMark Johnston TAILQ_FOREACH_SAFE(bp, &sc->sc_sync_delayed, bio_queue, bp2) { 1331855761d5SPawel Jakub Dawidek if (g_mirror_regular_collision(sc, bp)) 1332855761d5SPawel Jakub Dawidek continue; 13339abe2e7eSMark Johnston TAILQ_REMOVE(&sc->sc_sync_delayed, bp, bio_queue); 1334855761d5SPawel Jakub Dawidek G_MIRROR_LOGREQ(2, bp, 1335855761d5SPawel Jakub Dawidek "Releasing delayed synchronization request."); 1336855761d5SPawel Jakub Dawidek g_io_request(bp, bp->bio_from); 1337855761d5SPawel Jakub Dawidek } 1338855761d5SPawel Jakub Dawidek } 1339855761d5SPawel Jakub Dawidek 1340855761d5SPawel Jakub Dawidek /* 13415c2ac5cfSMark Johnston * Free a synchronization request and clear its slot in the array. 13425c2ac5cfSMark Johnston */ 13435c2ac5cfSMark Johnston static void 13445c2ac5cfSMark Johnston g_mirror_sync_request_free(struct g_mirror_disk *disk, struct bio *bp) 13455c2ac5cfSMark Johnston { 1346a4834289SMark Johnston int idx; 13475c2ac5cfSMark Johnston 13485c2ac5cfSMark Johnston if (disk != NULL && disk->d_sync.ds_bios != NULL) { 1349a4834289SMark Johnston idx = (int)(uintptr_t)bp->bio_caller1; 1350a4834289SMark Johnston KASSERT(disk->d_sync.ds_bios[idx] == bp, 1351a4834289SMark Johnston ("unexpected sync BIO at %p:%d", disk, idx)); 1352a4834289SMark Johnston disk->d_sync.ds_bios[idx] = NULL; 13535c2ac5cfSMark Johnston } 13545c2ac5cfSMark Johnston free(bp->bio_data, M_MIRROR); 13555c2ac5cfSMark Johnston g_destroy_bio(bp); 13565c2ac5cfSMark Johnston } 13575c2ac5cfSMark Johnston 13585c2ac5cfSMark Johnston /* 1359855761d5SPawel Jakub Dawidek * Handle synchronization requests. 1360762f440fSMark Johnston * Every synchronization request is a two-step process: first, a read request is 1361762f440fSMark Johnston * sent to the mirror provider via the sync consumer. If that request completes 1362762f440fSMark Johnston * successfully, it is converted to a write and sent to the disk being 1363762f440fSMark Johnston * synchronized. If the write also completes successfully, the synchronization 1364762f440fSMark Johnston * offset is advanced and a new read request is submitted. 1365855761d5SPawel Jakub Dawidek */ 1366fa4a1febSPawel Jakub Dawidek static void 13671787c3feSMark Johnston g_mirror_sync_request(struct g_mirror_softc *sc, struct bio *bp) 1368fa4a1febSPawel Jakub Dawidek { 1369fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 13705c2ac5cfSMark Johnston struct g_mirror_disk_sync *sync; 1371fa4a1febSPawel Jakub Dawidek 13721787c3feSMark Johnston KASSERT((bp->bio_cmd == BIO_READ && 13731787c3feSMark Johnston bp->bio_from->geom == sc->sc_sync.ds_geom) || 13741787c3feSMark Johnston (bp->bio_cmd == BIO_WRITE && bp->bio_from->geom == sc->sc_geom), 13751787c3feSMark Johnston ("Sync BIO %p with unexpected origin", bp)); 13761787c3feSMark Johnston 13772fdf5be1SPawel Jakub Dawidek bp->bio_from->index--; 1378fa4a1febSPawel Jakub Dawidek disk = bp->bio_from->private; 1379fa4a1febSPawel Jakub Dawidek if (disk == NULL) { 1380855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); /* Avoid recursion on sc_lock. */ 1381fa4a1febSPawel Jakub Dawidek g_topology_lock(); 1382fa4a1febSPawel Jakub Dawidek g_mirror_kill_consumer(sc, bp->bio_from); 1383fa4a1febSPawel Jakub Dawidek g_topology_unlock(); 13845c2ac5cfSMark Johnston g_mirror_sync_request_free(NULL, bp); 1385855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 1386fa4a1febSPawel Jakub Dawidek return; 1387fa4a1febSPawel Jakub Dawidek } 1388fa4a1febSPawel Jakub Dawidek 1389762f440fSMark Johnston sync = &disk->d_sync; 1390762f440fSMark Johnston 1391fa4a1febSPawel Jakub Dawidek /* 1392fa4a1febSPawel Jakub Dawidek * Synchronization request. 1393fa4a1febSPawel Jakub Dawidek */ 1394fa4a1febSPawel Jakub Dawidek switch (bp->bio_cmd) { 1395762f440fSMark Johnston case BIO_READ: { 1396fa4a1febSPawel Jakub Dawidek struct g_consumer *cp; 1397fa4a1febSPawel Jakub Dawidek 139840c5032dSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_sync_request_read, 139940c5032dSMark Johnston bp->bio_error); 140040c5032dSMark Johnston 1401fa4a1febSPawel Jakub Dawidek if (bp->bio_error != 0) { 1402fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(0, bp, 1403fa4a1febSPawel Jakub Dawidek "Synchronization request failed (error=%d).", 1404fa4a1febSPawel Jakub Dawidek bp->bio_error); 1405762f440fSMark Johnston 1406762f440fSMark Johnston /* 1407762f440fSMark Johnston * The read error will trigger a syncid bump, so there's 1408762f440fSMark Johnston * no need to do that here. 1409762f440fSMark Johnston * 14100d02f6c2SMark Johnston * The read error handling for regular requests will 14110d02f6c2SMark Johnston * retry the read from all active mirrors before passing 14120d02f6c2SMark Johnston * the error back up, so there's no need to retry here. 1413762f440fSMark Johnston */ 14145c2ac5cfSMark Johnston g_mirror_sync_request_free(disk, bp); 1415762f440fSMark Johnston g_mirror_event_send(disk, 1416762f440fSMark Johnston G_MIRROR_DISK_STATE_DISCONNECTED, 1417762f440fSMark Johnston G_MIRROR_EVENT_DONTWAIT); 1418fa4a1febSPawel Jakub Dawidek return; 1419fa4a1febSPawel Jakub Dawidek } 14202fdf5be1SPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, 14212fdf5be1SPawel Jakub Dawidek "Synchronization request half-finished."); 1422fa4a1febSPawel Jakub Dawidek bp->bio_cmd = BIO_WRITE; 142351385a3cSPawel Jakub Dawidek bp->bio_cflags = 0; 1424fa4a1febSPawel Jakub Dawidek cp = disk->d_consumer; 1425855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1426fa4a1febSPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, 1427fa4a1febSPawel Jakub Dawidek cp->acr, cp->acw, cp->ace)); 14282fdf5be1SPawel Jakub Dawidek cp->index++; 1429fa4a1febSPawel Jakub Dawidek g_io_request(bp, cp); 1430fa4a1febSPawel Jakub Dawidek return; 1431fa4a1febSPawel Jakub Dawidek } 1432762f440fSMark Johnston case BIO_WRITE: { 1433855761d5SPawel Jakub Dawidek off_t offset; 1434762f440fSMark Johnston int i; 1435e8adbe44SPawel Jakub Dawidek 143640c5032dSMark Johnston KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_sync_request_write, 143740c5032dSMark Johnston bp->bio_error); 143840c5032dSMark Johnston 1439fa4a1febSPawel Jakub Dawidek if (bp->bio_error != 0) { 1440fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(0, bp, 1441fa4a1febSPawel Jakub Dawidek "Synchronization request failed (error=%d).", 1442fa4a1febSPawel Jakub Dawidek bp->bio_error); 14435c2ac5cfSMark Johnston g_mirror_sync_request_free(disk, bp); 1444da844167SPawel Jakub Dawidek sc->sc_bump_id |= G_MIRROR_BUMP_GENID; 1445fa4a1febSPawel Jakub Dawidek g_mirror_event_send(disk, 1446fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_STATE_DISCONNECTED, 1447fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT); 1448fa4a1febSPawel Jakub Dawidek return; 1449fa4a1febSPawel Jakub Dawidek } 1450fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Synchronization request finished."); 145132cea4caSAndrey V. Elsukov if (sync->ds_offset >= sc->sc_mediasize || 1452855761d5SPawel Jakub Dawidek sync->ds_consumer == NULL || 1453855761d5SPawel Jakub Dawidek (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 1454855761d5SPawel Jakub Dawidek /* Don't send more synchronization requests. */ 1455855761d5SPawel Jakub Dawidek sync->ds_inflight--; 14565c2ac5cfSMark Johnston g_mirror_sync_request_free(disk, bp); 1457855761d5SPawel Jakub Dawidek if (sync->ds_inflight > 0) 1458855761d5SPawel Jakub Dawidek return; 1459855761d5SPawel Jakub Dawidek if (sync->ds_consumer == NULL || 1460855761d5SPawel Jakub Dawidek (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 1461855761d5SPawel Jakub Dawidek return; 1462855761d5SPawel Jakub Dawidek } 1463855761d5SPawel Jakub Dawidek /* Disk up-to-date, activate it. */ 1464fa4a1febSPawel Jakub Dawidek g_mirror_event_send(disk, G_MIRROR_DISK_STATE_ACTIVE, 1465fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT); 1466fa4a1febSPawel Jakub Dawidek return; 1467855761d5SPawel Jakub Dawidek } 1468855761d5SPawel Jakub Dawidek 1469855761d5SPawel Jakub Dawidek /* Send next synchronization request. */ 1470762f440fSMark Johnston g_mirror_sync_reinit(disk, bp, sync->ds_offset); 1471855761d5SPawel Jakub Dawidek sync->ds_offset += bp->bio_length; 1472762f440fSMark Johnston 1473855761d5SPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Sending synchronization request."); 1474855761d5SPawel Jakub Dawidek sync->ds_consumer->index++; 1475762f440fSMark Johnston 1476fa4a1febSPawel Jakub Dawidek /* 1477855761d5SPawel Jakub Dawidek * Delay the request if it is colliding with a regular request. 1478fa4a1febSPawel Jakub Dawidek */ 1479855761d5SPawel Jakub Dawidek if (g_mirror_regular_collision(sc, bp)) 1480855761d5SPawel Jakub Dawidek g_mirror_sync_delay(sc, bp); 1481855761d5SPawel Jakub Dawidek else 1482855761d5SPawel Jakub Dawidek g_io_request(bp, sync->ds_consumer); 1483855761d5SPawel Jakub Dawidek 14841787c3feSMark Johnston /* Requeue delayed requests if possible. */ 1485855761d5SPawel Jakub Dawidek g_mirror_regular_release(sc); 1486855761d5SPawel Jakub Dawidek 1487855761d5SPawel Jakub Dawidek /* Find the smallest offset */ 1488855761d5SPawel Jakub Dawidek offset = sc->sc_mediasize; 1489855761d5SPawel Jakub Dawidek for (i = 0; i < g_mirror_syncreqs; i++) { 1490855761d5SPawel Jakub Dawidek bp = sync->ds_bios[i]; 1491a4834289SMark Johnston if (bp != NULL && bp->bio_offset < offset) 1492855761d5SPawel Jakub Dawidek offset = bp->bio_offset; 1493855761d5SPawel Jakub Dawidek } 14942ceafb77SMark Johnston if (g_mirror_sync_period > 0 && 14952ceafb77SMark Johnston time_uptime - sync->ds_update_ts > g_mirror_sync_period) { 1496855761d5SPawel Jakub Dawidek sync->ds_offset_done = offset; 1497fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(disk); 14982ceafb77SMark Johnston sync->ds_update_ts = time_uptime; 1499fa4a1febSPawel Jakub Dawidek } 1500fa4a1febSPawel Jakub Dawidek return; 1501e8adbe44SPawel Jakub Dawidek } 1502fa4a1febSPawel Jakub Dawidek default: 1503762f440fSMark Johnston panic("Invalid I/O request %p", bp); 1504fa4a1febSPawel Jakub Dawidek } 1505fa4a1febSPawel Jakub Dawidek } 1506fa4a1febSPawel Jakub Dawidek 1507fa4a1febSPawel Jakub Dawidek static void 1508fe7c3780SPawel Jakub Dawidek g_mirror_request_prefer(struct g_mirror_softc *sc, struct bio *bp) 1509fe7c3780SPawel Jakub Dawidek { 1510fe7c3780SPawel Jakub Dawidek struct g_mirror_disk *disk; 1511fe7c3780SPawel Jakub Dawidek struct g_consumer *cp; 1512fe7c3780SPawel Jakub Dawidek struct bio *cbp; 1513fe7c3780SPawel Jakub Dawidek 1514fe7c3780SPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 1515fe7c3780SPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) 1516fe7c3780SPawel Jakub Dawidek break; 1517fe7c3780SPawel Jakub Dawidek } 1518fe7c3780SPawel Jakub Dawidek if (disk == NULL) { 1519fe7c3780SPawel Jakub Dawidek if (bp->bio_error == 0) 1520fe7c3780SPawel Jakub Dawidek bp->bio_error = ENXIO; 1521fe7c3780SPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1522fe7c3780SPawel Jakub Dawidek return; 1523fe7c3780SPawel Jakub Dawidek } 1524fe7c3780SPawel Jakub Dawidek cbp = g_clone_bio(bp); 1525fe7c3780SPawel Jakub Dawidek if (cbp == NULL) { 1526fe7c3780SPawel Jakub Dawidek if (bp->bio_error == 0) 1527fe7c3780SPawel Jakub Dawidek bp->bio_error = ENOMEM; 1528fe7c3780SPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1529fe7c3780SPawel Jakub Dawidek return; 1530fe7c3780SPawel Jakub Dawidek } 1531fe7c3780SPawel Jakub Dawidek /* 1532fe7c3780SPawel Jakub Dawidek * Fill in the component buf structure. 1533fe7c3780SPawel Jakub Dawidek */ 1534fe7c3780SPawel Jakub Dawidek cp = disk->d_consumer; 1535fe7c3780SPawel Jakub Dawidek cbp->bio_done = g_mirror_done; 1536fe7c3780SPawel Jakub Dawidek cbp->bio_to = cp->provider; 1537fe7c3780SPawel Jakub Dawidek G_MIRROR_LOGREQ(3, cbp, "Sending request."); 1538855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1539fe7c3780SPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, cp->acr, 1540fe7c3780SPawel Jakub Dawidek cp->acw, cp->ace)); 15412fdf5be1SPawel Jakub Dawidek cp->index++; 1542fe7c3780SPawel Jakub Dawidek g_io_request(cbp, cp); 1543fe7c3780SPawel Jakub Dawidek } 1544fe7c3780SPawel Jakub Dawidek 1545fe7c3780SPawel Jakub Dawidek static void 1546fa4a1febSPawel Jakub Dawidek g_mirror_request_round_robin(struct g_mirror_softc *sc, struct bio *bp) 1547fa4a1febSPawel Jakub Dawidek { 1548fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 1549fa4a1febSPawel Jakub Dawidek struct g_consumer *cp; 1550fa4a1febSPawel Jakub Dawidek struct bio *cbp; 1551fa4a1febSPawel Jakub Dawidek 1552fa4a1febSPawel Jakub Dawidek disk = g_mirror_get_disk(sc); 1553fa4a1febSPawel Jakub Dawidek if (disk == NULL) { 1554fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0) 1555fa4a1febSPawel Jakub Dawidek bp->bio_error = ENXIO; 1556fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1557fa4a1febSPawel Jakub Dawidek return; 1558fa4a1febSPawel Jakub Dawidek } 1559fa4a1febSPawel Jakub Dawidek cbp = g_clone_bio(bp); 1560fa4a1febSPawel Jakub Dawidek if (cbp == NULL) { 1561fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0) 1562fa4a1febSPawel Jakub Dawidek bp->bio_error = ENOMEM; 1563fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1564fa4a1febSPawel Jakub Dawidek return; 1565fa4a1febSPawel Jakub Dawidek } 1566fa4a1febSPawel Jakub Dawidek /* 1567fa4a1febSPawel Jakub Dawidek * Fill in the component buf structure. 1568fa4a1febSPawel Jakub Dawidek */ 1569fa4a1febSPawel Jakub Dawidek cp = disk->d_consumer; 1570fa4a1febSPawel Jakub Dawidek cbp->bio_done = g_mirror_done; 1571fa4a1febSPawel Jakub Dawidek cbp->bio_to = cp->provider; 1572fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, cbp, "Sending request."); 1573855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1574fa4a1febSPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, cp->acr, 1575fa4a1febSPawel Jakub Dawidek cp->acw, cp->ace)); 15762fdf5be1SPawel Jakub Dawidek cp->index++; 1577fa4a1febSPawel Jakub Dawidek g_io_request(cbp, cp); 1578fa4a1febSPawel Jakub Dawidek } 1579fa4a1febSPawel Jakub Dawidek 1580891852ccSAlexander Motin #define TRACK_SIZE (1 * 1024 * 1024) 1581891852ccSAlexander Motin #define LOAD_SCALE 256 1582891852ccSAlexander Motin #define ABS(x) (((x) >= 0) ? (x) : (-(x))) 1583891852ccSAlexander Motin 1584fa4a1febSPawel Jakub Dawidek static void 1585fa4a1febSPawel Jakub Dawidek g_mirror_request_load(struct g_mirror_softc *sc, struct bio *bp) 1586fa4a1febSPawel Jakub Dawidek { 1587fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk, *dp; 1588fa4a1febSPawel Jakub Dawidek struct g_consumer *cp; 1589fa4a1febSPawel Jakub Dawidek struct bio *cbp; 1590891852ccSAlexander Motin int prio, best; 1591fa4a1febSPawel Jakub Dawidek 1592891852ccSAlexander Motin /* Find a disk with the smallest load. */ 1593fa4a1febSPawel Jakub Dawidek disk = NULL; 1594891852ccSAlexander Motin best = INT_MAX; 1595fa4a1febSPawel Jakub Dawidek LIST_FOREACH(dp, &sc->sc_disks, d_next) { 1596fa4a1febSPawel Jakub Dawidek if (dp->d_state != G_MIRROR_DISK_STATE_ACTIVE) 1597fa4a1febSPawel Jakub Dawidek continue; 1598891852ccSAlexander Motin prio = dp->load; 1599891852ccSAlexander Motin /* If disk head is precisely in position - highly prefer it. */ 1600891852ccSAlexander Motin if (dp->d_last_offset == bp->bio_offset) 1601891852ccSAlexander Motin prio -= 2 * LOAD_SCALE; 1602891852ccSAlexander Motin else 1603891852ccSAlexander Motin /* If disk head is close to position - prefer it. */ 1604891852ccSAlexander Motin if (ABS(dp->d_last_offset - bp->bio_offset) < TRACK_SIZE) 1605891852ccSAlexander Motin prio -= 1 * LOAD_SCALE; 1606891852ccSAlexander Motin if (prio <= best) { 1607fa4a1febSPawel Jakub Dawidek disk = dp; 1608891852ccSAlexander Motin best = prio; 1609fa4a1febSPawel Jakub Dawidek } 1610fa4a1febSPawel Jakub Dawidek } 1611862f5624SPawel Jakub Dawidek KASSERT(disk != NULL, ("NULL disk for %s.", sc->sc_name)); 1612fa4a1febSPawel Jakub Dawidek cbp = g_clone_bio(bp); 1613fa4a1febSPawel Jakub Dawidek if (cbp == NULL) { 1614fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0) 1615fa4a1febSPawel Jakub Dawidek bp->bio_error = ENOMEM; 1616fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1617fa4a1febSPawel Jakub Dawidek return; 1618fa4a1febSPawel Jakub Dawidek } 1619fa4a1febSPawel Jakub Dawidek /* 1620fa4a1febSPawel Jakub Dawidek * Fill in the component buf structure. 1621fa4a1febSPawel Jakub Dawidek */ 1622fa4a1febSPawel Jakub Dawidek cp = disk->d_consumer; 1623fa4a1febSPawel Jakub Dawidek cbp->bio_done = g_mirror_done; 1624fa4a1febSPawel Jakub Dawidek cbp->bio_to = cp->provider; 1625fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, cbp, "Sending request."); 1626855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1627fa4a1febSPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, cp->acr, 1628fa4a1febSPawel Jakub Dawidek cp->acw, cp->ace)); 16292fdf5be1SPawel Jakub Dawidek cp->index++; 1630891852ccSAlexander Motin /* Remember last head position */ 1631891852ccSAlexander Motin disk->d_last_offset = bp->bio_offset + bp->bio_length; 1632891852ccSAlexander Motin /* Update loads. */ 1633891852ccSAlexander Motin LIST_FOREACH(dp, &sc->sc_disks, d_next) { 1634891852ccSAlexander Motin dp->load = (dp->d_consumer->index * LOAD_SCALE + 1635891852ccSAlexander Motin dp->load * 7) / 8; 1636891852ccSAlexander Motin } 1637fa4a1febSPawel Jakub Dawidek g_io_request(cbp, cp); 1638fa4a1febSPawel Jakub Dawidek } 1639fa4a1febSPawel Jakub Dawidek 1640fa4a1febSPawel Jakub Dawidek static void 1641fa4a1febSPawel Jakub Dawidek g_mirror_request_split(struct g_mirror_softc *sc, struct bio *bp) 1642fa4a1febSPawel Jakub Dawidek { 16439abe2e7eSMark Johnston struct bio_queue queue; 1644fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 16450d81fba6SMateusz Guzik struct g_consumer *cp __diagused; 1646fa4a1febSPawel Jakub Dawidek struct bio *cbp; 1647fa4a1febSPawel Jakub Dawidek off_t left, mod, offset, slice; 1648fa4a1febSPawel Jakub Dawidek u_char *data; 1649fa4a1febSPawel Jakub Dawidek u_int ndisks; 1650fa4a1febSPawel Jakub Dawidek 1651fa4a1febSPawel Jakub Dawidek if (bp->bio_length <= sc->sc_slice) { 1652fa4a1febSPawel Jakub Dawidek g_mirror_request_round_robin(sc, bp); 1653fa4a1febSPawel Jakub Dawidek return; 1654fa4a1febSPawel Jakub Dawidek } 1655fa4a1febSPawel Jakub Dawidek ndisks = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); 1656fa4a1febSPawel Jakub Dawidek slice = bp->bio_length / ndisks; 1657fa4a1febSPawel Jakub Dawidek mod = slice % sc->sc_provider->sectorsize; 1658fa4a1febSPawel Jakub Dawidek if (mod != 0) 1659fa4a1febSPawel Jakub Dawidek slice += sc->sc_provider->sectorsize - mod; 1660fa4a1febSPawel Jakub Dawidek /* 1661fa4a1febSPawel Jakub Dawidek * Allocate all bios before sending any request, so we can 1662fa4a1febSPawel Jakub Dawidek * return ENOMEM in nice and clean way. 1663fa4a1febSPawel Jakub Dawidek */ 1664fa4a1febSPawel Jakub Dawidek left = bp->bio_length; 1665fa4a1febSPawel Jakub Dawidek offset = bp->bio_offset; 1666fa4a1febSPawel Jakub Dawidek data = bp->bio_data; 16679abe2e7eSMark Johnston TAILQ_INIT(&queue); 1668fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 1669fa4a1febSPawel Jakub Dawidek if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) 1670fa4a1febSPawel Jakub Dawidek continue; 1671fa4a1febSPawel Jakub Dawidek cbp = g_clone_bio(bp); 1672fa4a1febSPawel Jakub Dawidek if (cbp == NULL) { 16739abe2e7eSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 16749abe2e7eSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 16755d85cd2dSAlexander Motin g_destroy_bio(cbp); 16769abe2e7eSMark Johnston } 1677fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0) 1678fa4a1febSPawel Jakub Dawidek bp->bio_error = ENOMEM; 1679fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1680fa4a1febSPawel Jakub Dawidek return; 1681fa4a1febSPawel Jakub Dawidek } 16829abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 1683fa4a1febSPawel Jakub Dawidek cbp->bio_done = g_mirror_done; 1684fa4a1febSPawel Jakub Dawidek cbp->bio_caller1 = disk; 1685fa4a1febSPawel Jakub Dawidek cbp->bio_to = disk->d_consumer->provider; 1686fa4a1febSPawel Jakub Dawidek cbp->bio_offset = offset; 1687fa4a1febSPawel Jakub Dawidek cbp->bio_data = data; 1688fa4a1febSPawel Jakub Dawidek cbp->bio_length = MIN(left, slice); 1689fa4a1febSPawel Jakub Dawidek left -= cbp->bio_length; 1690fa4a1febSPawel Jakub Dawidek if (left == 0) 1691fa4a1febSPawel Jakub Dawidek break; 1692fa4a1febSPawel Jakub Dawidek offset += cbp->bio_length; 1693fa4a1febSPawel Jakub Dawidek data += cbp->bio_length; 1694fa4a1febSPawel Jakub Dawidek } 16959abe2e7eSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 16969abe2e7eSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 1697fa4a1febSPawel Jakub Dawidek G_MIRROR_LOGREQ(3, cbp, "Sending request."); 1698fa4a1febSPawel Jakub Dawidek disk = cbp->bio_caller1; 1699fa4a1febSPawel Jakub Dawidek cbp->bio_caller1 = NULL; 1700fa4a1febSPawel Jakub Dawidek cp = disk->d_consumer; 1701855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1702fa4a1febSPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, 1703fa4a1febSPawel Jakub Dawidek cp->acr, cp->acw, cp->ace)); 17042fdf5be1SPawel Jakub Dawidek disk->d_consumer->index++; 1705fa4a1febSPawel Jakub Dawidek g_io_request(cbp, disk->d_consumer); 1706fa4a1febSPawel Jakub Dawidek } 1707fa4a1febSPawel Jakub Dawidek } 1708fa4a1febSPawel Jakub Dawidek 1709fa4a1febSPawel Jakub Dawidek static void 17101787c3feSMark Johnston g_mirror_register_request(struct g_mirror_softc *sc, struct bio *bp) 1711fa4a1febSPawel Jakub Dawidek { 17121787c3feSMark Johnston struct bio_queue queue; 17131787c3feSMark Johnston struct bio *cbp; 17141787c3feSMark Johnston struct g_consumer *cp; 17151787c3feSMark Johnston struct g_mirror_disk *disk; 1716fa4a1febSPawel Jakub Dawidek 17171787c3feSMark Johnston sx_assert(&sc->sc_lock, SA_XLOCKED); 17181787c3feSMark Johnston 17191787c3feSMark Johnston /* 17201787c3feSMark Johnston * To avoid ordering issues, if a write is deferred because of a 17211787c3feSMark Johnston * collision with a sync request, all I/O is deferred until that 17221787c3feSMark Johnston * write is initiated. 17231787c3feSMark Johnston */ 17241787c3feSMark Johnston if (bp->bio_from->geom != sc->sc_sync.ds_geom && 17251787c3feSMark Johnston !TAILQ_EMPTY(&sc->sc_regular_delayed)) { 17261787c3feSMark Johnston g_mirror_regular_delay(sc, bp); 17271787c3feSMark Johnston return; 17281787c3feSMark Johnston } 17291787c3feSMark Johnston 1730fa4a1febSPawel Jakub Dawidek switch (bp->bio_cmd) { 1731fa4a1febSPawel Jakub Dawidek case BIO_READ: 1732fa4a1febSPawel Jakub Dawidek switch (sc->sc_balance) { 1733fa4a1febSPawel Jakub Dawidek case G_MIRROR_BALANCE_LOAD: 1734fa4a1febSPawel Jakub Dawidek g_mirror_request_load(sc, bp); 1735fa4a1febSPawel Jakub Dawidek break; 1736fe7c3780SPawel Jakub Dawidek case G_MIRROR_BALANCE_PREFER: 1737fe7c3780SPawel Jakub Dawidek g_mirror_request_prefer(sc, bp); 1738fe7c3780SPawel Jakub Dawidek break; 1739fe7c3780SPawel Jakub Dawidek case G_MIRROR_BALANCE_ROUND_ROBIN: 1740fe7c3780SPawel Jakub Dawidek g_mirror_request_round_robin(sc, bp); 1741fe7c3780SPawel Jakub Dawidek break; 1742fa4a1febSPawel Jakub Dawidek case G_MIRROR_BALANCE_SPLIT: 1743fa4a1febSPawel Jakub Dawidek g_mirror_request_split(sc, bp); 1744fa4a1febSPawel Jakub Dawidek break; 1745fa4a1febSPawel Jakub Dawidek } 1746fa4a1febSPawel Jakub Dawidek return; 1747fa4a1febSPawel Jakub Dawidek case BIO_WRITE: 1748fa4a1febSPawel Jakub Dawidek case BIO_DELETE: 1749855761d5SPawel Jakub Dawidek /* 1750855761d5SPawel Jakub Dawidek * Delay the request if it is colliding with a synchronization 1751855761d5SPawel Jakub Dawidek * request. 1752855761d5SPawel Jakub Dawidek */ 1753855761d5SPawel Jakub Dawidek if (g_mirror_sync_collision(sc, bp)) { 1754855761d5SPawel Jakub Dawidek g_mirror_regular_delay(sc, bp); 1755855761d5SPawel Jakub Dawidek return; 1756855761d5SPawel Jakub Dawidek } 1757855761d5SPawel Jakub Dawidek 17582fdf5be1SPawel Jakub Dawidek if (sc->sc_idle) 17592fdf5be1SPawel Jakub Dawidek g_mirror_unidle(sc); 1760fe6f94eaSPawel Jakub Dawidek else 176101f1f41cSPawel Jakub Dawidek sc->sc_last_write = time_uptime; 1762fe6f94eaSPawel Jakub Dawidek 1763fa4a1febSPawel Jakub Dawidek /* 17644dea20beSMark Johnston * Bump syncid on first write. 17654dea20beSMark Johnston */ 17664dea20beSMark Johnston if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID) != 0) { 17674dea20beSMark Johnston sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID; 17684dea20beSMark Johnston g_mirror_bump_syncid(sc); 17694dea20beSMark Johnston } 17704dea20beSMark Johnston 17714dea20beSMark Johnston /* 1772fa4a1febSPawel Jakub Dawidek * Allocate all bios before sending any request, so we can 1773fa4a1febSPawel Jakub Dawidek * return ENOMEM in nice and clean way. 1774fa4a1febSPawel Jakub Dawidek */ 17759abe2e7eSMark Johnston TAILQ_INIT(&queue); 1776fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 1777fa4a1febSPawel Jakub Dawidek switch (disk->d_state) { 1778fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_ACTIVE: 1779fa4a1febSPawel Jakub Dawidek break; 1780fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_SYNCHRONIZING: 17811787c3feSMark Johnston if (bp->bio_offset >= disk->d_sync.ds_offset) 1782fa4a1febSPawel Jakub Dawidek continue; 1783fa4a1febSPawel Jakub Dawidek break; 1784fa4a1febSPawel Jakub Dawidek default: 1785fa4a1febSPawel Jakub Dawidek continue; 1786fa4a1febSPawel Jakub Dawidek } 1787d89862acSGleb Smirnoff if (bp->bio_cmd == BIO_DELETE && 1788d89862acSGleb Smirnoff (disk->d_flags & G_MIRROR_DISK_FLAG_CANDELETE) == 0) 1789d89862acSGleb Smirnoff continue; 1790fa4a1febSPawel Jakub Dawidek cbp = g_clone_bio(bp); 1791fa4a1febSPawel Jakub Dawidek if (cbp == NULL) { 17929abe2e7eSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 17939abe2e7eSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 1794fa4a1febSPawel Jakub Dawidek g_destroy_bio(cbp); 17959abe2e7eSMark Johnston } 1796fa4a1febSPawel Jakub Dawidek if (bp->bio_error == 0) 1797fa4a1febSPawel Jakub Dawidek bp->bio_error = ENOMEM; 1798fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, bp->bio_error); 1799fa4a1febSPawel Jakub Dawidek return; 1800fa4a1febSPawel Jakub Dawidek } 18019abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 1802fa4a1febSPawel Jakub Dawidek cbp->bio_done = g_mirror_done; 180331522023SPawel Jakub Dawidek cp = disk->d_consumer; 180431522023SPawel Jakub Dawidek cbp->bio_caller1 = cp; 1805fa4a1febSPawel Jakub Dawidek cbp->bio_to = cp->provider; 1806855761d5SPawel Jakub Dawidek KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 1807fa4a1febSPawel Jakub Dawidek ("Consumer %s not opened (r%dw%de%d).", 1808fa4a1febSPawel Jakub Dawidek cp->provider->name, cp->acr, cp->acw, cp->ace)); 180931522023SPawel Jakub Dawidek } 18109abe2e7eSMark Johnston if (TAILQ_EMPTY(&queue)) { 18111787c3feSMark Johnston KASSERT(bp->bio_cmd == BIO_DELETE, 18121787c3feSMark Johnston ("No consumers for regular request %p", bp)); 18137715befdSAlexander Motin g_io_deliver(bp, EOPNOTSUPP); 18147715befdSAlexander Motin return; 18157715befdSAlexander Motin } 18169abe2e7eSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 181731522023SPawel Jakub Dawidek G_MIRROR_LOGREQ(3, cbp, "Sending request."); 18189abe2e7eSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 181931522023SPawel Jakub Dawidek cp = cbp->bio_caller1; 182031522023SPawel Jakub Dawidek cbp->bio_caller1 = NULL; 18212fdf5be1SPawel Jakub Dawidek cp->index++; 1822fe6f94eaSPawel Jakub Dawidek sc->sc_writes++; 1823fa4a1febSPawel Jakub Dawidek g_io_request(cbp, cp); 1824fa4a1febSPawel Jakub Dawidek } 1825fa4a1febSPawel Jakub Dawidek /* 1826855761d5SPawel Jakub Dawidek * Put request onto inflight queue, so we can check if new 1827855761d5SPawel Jakub Dawidek * synchronization requests don't collide with it. 1828855761d5SPawel Jakub Dawidek */ 18299abe2e7eSMark Johnston TAILQ_INSERT_TAIL(&sc->sc_inflight, bp, bio_queue); 1830fa4a1febSPawel Jakub Dawidek return; 18318b522bdaSWarner Losh case BIO_SPEEDUP: 18321787c3feSMark Johnston case BIO_FLUSH: 18331787c3feSMark Johnston TAILQ_INIT(&queue); 18341787c3feSMark Johnston LIST_FOREACH(disk, &sc->sc_disks, d_next) { 18351787c3feSMark Johnston if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) 18361787c3feSMark Johnston continue; 18371787c3feSMark Johnston cbp = g_clone_bio(bp); 18381787c3feSMark Johnston if (cbp == NULL) { 18391787c3feSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 18401787c3feSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 18411787c3feSMark Johnston g_destroy_bio(cbp); 1842fa4a1febSPawel Jakub Dawidek } 18431787c3feSMark Johnston if (bp->bio_error == 0) 18441787c3feSMark Johnston bp->bio_error = ENOMEM; 18451787c3feSMark Johnston g_io_deliver(bp, bp->bio_error); 18461787c3feSMark Johnston return; 18471787c3feSMark Johnston } 18481787c3feSMark Johnston TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); 18491787c3feSMark Johnston cbp->bio_done = g_mirror_done; 18501787c3feSMark Johnston cbp->bio_caller1 = disk; 18511787c3feSMark Johnston cbp->bio_to = disk->d_consumer->provider; 18521787c3feSMark Johnston } 18531787c3feSMark Johnston KASSERT(!TAILQ_EMPTY(&queue), 18541787c3feSMark Johnston ("No consumers for regular request %p", bp)); 18551787c3feSMark Johnston while ((cbp = TAILQ_FIRST(&queue)) != NULL) { 18561787c3feSMark Johnston G_MIRROR_LOGREQ(3, cbp, "Sending request."); 18571787c3feSMark Johnston TAILQ_REMOVE(&queue, cbp, bio_queue); 18581787c3feSMark Johnston disk = cbp->bio_caller1; 18591787c3feSMark Johnston cbp->bio_caller1 = NULL; 18601787c3feSMark Johnston cp = disk->d_consumer; 18611787c3feSMark Johnston KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, 18621787c3feSMark Johnston ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, 18631787c3feSMark Johnston cp->acr, cp->acw, cp->ace)); 18641787c3feSMark Johnston cp->index++; 18651787c3feSMark Johnston g_io_request(cbp, cp); 18661787c3feSMark Johnston } 18671787c3feSMark Johnston break; 1868fa4a1febSPawel Jakub Dawidek default: 1869fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("Invalid command here: %u (device=%s)", 1870fa4a1febSPawel Jakub Dawidek bp->bio_cmd, sc->sc_name)); 1871fa4a1febSPawel Jakub Dawidek break; 1872fa4a1febSPawel Jakub Dawidek } 1873fa4a1febSPawel Jakub Dawidek } 1874fa4a1febSPawel Jakub Dawidek 18756d8fb92dSPawel Jakub Dawidek static int 18766d8fb92dSPawel Jakub Dawidek g_mirror_can_destroy(struct g_mirror_softc *sc) 18776d8fb92dSPawel Jakub Dawidek { 18786d8fb92dSPawel Jakub Dawidek struct g_geom *gp; 18796d8fb92dSPawel Jakub Dawidek struct g_consumer *cp; 18806d8fb92dSPawel Jakub Dawidek 18816d8fb92dSPawel Jakub Dawidek g_topology_assert(); 18826d8fb92dSPawel Jakub Dawidek gp = sc->sc_geom; 1883a0636676SPawel Jakub Dawidek if (gp->softc == NULL) 1884a0636676SPawel Jakub Dawidek return (1); 18851ee0138dSAndrey V. Elsukov if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_TASTING) != 0) 18861ee0138dSAndrey V. Elsukov return (0); 18876d8fb92dSPawel Jakub Dawidek LIST_FOREACH(cp, &gp->consumer, consumer) { 18886d8fb92dSPawel Jakub Dawidek if (g_mirror_is_busy(sc, cp)) 18896d8fb92dSPawel Jakub Dawidek return (0); 18906d8fb92dSPawel Jakub Dawidek } 18916d8fb92dSPawel Jakub Dawidek gp = sc->sc_sync.ds_geom; 18926d8fb92dSPawel Jakub Dawidek LIST_FOREACH(cp, &gp->consumer, consumer) { 18936d8fb92dSPawel Jakub Dawidek if (g_mirror_is_busy(sc, cp)) 18946d8fb92dSPawel Jakub Dawidek return (0); 18956d8fb92dSPawel Jakub Dawidek } 18966d8fb92dSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "No I/O requests for %s, it can be destroyed.", 18976d8fb92dSPawel Jakub Dawidek sc->sc_name); 18986d8fb92dSPawel Jakub Dawidek return (1); 18996d8fb92dSPawel Jakub Dawidek } 19006d8fb92dSPawel Jakub Dawidek 19016d8fb92dSPawel Jakub Dawidek static int 19026d8fb92dSPawel Jakub Dawidek g_mirror_try_destroy(struct g_mirror_softc *sc) 19036d8fb92dSPawel Jakub Dawidek { 19046d8fb92dSPawel Jakub Dawidek 19050f2bbe5bSPawel Jakub Dawidek if (sc->sc_rootmount != NULL) { 19060f2bbe5bSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "root_mount_rel[%u] %p", __LINE__, 19070f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount); 19080f2bbe5bSPawel Jakub Dawidek root_mount_rel(sc->sc_rootmount); 19090f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount = NULL; 19100f2bbe5bSPawel Jakub Dawidek } 19116d8fb92dSPawel Jakub Dawidek g_topology_lock(); 19126d8fb92dSPawel Jakub Dawidek if (!g_mirror_can_destroy(sc)) { 19136d8fb92dSPawel Jakub Dawidek g_topology_unlock(); 19146d8fb92dSPawel Jakub Dawidek return (0); 19156d8fb92dSPawel Jakub Dawidek } 1916a0636676SPawel Jakub Dawidek sc->sc_geom->softc = NULL; 1917a0636676SPawel Jakub Dawidek sc->sc_sync.ds_geom->softc = NULL; 1918a7d94fccSMark Johnston if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DRAIN) != 0) { 19196d8fb92dSPawel Jakub Dawidek g_topology_unlock(); 19206d8fb92dSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, 19216d8fb92dSPawel Jakub Dawidek &sc->sc_worker); 1922855761d5SPawel Jakub Dawidek /* Unlock sc_lock here, as it can be destroyed after wakeup. */ 1923855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 19246d8fb92dSPawel Jakub Dawidek wakeup(&sc->sc_worker); 19256d8fb92dSPawel Jakub Dawidek sc->sc_worker = NULL; 19266d8fb92dSPawel Jakub Dawidek } else { 19276d8fb92dSPawel Jakub Dawidek g_topology_unlock(); 1928855761d5SPawel Jakub Dawidek g_mirror_destroy_device(sc); 19296d8fb92dSPawel Jakub Dawidek } 19306d8fb92dSPawel Jakub Dawidek return (1); 19316d8fb92dSPawel Jakub Dawidek } 19326d8fb92dSPawel Jakub Dawidek 1933fa4a1febSPawel Jakub Dawidek /* 1934fa4a1febSPawel Jakub Dawidek * Worker thread. 1935fa4a1febSPawel Jakub Dawidek */ 1936fa4a1febSPawel Jakub Dawidek static void 1937fa4a1febSPawel Jakub Dawidek g_mirror_worker(void *arg) 1938fa4a1febSPawel Jakub Dawidek { 1939fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 1940fa4a1febSPawel Jakub Dawidek struct g_mirror_event *ep; 1941fa4a1febSPawel Jakub Dawidek struct bio *bp; 1942fe6f94eaSPawel Jakub Dawidek int timeout; 1943fa4a1febSPawel Jakub Dawidek 1944fa4a1febSPawel Jakub Dawidek sc = arg; 1945982d11f8SJeff Roberson thread_lock(curthread); 194663710c4dSJohn Baldwin sched_prio(curthread, PRIBIO); 1947982d11f8SJeff Roberson thread_unlock(curthread); 1948fa4a1febSPawel Jakub Dawidek 1949855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 1950fa4a1febSPawel Jakub Dawidek for (;;) { 1951fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(5, "%s: Let's see...", __func__); 1952fa4a1febSPawel Jakub Dawidek /* 1953fa4a1febSPawel Jakub Dawidek * First take a look at events. 1954fa4a1febSPawel Jakub Dawidek * This is important to handle events before any I/O requests. 1955fa4a1febSPawel Jakub Dawidek */ 1956b634781eSMark Johnston ep = g_mirror_event_first(sc); 1957855761d5SPawel Jakub Dawidek if (ep != NULL) { 1958f663832bSPawel Jakub Dawidek g_mirror_event_remove(sc, ep); 1959fa4a1febSPawel Jakub Dawidek if ((ep->e_flags & G_MIRROR_EVENT_DEVICE) != 0) { 1960fa4a1febSPawel Jakub Dawidek /* Update only device status. */ 1961fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(3, 1962fa4a1febSPawel Jakub Dawidek "Running event for device %s.", 1963fa4a1febSPawel Jakub Dawidek sc->sc_name); 1964fa4a1febSPawel Jakub Dawidek ep->e_error = 0; 1965fff048e4SMark Johnston g_mirror_update_device(sc, true); 1966fa4a1febSPawel Jakub Dawidek } else { 1967fa4a1febSPawel Jakub Dawidek /* Update disk status. */ 1968fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(3, "Running event for disk %s.", 1969fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(ep->e_disk)); 1970fa4a1febSPawel Jakub Dawidek ep->e_error = g_mirror_update_disk(ep->e_disk, 19719eec299fSPawel Jakub Dawidek ep->e_state); 1972fa4a1febSPawel Jakub Dawidek if (ep->e_error == 0) 1973fff048e4SMark Johnston g_mirror_update_device(sc, false); 1974fa4a1febSPawel Jakub Dawidek } 1975fa4a1febSPawel Jakub Dawidek if ((ep->e_flags & G_MIRROR_EVENT_DONTWAIT) != 0) { 1976fa4a1febSPawel Jakub Dawidek KASSERT(ep->e_error == 0, 1977fa4a1febSPawel Jakub Dawidek ("Error cannot be handled.")); 1978fa4a1febSPawel Jakub Dawidek g_mirror_event_free(ep); 1979fa4a1febSPawel Jakub Dawidek } else { 1980fa4a1febSPawel Jakub Dawidek ep->e_flags |= G_MIRROR_EVENT_DONE; 1981fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, 1982fa4a1febSPawel Jakub Dawidek ep); 1983fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_events_mtx); 1984fa4a1febSPawel Jakub Dawidek wakeup(ep); 1985fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_events_mtx); 1986fa4a1febSPawel Jakub Dawidek } 1987fa4a1febSPawel Jakub Dawidek if ((sc->sc_flags & 1988fa4a1febSPawel Jakub Dawidek G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 1989855761d5SPawel Jakub Dawidek if (g_mirror_try_destroy(sc)) { 1990855761d5SPawel Jakub Dawidek curthread->td_pflags &= ~TDP_GEOM; 1991855761d5SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Thread exiting."); 19923745c395SJulian Elischer kproc_exit(0); 1993fa4a1febSPawel Jakub Dawidek } 1994855761d5SPawel Jakub Dawidek } 1995fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(5, "%s: I'm here 1.", __func__); 1996fa4a1febSPawel Jakub Dawidek continue; 1997fa4a1febSPawel Jakub Dawidek } 19981787c3feSMark Johnston 1999fa4a1febSPawel Jakub Dawidek /* 2000fe6f94eaSPawel Jakub Dawidek * Check if we can mark array as CLEAN and if we can't take 2001fe6f94eaSPawel Jakub Dawidek * how much seconds should we wait. 2002fe6f94eaSPawel Jakub Dawidek */ 2003855761d5SPawel Jakub Dawidek timeout = g_mirror_idle(sc, -1); 20041787c3feSMark Johnston 2005fe6f94eaSPawel Jakub Dawidek /* 20061787c3feSMark Johnston * Handle I/O requests. 2007fa4a1febSPawel Jakub Dawidek */ 2008fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 20099abe2e7eSMark Johnston bp = TAILQ_FIRST(&sc->sc_queue); 20109abe2e7eSMark Johnston if (bp != NULL) 20119abe2e7eSMark Johnston TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue); 20129abe2e7eSMark Johnston else { 2013fa4a1febSPawel Jakub Dawidek if ((sc->sc_flags & 2014fa4a1febSPawel Jakub Dawidek G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 2015fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 2016855761d5SPawel Jakub Dawidek if (g_mirror_try_destroy(sc)) { 2017855761d5SPawel Jakub Dawidek curthread->td_pflags &= ~TDP_GEOM; 2018855761d5SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Thread exiting."); 20193745c395SJulian Elischer kproc_exit(0); 2020855761d5SPawel Jakub Dawidek } 20216d8fb92dSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 20229abe2e7eSMark Johnston if (!TAILQ_EMPTY(&sc->sc_queue)) { 20230d75d0dfSMark Johnston mtx_unlock(&sc->sc_queue_mtx); 20240d75d0dfSMark Johnston continue; 20250d75d0dfSMark Johnston } 2026fa4a1febSPawel Jakub Dawidek } 20277653e6d7SMark Johnston if (g_mirror_event_first(sc) != NULL) { 20287653e6d7SMark Johnston mtx_unlock(&sc->sc_queue_mtx); 20298b937705SMark Johnston continue; 20307653e6d7SMark Johnston } 2031855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 2032fe6f94eaSPawel Jakub Dawidek MSLEEP(sc, &sc->sc_queue_mtx, PRIBIO | PDROP, "m:w1", 2033fe6f94eaSPawel Jakub Dawidek timeout * hz); 2034855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 20359bb09163SPawel Jakub Dawidek G_MIRROR_DEBUG(5, "%s: I'm here 4.", __func__); 2036fa4a1febSPawel Jakub Dawidek continue; 2037fa4a1febSPawel Jakub Dawidek } 2038fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 2039fa4a1febSPawel Jakub Dawidek 20408e007c52SPawel Jakub Dawidek if (bp->bio_from->geom == sc->sc_sync.ds_geom && 20418e007c52SPawel Jakub Dawidek (bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) { 20421787c3feSMark Johnston /* 20431787c3feSMark Johnston * Handle completion of the first half (the read) of a 20441787c3feSMark Johnston * block synchronization operation. 20451787c3feSMark Johnston */ 20461787c3feSMark Johnston g_mirror_sync_request(sc, bp); 20478e007c52SPawel Jakub Dawidek } else if (bp->bio_to != sc->sc_provider) { 2048855761d5SPawel Jakub Dawidek if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_REGULAR) != 0) 20491787c3feSMark Johnston /* 20501787c3feSMark Johnston * Handle completion of a regular I/O request. 20511787c3feSMark Johnston */ 20521787c3feSMark Johnston g_mirror_regular_request(sc, bp); 2053855761d5SPawel Jakub Dawidek else if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) 20541787c3feSMark Johnston /* 20551787c3feSMark Johnston * Handle completion of the second half (the 20561787c3feSMark Johnston * write) of a block synchronization operation. 20571787c3feSMark Johnston */ 20581787c3feSMark Johnston g_mirror_sync_request(sc, bp); 2059de6f1c7cSPawel Jakub Dawidek else { 2060de6f1c7cSPawel Jakub Dawidek KASSERT(0, 20619a8fa125SWarner Losh ("Invalid request cflags=0x%hx to=%s.", 2062de6f1c7cSPawel Jakub Dawidek bp->bio_cflags, bp->bio_to->name)); 2063de6f1c7cSPawel Jakub Dawidek } 2064de6f1c7cSPawel Jakub Dawidek } else { 20651787c3feSMark Johnston /* 20661787c3feSMark Johnston * Initiate an I/O request. 20671787c3feSMark Johnston */ 20681787c3feSMark Johnston g_mirror_register_request(sc, bp); 2069de6f1c7cSPawel Jakub Dawidek } 2070f663832bSPawel Jakub Dawidek G_MIRROR_DEBUG(5, "%s: I'm here 9.", __func__); 2071fa4a1febSPawel Jakub Dawidek } 2072fa4a1febSPawel Jakub Dawidek } 2073fa4a1febSPawel Jakub Dawidek 2074fa4a1febSPawel Jakub Dawidek static void 2075fe6f94eaSPawel Jakub Dawidek g_mirror_update_idle(struct g_mirror_softc *sc, struct g_mirror_disk *disk) 2076fa4a1febSPawel Jakub Dawidek { 2077fa4a1febSPawel Jakub Dawidek 2078855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 2079855761d5SPawel Jakub Dawidek 2080501250baSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) 2081501250baSPawel Jakub Dawidek return; 2082fe6f94eaSPawel Jakub Dawidek if (!sc->sc_idle && (disk->d_flags & G_MIRROR_DISK_FLAG_DIRTY) == 0) { 20837d31c393SMark Johnston G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as dirty.", 2084855761d5SPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name); 2085fa4a1febSPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY; 2086fe6f94eaSPawel Jakub Dawidek } else if (sc->sc_idle && 2087fe6f94eaSPawel Jakub Dawidek (disk->d_flags & G_MIRROR_DISK_FLAG_DIRTY) != 0) { 20887d31c393SMark Johnston G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as clean.", 2089855761d5SPawel Jakub Dawidek g_mirror_get_diskname(disk), sc->sc_name); 2090fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 2091fa4a1febSPawel Jakub Dawidek } 2092fa4a1febSPawel Jakub Dawidek } 2093fa4a1febSPawel Jakub Dawidek 2094fa4a1febSPawel Jakub Dawidek static void 2095762f440fSMark Johnston g_mirror_sync_reinit(const struct g_mirror_disk *disk, struct bio *bp, 2096762f440fSMark Johnston off_t offset) 2097762f440fSMark Johnston { 2098762f440fSMark Johnston void *data; 2099762f440fSMark Johnston int idx; 2100762f440fSMark Johnston 2101762f440fSMark Johnston data = bp->bio_data; 2102762f440fSMark Johnston idx = (int)(uintptr_t)bp->bio_caller1; 2103762f440fSMark Johnston g_reset_bio(bp); 2104762f440fSMark Johnston 2105762f440fSMark Johnston bp->bio_cmd = BIO_READ; 2106762f440fSMark Johnston bp->bio_data = data; 2107762f440fSMark Johnston bp->bio_done = g_mirror_sync_done; 2108762f440fSMark Johnston bp->bio_from = disk->d_sync.ds_consumer; 2109762f440fSMark Johnston bp->bio_to = disk->d_softc->sc_provider; 2110762f440fSMark Johnston bp->bio_caller1 = (void *)(uintptr_t)idx; 2111762f440fSMark Johnston bp->bio_offset = offset; 2112cd853791SKonstantin Belousov bp->bio_length = MIN(maxphys, 2113762f440fSMark Johnston disk->d_softc->sc_mediasize - bp->bio_offset); 2114762f440fSMark Johnston } 2115762f440fSMark Johnston 2116762f440fSMark Johnston static void 2117fa4a1febSPawel Jakub Dawidek g_mirror_sync_start(struct g_mirror_disk *disk) 2118fa4a1febSPawel Jakub Dawidek { 2119fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 2120762f440fSMark Johnston struct g_mirror_disk_sync *sync; 2121855761d5SPawel Jakub Dawidek struct g_consumer *cp; 2122855761d5SPawel Jakub Dawidek struct bio *bp; 21230d81fba6SMateusz Guzik int error __diagused, i; 2124fa4a1febSPawel Jakub Dawidek 2125855761d5SPawel Jakub Dawidek g_topology_assert_not(); 2126fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 2127762f440fSMark Johnston sync = &disk->d_sync; 2128855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 2129855761d5SPawel Jakub Dawidek 2130855761d5SPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING, 2131855761d5SPawel Jakub Dawidek ("Disk %s is not marked for synchronization.", 2132855761d5SPawel Jakub Dawidek g_mirror_get_diskname(disk))); 2133fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 2134fa4a1febSPawel Jakub Dawidek ("Device not in RUNNING state (%s, %u).", sc->sc_name, 2135fa4a1febSPawel Jakub Dawidek sc->sc_state)); 2136fa4a1febSPawel Jakub Dawidek 2137855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 2138855761d5SPawel Jakub Dawidek g_topology_lock(); 2139855761d5SPawel Jakub Dawidek cp = g_new_consumer(sc->sc_sync.ds_geom); 214040ea77a0SAlexander Motin cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 2141855761d5SPawel Jakub Dawidek error = g_attach(cp, sc->sc_provider); 2142855761d5SPawel Jakub Dawidek KASSERT(error == 0, 2143855761d5SPawel Jakub Dawidek ("Cannot attach to %s (error=%d).", sc->sc_name, error)); 2144855761d5SPawel Jakub Dawidek error = g_access(cp, 1, 0, 0); 2145855761d5SPawel Jakub Dawidek KASSERT(error == 0, ("Cannot open %s (error=%d).", sc->sc_name, error)); 2146855761d5SPawel Jakub Dawidek g_topology_unlock(); 2147855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 2148855761d5SPawel Jakub Dawidek 2149fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: rebuilding provider %s.", sc->sc_name, 2150fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk)); 2151501250baSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) == 0) 2152fa4a1febSPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY; 2153762f440fSMark Johnston KASSERT(sync->ds_consumer == NULL, 2154fa4a1febSPawel Jakub Dawidek ("Sync consumer already exists (device=%s, disk=%s).", 2155fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk))); 2156855761d5SPawel Jakub Dawidek 2157762f440fSMark Johnston sync->ds_consumer = cp; 2158762f440fSMark Johnston sync->ds_consumer->private = disk; 2159762f440fSMark Johnston sync->ds_consumer->index = 0; 2160855761d5SPawel Jakub Dawidek 2161855761d5SPawel Jakub Dawidek /* 2162855761d5SPawel Jakub Dawidek * Allocate memory for synchronization bios and initialize them. 2163855761d5SPawel Jakub Dawidek */ 2164762f440fSMark Johnston sync->ds_bios = malloc(sizeof(struct bio *) * g_mirror_syncreqs, 2165855761d5SPawel Jakub Dawidek M_MIRROR, M_WAITOK); 2166855761d5SPawel Jakub Dawidek for (i = 0; i < g_mirror_syncreqs; i++) { 2167855761d5SPawel Jakub Dawidek bp = g_alloc_bio(); 2168762f440fSMark Johnston sync->ds_bios[i] = bp; 2169762f440fSMark Johnston 2170cd853791SKonstantin Belousov bp->bio_data = malloc(maxphys, M_MIRROR, M_WAITOK); 2171ef25813dSRuslan Ermilov bp->bio_caller1 = (void *)(uintptr_t)i; 2172762f440fSMark Johnston g_mirror_sync_reinit(disk, bp, sync->ds_offset); 2173762f440fSMark Johnston sync->ds_offset += bp->bio_length; 2174855761d5SPawel Jakub Dawidek } 2175855761d5SPawel Jakub Dawidek 2176855761d5SPawel Jakub Dawidek /* Increase the number of disks in SYNCHRONIZING state. */ 2177fa4a1febSPawel Jakub Dawidek sc->sc_sync.ds_ndisks++; 2178855761d5SPawel Jakub Dawidek /* Set the number of in-flight synchronization requests. */ 2179762f440fSMark Johnston sync->ds_inflight = g_mirror_syncreqs; 2180855761d5SPawel Jakub Dawidek 2181855761d5SPawel Jakub Dawidek /* 2182855761d5SPawel Jakub Dawidek * Fire off first synchronization requests. 2183855761d5SPawel Jakub Dawidek */ 2184855761d5SPawel Jakub Dawidek for (i = 0; i < g_mirror_syncreqs; i++) { 2185762f440fSMark Johnston bp = sync->ds_bios[i]; 2186855761d5SPawel Jakub Dawidek G_MIRROR_LOGREQ(3, bp, "Sending synchronization request."); 2187762f440fSMark Johnston sync->ds_consumer->index++; 2188855761d5SPawel Jakub Dawidek /* 2189855761d5SPawel Jakub Dawidek * Delay the request if it is colliding with a regular request. 2190855761d5SPawel Jakub Dawidek */ 2191855761d5SPawel Jakub Dawidek if (g_mirror_regular_collision(sc, bp)) 2192855761d5SPawel Jakub Dawidek g_mirror_sync_delay(sc, bp); 2193855761d5SPawel Jakub Dawidek else 2194762f440fSMark Johnston g_io_request(bp, sync->ds_consumer); 2195855761d5SPawel Jakub Dawidek } 2196fa4a1febSPawel Jakub Dawidek } 2197fa4a1febSPawel Jakub Dawidek 2198fa4a1febSPawel Jakub Dawidek /* 2199fa4a1febSPawel Jakub Dawidek * Stop synchronization process. 2200fa4a1febSPawel Jakub Dawidek * type: 0 - synchronization finished 2201fa4a1febSPawel Jakub Dawidek * 1 - synchronization stopped 2202fa4a1febSPawel Jakub Dawidek */ 2203fa4a1febSPawel Jakub Dawidek static void 2204fa4a1febSPawel Jakub Dawidek g_mirror_sync_stop(struct g_mirror_disk *disk, int type) 2205fa4a1febSPawel Jakub Dawidek { 2206855761d5SPawel Jakub Dawidek struct g_mirror_softc *sc; 2207855761d5SPawel Jakub Dawidek struct g_consumer *cp; 2208fa4a1febSPawel Jakub Dawidek 2209855761d5SPawel Jakub Dawidek g_topology_assert_not(); 2210855761d5SPawel Jakub Dawidek sc = disk->d_softc; 2211855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 2212855761d5SPawel Jakub Dawidek 2213fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING, 2214fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", g_mirror_get_diskname(disk), 2215fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2216fa4a1febSPawel Jakub Dawidek if (disk->d_sync.ds_consumer == NULL) 2217fa4a1febSPawel Jakub Dawidek return; 2218fa4a1febSPawel Jakub Dawidek 2219fa4a1febSPawel Jakub Dawidek if (type == 0) { 2220fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: rebuilding provider %s finished.", 2221855761d5SPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2222fa4a1febSPawel Jakub Dawidek } else /* if (type == 1) */ { 2223fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: rebuilding provider %s stopped.", 2224855761d5SPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2225fa4a1febSPawel Jakub Dawidek } 2226b450976dSMark Johnston g_mirror_regular_release(sc); 2227855761d5SPawel Jakub Dawidek free(disk->d_sync.ds_bios, M_MIRROR); 2228855761d5SPawel Jakub Dawidek disk->d_sync.ds_bios = NULL; 2229855761d5SPawel Jakub Dawidek cp = disk->d_sync.ds_consumer; 2230fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_consumer = NULL; 2231fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 2232855761d5SPawel Jakub Dawidek sc->sc_sync.ds_ndisks--; 2233855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); /* Avoid recursion on sc_lock. */ 2234855761d5SPawel Jakub Dawidek g_topology_lock(); 2235855761d5SPawel Jakub Dawidek g_mirror_kill_consumer(sc, cp); 2236855761d5SPawel Jakub Dawidek g_topology_unlock(); 2237855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 2238fa4a1febSPawel Jakub Dawidek } 2239fa4a1febSPawel Jakub Dawidek 2240fa4a1febSPawel Jakub Dawidek static void 2241fa4a1febSPawel Jakub Dawidek g_mirror_launch_provider(struct g_mirror_softc *sc) 2242fa4a1febSPawel Jakub Dawidek { 2243fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 22442084cbe9SScott Long struct g_provider *pp, *dp; 2245fa4a1febSPawel Jakub Dawidek 2246855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_LOCKED); 2247fa4a1febSPawel Jakub Dawidek 2248855761d5SPawel Jakub Dawidek g_topology_lock(); 2249fa4a1febSPawel Jakub Dawidek pp = g_new_providerf(sc->sc_geom, "mirror/%s", sc->sc_name); 225040ea77a0SAlexander Motin pp->flags |= G_PF_DIRECT_RECEIVE; 2251fa4a1febSPawel Jakub Dawidek pp->mediasize = sc->sc_mediasize; 2252fa4a1febSPawel Jakub Dawidek pp->sectorsize = sc->sc_sectorsize; 225392f60381SAlexander Motin pp->stripesize = 0; 225492f60381SAlexander Motin pp->stripeoffset = 0; 22552084cbe9SScott Long 22562084cbe9SScott Long /* Splitting of unmapped BIO's could work but isn't implemented now */ 2257f07b6947SScott Long if (sc->sc_balance != G_MIRROR_BALANCE_SPLIT) 22582084cbe9SScott Long pp->flags |= G_PF_ACCEPT_UNMAPPED; 22592084cbe9SScott Long 226092f60381SAlexander Motin LIST_FOREACH(disk, &sc->sc_disks, d_next) { 22612084cbe9SScott Long if (disk->d_consumer && disk->d_consumer->provider) { 22622084cbe9SScott Long dp = disk->d_consumer->provider; 22632084cbe9SScott Long if (dp->stripesize > pp->stripesize) { 22642084cbe9SScott Long pp->stripesize = dp->stripesize; 22652084cbe9SScott Long pp->stripeoffset = dp->stripeoffset; 22662084cbe9SScott Long } 22672084cbe9SScott Long /* A provider underneath us doesn't support unmapped */ 22682084cbe9SScott Long if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { 2269647a92d6SEd Schouten G_MIRROR_DEBUG(0, "Cancelling unmapped " 2270647a92d6SEd Schouten "because of %s.", dp->name); 22712084cbe9SScott Long pp->flags &= ~G_PF_ACCEPT_UNMAPPED; 22722084cbe9SScott Long } 227392f60381SAlexander Motin } 227492f60381SAlexander Motin } 2275dc399583SAlexander Motin pp->private = sc; 2276dc399583SAlexander Motin sc->sc_refcnt++; 2277fa4a1febSPawel Jakub Dawidek sc->sc_provider = pp; 2278fa4a1febSPawel Jakub Dawidek g_error_provider(pp, 0); 2279855761d5SPawel Jakub Dawidek g_topology_unlock(); 22800cca572eSJohn-Mark Gurney G_MIRROR_DEBUG(0, "Device %s launched (%u/%u).", pp->name, 22810cca572eSJohn-Mark Gurney g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE), sc->sc_ndisks); 2282fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2283fa4a1febSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) 2284fa4a1febSPawel Jakub Dawidek g_mirror_sync_start(disk); 2285fa4a1febSPawel Jakub Dawidek } 2286fa4a1febSPawel Jakub Dawidek } 2287fa4a1febSPawel Jakub Dawidek 2288fa4a1febSPawel Jakub Dawidek static void 2289fa4a1febSPawel Jakub Dawidek g_mirror_destroy_provider(struct g_mirror_softc *sc) 2290fa4a1febSPawel Jakub Dawidek { 2291fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 2292fa4a1febSPawel Jakub Dawidek struct bio *bp; 2293fa4a1febSPawel Jakub Dawidek 2294855761d5SPawel Jakub Dawidek g_topology_assert_not(); 2295fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_provider != NULL, ("NULL provider (device=%s).", 2296fa4a1febSPawel Jakub Dawidek sc->sc_name)); 2297fa4a1febSPawel Jakub Dawidek 2298a65d524aSMark Johnston LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2299a65d524aSMark Johnston if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) 2300a65d524aSMark Johnston g_mirror_sync_stop(disk, 1); 2301a65d524aSMark Johnston } 2302a65d524aSMark Johnston 2303855761d5SPawel Jakub Dawidek g_topology_lock(); 2304fa4a1febSPawel Jakub Dawidek g_error_provider(sc->sc_provider, ENXIO); 2305fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 23069abe2e7eSMark Johnston while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL) { 23079abe2e7eSMark Johnston TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue); 2308be20fc2eSMark Johnston /* 2309be20fc2eSMark Johnston * Abort any pending I/O that wasn't generated by us. 2310be20fc2eSMark Johnston * Synchronization requests and requests destined for individual 2311be20fc2eSMark Johnston * mirror components can be destroyed immediately. 2312be20fc2eSMark Johnston */ 2313be20fc2eSMark Johnston if (bp->bio_to == sc->sc_provider && 2314be20fc2eSMark Johnston bp->bio_from->geom != sc->sc_sync.ds_geom) { 2315fa4a1febSPawel Jakub Dawidek g_io_deliver(bp, ENXIO); 2316be20fc2eSMark Johnston } else { 2317be20fc2eSMark Johnston if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) 2318be20fc2eSMark Johnston free(bp->bio_data, M_MIRROR); 2319be20fc2eSMark Johnston g_destroy_bio(bp); 2320be20fc2eSMark Johnston } 2321be20fc2eSMark Johnston } 2322fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 23238b64f3caSAlexander Motin g_wither_provider(sc->sc_provider, ENXIO); 2324fa4a1febSPawel Jakub Dawidek sc->sc_provider = NULL; 23255a236b0eSAlexander Motin G_MIRROR_DEBUG(0, "Device %s: provider destroyed.", sc->sc_name); 23268b64f3caSAlexander Motin g_topology_unlock(); 2327fa4a1febSPawel Jakub Dawidek } 2328fa4a1febSPawel Jakub Dawidek 2329fa4a1febSPawel Jakub Dawidek static void 2330fa4a1febSPawel Jakub Dawidek g_mirror_go(void *arg) 2331fa4a1febSPawel Jakub Dawidek { 2332fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 23332f1cfb7fSMark Johnston struct g_mirror_event *ep; 2334fa4a1febSPawel Jakub Dawidek 2335fa4a1febSPawel Jakub Dawidek sc = arg; 2336fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Force device %s start due to timeout.", sc->sc_name); 23372f1cfb7fSMark Johnston ep = sc->sc_timeout_event; 23382f1cfb7fSMark Johnston sc->sc_timeout_event = NULL; 23392f1cfb7fSMark Johnston g_mirror_event_dispatch(ep, sc, 0, 2340fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT | G_MIRROR_EVENT_DEVICE); 2341fa4a1febSPawel Jakub Dawidek } 2342fa4a1febSPawel Jakub Dawidek 23432f1cfb7fSMark Johnston static void 23442f1cfb7fSMark Johnston g_mirror_timeout_drain(struct g_mirror_softc *sc) 23452f1cfb7fSMark Johnston { 23462f1cfb7fSMark Johnston sx_assert(&sc->sc_lock, SX_XLOCKED); 23472f1cfb7fSMark Johnston 23482f1cfb7fSMark Johnston callout_drain(&sc->sc_callout); 23492f1cfb7fSMark Johnston g_mirror_event_free(sc->sc_timeout_event); 23502f1cfb7fSMark Johnston sc->sc_timeout_event = NULL; 23512f1cfb7fSMark Johnston } 23522f1cfb7fSMark Johnston 2353fa4a1febSPawel Jakub Dawidek static u_int 2354fa4a1febSPawel Jakub Dawidek g_mirror_determine_state(struct g_mirror_disk *disk) 2355fa4a1febSPawel Jakub Dawidek { 2356fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 2357fa4a1febSPawel Jakub Dawidek u_int state; 2358fa4a1febSPawel Jakub Dawidek 2359fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 2360fa4a1febSPawel Jakub Dawidek if (sc->sc_syncid == disk->d_sync.ds_syncid) { 2361fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2362db7c5083SMark Johnston G_MIRROR_DISK_FLAG_SYNCHRONIZING) == 0 && 2363db7c5083SMark Johnston (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 0 || 2364db7c5083SMark Johnston (disk->d_flags & G_MIRROR_DISK_FLAG_DIRTY) == 0)) { 2365fa4a1febSPawel Jakub Dawidek /* Disk does not need synchronization. */ 2366fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_ACTIVE; 2367fa4a1febSPawel Jakub Dawidek } else { 2368fa4a1febSPawel Jakub Dawidek if ((sc->sc_flags & 2369fa4a1febSPawel Jakub Dawidek G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) == 0 || 2370fa4a1febSPawel Jakub Dawidek (disk->d_flags & 2371fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_FORCE_SYNC) != 0) { 2372fa4a1febSPawel Jakub Dawidek /* 2373fa4a1febSPawel Jakub Dawidek * We can start synchronization from 2374fa4a1febSPawel Jakub Dawidek * the stored offset. 2375fa4a1febSPawel Jakub Dawidek */ 2376fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_SYNCHRONIZING; 2377fa4a1febSPawel Jakub Dawidek } else { 2378fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_STALE; 2379fa4a1febSPawel Jakub Dawidek } 2380fa4a1febSPawel Jakub Dawidek } 2381fa4a1febSPawel Jakub Dawidek } else if (disk->d_sync.ds_syncid < sc->sc_syncid) { 2382fa4a1febSPawel Jakub Dawidek /* 2383fa4a1febSPawel Jakub Dawidek * Reset all synchronization data for this disk, 2384fa4a1febSPawel Jakub Dawidek * because if it even was synchronized, it was 2385fa4a1febSPawel Jakub Dawidek * synchronized to disks with different syncid. 2386fa4a1febSPawel Jakub Dawidek */ 2387fa4a1febSPawel Jakub Dawidek disk->d_flags |= G_MIRROR_DISK_FLAG_SYNCHRONIZING; 2388fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset = 0; 2389fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset_done = 0; 2390fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid = sc->sc_syncid; 2391fa4a1febSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) == 0 || 2392fa4a1febSPawel Jakub Dawidek (disk->d_flags & G_MIRROR_DISK_FLAG_FORCE_SYNC) != 0) { 2393fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_SYNCHRONIZING; 2394fa4a1febSPawel Jakub Dawidek } else { 2395fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_STALE; 2396fa4a1febSPawel Jakub Dawidek } 2397fa4a1febSPawel Jakub Dawidek } else /* if (sc->sc_syncid < disk->d_sync.ds_syncid) */ { 2398fa4a1febSPawel Jakub Dawidek /* 2399fa4a1febSPawel Jakub Dawidek * Not good, NOT GOOD! 2400fa4a1febSPawel Jakub Dawidek * It means that mirror was started on stale disks 2401fa4a1febSPawel Jakub Dawidek * and more fresh disk just arrive. 24023c57a41dSPawel Jakub Dawidek * If there were writes, mirror is broken, sorry. 2403fa4a1febSPawel Jakub Dawidek * I think the best choice here is don't touch 2404776fc0e9SYaroslav Tykhiy * this disk and inform the user loudly. 2405fa4a1febSPawel Jakub Dawidek */ 2406fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s was started before the freshest " 2407fa4a1febSPawel Jakub Dawidek "disk (%s) arrives!! It will not be connected to the " 2408fa4a1febSPawel Jakub Dawidek "running device.", sc->sc_name, 2409fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk)); 2410fa4a1febSPawel Jakub Dawidek g_mirror_destroy_disk(disk); 2411fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DISK_STATE_NONE; 2412fa4a1febSPawel Jakub Dawidek /* Return immediately, because disk was destroyed. */ 2413fa4a1febSPawel Jakub Dawidek return (state); 2414fa4a1febSPawel Jakub Dawidek } 2415fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(3, "State for %s disk: %s.", 2416fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), g_mirror_disk_state2str(state)); 2417fa4a1febSPawel Jakub Dawidek return (state); 2418fa4a1febSPawel Jakub Dawidek } 2419fa4a1febSPawel Jakub Dawidek 2420fa4a1febSPawel Jakub Dawidek /* 2421fa4a1febSPawel Jakub Dawidek * Update device state. 2422fa4a1febSPawel Jakub Dawidek */ 2423fa4a1febSPawel Jakub Dawidek static void 2424fff048e4SMark Johnston g_mirror_update_device(struct g_mirror_softc *sc, bool force) 2425fa4a1febSPawel Jakub Dawidek { 2426fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 2427fa4a1febSPawel Jakub Dawidek u_int state; 2428fa4a1febSPawel Jakub Dawidek 2429855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 2430fa4a1febSPawel Jakub Dawidek 2431fa4a1febSPawel Jakub Dawidek switch (sc->sc_state) { 2432fa4a1febSPawel Jakub Dawidek case G_MIRROR_DEVICE_STATE_STARTING: 2433fa4a1febSPawel Jakub Dawidek { 24349a9f5041SPawel Jakub Dawidek struct g_mirror_disk *pdisk, *tdisk; 2435af7dcae0SConrad Meyer const char *mismatch; 2436af7dcae0SConrad Meyer uintmax_t found, newest; 2437af7dcae0SConrad Meyer u_int dirty, ndisks; 2438af7dcae0SConrad Meyer 2439af7dcae0SConrad Meyer /* Pre-flight checks */ 2440af7dcae0SConrad Meyer LIST_FOREACH_SAFE(disk, &sc->sc_disks, d_next, tdisk) { 2441af7dcae0SConrad Meyer /* 2442af7dcae0SConrad Meyer * Confirm we already detected the newest genid. 2443af7dcae0SConrad Meyer */ 2444af7dcae0SConrad Meyer KASSERT(sc->sc_genid >= disk->d_genid, 2445af7dcae0SConrad Meyer ("%s: found newer genid %u (sc:%p had %u).", __func__, 2446af7dcae0SConrad Meyer disk->d_genid, sc, sc->sc_genid)); 2447af7dcae0SConrad Meyer 2448af7dcae0SConrad Meyer /* Kick out any previously tasted stale components. */ 2449af7dcae0SConrad Meyer if (disk->d_genid < sc->sc_genid) { 2450af7dcae0SConrad Meyer G_MIRROR_DEBUG(0, "Stale 'genid' field on %s " 2451af7dcae0SConrad Meyer "(device %s) (component=%u latest=%u), skipping.", 2452af7dcae0SConrad Meyer g_mirror_get_diskname(disk), sc->sc_name, 2453af7dcae0SConrad Meyer disk->d_genid, sc->sc_genid); 2454af7dcae0SConrad Meyer g_mirror_destroy_disk(disk); 2455af7dcae0SConrad Meyer sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 2456af7dcae0SConrad Meyer continue; 2457af7dcae0SConrad Meyer } 2458af7dcae0SConrad Meyer 2459af7dcae0SConrad Meyer /* 2460af7dcae0SConrad Meyer * Confirm we already detected the newest syncid. 2461af7dcae0SConrad Meyer */ 2462af7dcae0SConrad Meyer KASSERT(sc->sc_syncid >= disk->d_sync.ds_syncid, 2463af7dcae0SConrad Meyer ("%s: found newer syncid %u (sc:%p had %u).", 2464af7dcae0SConrad Meyer __func__, disk->d_sync.ds_syncid, sc, 2465af7dcae0SConrad Meyer sc->sc_syncid)); 2466af7dcae0SConrad Meyer 2467af7dcae0SConrad Meyer #define DETECT_MISMATCH(field, name) \ 2468af7dcae0SConrad Meyer if (mismatch == NULL && \ 2469af7dcae0SConrad Meyer disk->d_init_ ## field != sc->sc_ ## field) { \ 2470af7dcae0SConrad Meyer mismatch = name; \ 2471af7dcae0SConrad Meyer found = (intmax_t)disk->d_init_ ## field; \ 2472af7dcae0SConrad Meyer newest = (intmax_t)sc->sc_ ## field; \ 2473af7dcae0SConrad Meyer } 2474af7dcae0SConrad Meyer mismatch = NULL; 2475af7dcae0SConrad Meyer DETECT_MISMATCH(ndisks, "md_all"); 2476af7dcae0SConrad Meyer DETECT_MISMATCH(balance, "md_balance"); 2477af7dcae0SConrad Meyer DETECT_MISMATCH(slice, "md_slice"); 2478af7dcae0SConrad Meyer DETECT_MISMATCH(mediasize, "md_mediasize"); 2479af7dcae0SConrad Meyer #undef DETECT_MISMATCH 2480af7dcae0SConrad Meyer if (mismatch != NULL) { 2481af7dcae0SConrad Meyer G_MIRROR_DEBUG(0, "Found a mismatching '%s' " 2482af7dcae0SConrad Meyer "field on %s (device %s) (found=%ju " 2483af7dcae0SConrad Meyer "newest=%ju).", mismatch, 2484af7dcae0SConrad Meyer g_mirror_get_diskname(disk), sc->sc_name, 2485af7dcae0SConrad Meyer found, newest); 2486af7dcae0SConrad Meyer g_mirror_destroy_disk(disk); 2487af7dcae0SConrad Meyer sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 2488af7dcae0SConrad Meyer continue; 2489af7dcae0SConrad Meyer } 2490af7dcae0SConrad Meyer } 2491fa4a1febSPawel Jakub Dawidek 2492fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_provider == NULL, 2493fa4a1febSPawel Jakub Dawidek ("Non-NULL provider in STARTING state (%s).", sc->sc_name)); 2494fa4a1febSPawel Jakub Dawidek /* 2495af7dcae0SConrad Meyer * Are we ready? If the timeout (force is true) has expired, and 2496af7dcae0SConrad Meyer * any disks are present, then yes. If we're permitted to launch 2497af7dcae0SConrad Meyer * before the timeout has expired and the expected number of 2498af7dcae0SConrad Meyer * current-generation mirror disks have been tasted, then yes. 2499fa4a1febSPawel Jakub Dawidek */ 2500855761d5SPawel Jakub Dawidek ndisks = g_mirror_ndisks(sc, -1); 2501af7dcae0SConrad Meyer if ((force && ndisks > 0) || 2502af7dcae0SConrad Meyer (g_launch_mirror_before_timeout && ndisks == sc->sc_ndisks)) { 2503fa4a1febSPawel Jakub Dawidek ; 2504855761d5SPawel Jakub Dawidek } else if (ndisks == 0) { 2505fa4a1febSPawel Jakub Dawidek /* 2506fa4a1febSPawel Jakub Dawidek * Disks went down in starting phase, so destroy 2507fa4a1febSPawel Jakub Dawidek * device. 2508fa4a1febSPawel Jakub Dawidek */ 25092f1cfb7fSMark Johnston g_mirror_timeout_drain(sc); 2510fa4a1febSPawel Jakub Dawidek sc->sc_flags |= G_MIRROR_DEVICE_FLAG_DESTROY; 25110f2bbe5bSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "root_mount_rel[%u] %p", __LINE__, 25120f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount); 25130f2bbe5bSPawel Jakub Dawidek root_mount_rel(sc->sc_rootmount); 25140f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount = NULL; 2515fa4a1febSPawel Jakub Dawidek return; 2516fa4a1febSPawel Jakub Dawidek } else { 2517fa4a1febSPawel Jakub Dawidek return; 2518fa4a1febSPawel Jakub Dawidek } 2519fa4a1febSPawel Jakub Dawidek 2520fa4a1febSPawel Jakub Dawidek /* 2521fa4a1febSPawel Jakub Dawidek * Activate all disks with the biggest syncid. 2522fa4a1febSPawel Jakub Dawidek */ 2523fa4a1febSPawel Jakub Dawidek if (force) { 2524fa4a1febSPawel Jakub Dawidek /* 25257d01f182SPawel Jakub Dawidek * If 'force' is true, we have been called due to 25267d01f182SPawel Jakub Dawidek * timeout, so don't bother canceling timeout. 2527fa4a1febSPawel Jakub Dawidek */ 2528fa4a1febSPawel Jakub Dawidek ndisks = 0; 2529fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2530fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2531fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_SYNCHRONIZING) == 0) { 2532fa4a1febSPawel Jakub Dawidek ndisks++; 2533fa4a1febSPawel Jakub Dawidek } 2534fa4a1febSPawel Jakub Dawidek } 2535fa4a1febSPawel Jakub Dawidek if (ndisks == 0) { 2536959521ffSPawel Jakub Dawidek /* No valid disks found, destroy device. */ 2537959521ffSPawel Jakub Dawidek sc->sc_flags |= G_MIRROR_DEVICE_FLAG_DESTROY; 25380f2bbe5bSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "root_mount_rel[%u] %p", 25390f2bbe5bSPawel Jakub Dawidek __LINE__, sc->sc_rootmount); 25400f2bbe5bSPawel Jakub Dawidek root_mount_rel(sc->sc_rootmount); 25410f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount = NULL; 2542fa4a1febSPawel Jakub Dawidek return; 2543fa4a1febSPawel Jakub Dawidek } 2544fa4a1febSPawel Jakub Dawidek } else { 2545fa4a1febSPawel Jakub Dawidek /* Cancel timeout. */ 25462f1cfb7fSMark Johnston g_mirror_timeout_drain(sc); 2547fa4a1febSPawel Jakub Dawidek } 2548fa4a1febSPawel Jakub Dawidek 2549fa4a1febSPawel Jakub Dawidek /* 2550fa4a1febSPawel Jakub Dawidek * Here we need to look for dirty disks and if all disks 2551fa4a1febSPawel Jakub Dawidek * with the biggest syncid are dirty, we have to choose 2552fa4a1febSPawel Jakub Dawidek * one with the biggest priority and rebuild the rest. 2553fa4a1febSPawel Jakub Dawidek */ 2554fa4a1febSPawel Jakub Dawidek /* 2555fa4a1febSPawel Jakub Dawidek * Find the number of dirty disks with the biggest syncid. 2556fa4a1febSPawel Jakub Dawidek * Find the number of disks with the biggest syncid. 2557fa4a1febSPawel Jakub Dawidek * While here, find a disk with the biggest priority. 2558fa4a1febSPawel Jakub Dawidek */ 2559fa4a1febSPawel Jakub Dawidek dirty = ndisks = 0; 2560fa4a1febSPawel Jakub Dawidek pdisk = NULL; 2561fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2562af7dcae0SConrad Meyer if (disk->d_sync.ds_syncid != sc->sc_syncid) 2563fa4a1febSPawel Jakub Dawidek continue; 2564fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2565fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_SYNCHRONIZING) != 0) { 2566fa4a1febSPawel Jakub Dawidek continue; 2567fa4a1febSPawel Jakub Dawidek } 2568fa4a1febSPawel Jakub Dawidek ndisks++; 2569fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & G_MIRROR_DISK_FLAG_DIRTY) != 0) { 2570fa4a1febSPawel Jakub Dawidek dirty++; 2571fa4a1febSPawel Jakub Dawidek if (pdisk == NULL || 2572fa4a1febSPawel Jakub Dawidek pdisk->d_priority < disk->d_priority) { 2573fa4a1febSPawel Jakub Dawidek pdisk = disk; 2574fa4a1febSPawel Jakub Dawidek } 2575fa4a1febSPawel Jakub Dawidek } 2576fa4a1febSPawel Jakub Dawidek } 2577fa4a1febSPawel Jakub Dawidek if (dirty == 0) { 2578fa4a1febSPawel Jakub Dawidek /* No dirty disks at all, great. */ 2579fa4a1febSPawel Jakub Dawidek } else if (dirty == ndisks) { 2580fa4a1febSPawel Jakub Dawidek /* 2581fa4a1febSPawel Jakub Dawidek * Force synchronization for all dirty disks except one 2582fa4a1febSPawel Jakub Dawidek * with the biggest priority. 2583fa4a1febSPawel Jakub Dawidek */ 2584fa4a1febSPawel Jakub Dawidek KASSERT(pdisk != NULL, ("pdisk == NULL")); 2585fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Using disk %s (device %s) as a " 2586fa4a1febSPawel Jakub Dawidek "master disk for synchronization.", 2587fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(pdisk), sc->sc_name); 2588fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2589af7dcae0SConrad Meyer if (disk->d_sync.ds_syncid != sc->sc_syncid) 2590fa4a1febSPawel Jakub Dawidek continue; 2591fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2592fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_SYNCHRONIZING) != 0) { 2593fa4a1febSPawel Jakub Dawidek continue; 2594fa4a1febSPawel Jakub Dawidek } 2595fa4a1febSPawel Jakub Dawidek KASSERT((disk->d_flags & 2596fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_DIRTY) != 0, 2597fa4a1febSPawel Jakub Dawidek ("Disk %s isn't marked as dirty.", 2598fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk))); 2599fa4a1febSPawel Jakub Dawidek /* Skip the disk with the biggest priority. */ 2600fa4a1febSPawel Jakub Dawidek if (disk == pdisk) 2601fa4a1febSPawel Jakub Dawidek continue; 2602fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid = 0; 2603fa4a1febSPawel Jakub Dawidek } 2604fa4a1febSPawel Jakub Dawidek } else if (dirty < ndisks) { 2605fa4a1febSPawel Jakub Dawidek /* 2606fa4a1febSPawel Jakub Dawidek * Force synchronization for all dirty disks. 2607fa4a1febSPawel Jakub Dawidek * We have some non-dirty disks. 2608fa4a1febSPawel Jakub Dawidek */ 2609fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2610af7dcae0SConrad Meyer if (disk->d_sync.ds_syncid != sc->sc_syncid) 2611fa4a1febSPawel Jakub Dawidek continue; 2612fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2613fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_SYNCHRONIZING) != 0) { 2614fa4a1febSPawel Jakub Dawidek continue; 2615fa4a1febSPawel Jakub Dawidek } 2616fa4a1febSPawel Jakub Dawidek if ((disk->d_flags & 2617fa4a1febSPawel Jakub Dawidek G_MIRROR_DISK_FLAG_DIRTY) == 0) { 2618fa4a1febSPawel Jakub Dawidek continue; 2619fa4a1febSPawel Jakub Dawidek } 2620fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid = 0; 2621fa4a1febSPawel Jakub Dawidek } 2622fa4a1febSPawel Jakub Dawidek } 2623fa4a1febSPawel Jakub Dawidek 2624fa4a1febSPawel Jakub Dawidek /* Reset hint. */ 2625fa4a1febSPawel Jakub Dawidek sc->sc_hint = NULL; 2626af7dcae0SConrad Meyer if (force) { 2627fa4a1febSPawel Jakub Dawidek /* Remember to bump syncid on first write. */ 2628da844167SPawel Jakub Dawidek sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 2629fa4a1febSPawel Jakub Dawidek } 2630fa4a1febSPawel Jakub Dawidek state = G_MIRROR_DEVICE_STATE_RUNNING; 2631fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Device %s state changed from %s to %s.", 2632fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_device_state2str(sc->sc_state), 2633fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(state)); 2634fa4a1febSPawel Jakub Dawidek sc->sc_state = state; 2635fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2636fa4a1febSPawel Jakub Dawidek state = g_mirror_determine_state(disk); 2637fa4a1febSPawel Jakub Dawidek g_mirror_event_send(disk, state, 2638fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_DONTWAIT); 26399a9f5041SPawel Jakub Dawidek if (state == G_MIRROR_DISK_STATE_STALE) 2640da844167SPawel Jakub Dawidek sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 2641fa4a1febSPawel Jakub Dawidek } 2642fa4a1febSPawel Jakub Dawidek break; 2643fa4a1febSPawel Jakub Dawidek } 2644fa4a1febSPawel Jakub Dawidek case G_MIRROR_DEVICE_STATE_RUNNING: 26452fdf5be1SPawel Jakub Dawidek if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 0 && 2646fa4a1febSPawel Jakub Dawidek g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_NEW) == 0) { 2647fa4a1febSPawel Jakub Dawidek /* 2648c1ab409cSMark Johnston * No usable disks, so destroy the device. 2649fa4a1febSPawel Jakub Dawidek */ 26502fdf5be1SPawel Jakub Dawidek sc->sc_flags |= G_MIRROR_DEVICE_FLAG_DESTROY; 2651f1ad62a4SPawel Jakub Dawidek break; 2652fa4a1febSPawel Jakub Dawidek } else if (g_mirror_ndisks(sc, 26534084e6aaSPawel Jakub Dawidek G_MIRROR_DISK_STATE_ACTIVE) > 0 && 26544084e6aaSPawel Jakub Dawidek g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_NEW) == 0) { 2655fa4a1febSPawel Jakub Dawidek /* 2656fa4a1febSPawel Jakub Dawidek * We have active disks, launch provider if it doesn't 2657fa4a1febSPawel Jakub Dawidek * exist. 2658fa4a1febSPawel Jakub Dawidek */ 2659fa4a1febSPawel Jakub Dawidek if (sc->sc_provider == NULL) 2660fa4a1febSPawel Jakub Dawidek g_mirror_launch_provider(sc); 26610f2bbe5bSPawel Jakub Dawidek if (sc->sc_rootmount != NULL) { 266259ddf345SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "root_mount_rel[%u] %p", 266359ddf345SPawel Jakub Dawidek __LINE__, sc->sc_rootmount); 26640f2bbe5bSPawel Jakub Dawidek root_mount_rel(sc->sc_rootmount); 26650f2bbe5bSPawel Jakub Dawidek sc->sc_rootmount = NULL; 26660f2bbe5bSPawel Jakub Dawidek } 266759ddf345SPawel Jakub Dawidek } 2668f1ad62a4SPawel Jakub Dawidek /* 2669da844167SPawel Jakub Dawidek * Genid should be bumped immediately, so do it here. 2670f1ad62a4SPawel Jakub Dawidek */ 2671da844167SPawel Jakub Dawidek if ((sc->sc_bump_id & G_MIRROR_BUMP_GENID) != 0) { 26729a9f5041SPawel Jakub Dawidek sc->sc_bump_id &= ~G_MIRROR_BUMP_GENID; 26739a9f5041SPawel Jakub Dawidek g_mirror_bump_genid(sc); 26749a9f5041SPawel Jakub Dawidek } 26757103ac8aSAndriy Gapon if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID_NOW) != 0) { 26767103ac8aSAndriy Gapon sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID_NOW; 26777103ac8aSAndriy Gapon g_mirror_bump_syncid(sc); 26787103ac8aSAndriy Gapon } 2679fa4a1febSPawel Jakub Dawidek break; 2680fa4a1febSPawel Jakub Dawidek default: 2681fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("Wrong device state (%s, %s).", 2682fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_device_state2str(sc->sc_state))); 2683fa4a1febSPawel Jakub Dawidek break; 2684fa4a1febSPawel Jakub Dawidek } 2685fa4a1febSPawel Jakub Dawidek } 2686fa4a1febSPawel Jakub Dawidek 2687fa4a1febSPawel Jakub Dawidek /* 2688fa4a1febSPawel Jakub Dawidek * Update disk state and device state if needed. 2689fa4a1febSPawel Jakub Dawidek */ 2690fa4a1febSPawel Jakub Dawidek #define DISK_STATE_CHANGED() G_MIRROR_DEBUG(1, \ 2691fa4a1febSPawel Jakub Dawidek "Disk %s state changed from %s to %s (device %s).", \ 2692fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), \ 2693fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state), \ 2694fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(state), sc->sc_name) 2695fa4a1febSPawel Jakub Dawidek static int 26969eec299fSPawel Jakub Dawidek g_mirror_update_disk(struct g_mirror_disk *disk, u_int state) 2697fa4a1febSPawel Jakub Dawidek { 2698fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 2699fa4a1febSPawel Jakub Dawidek 2700fa4a1febSPawel Jakub Dawidek sc = disk->d_softc; 2701855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 2702855761d5SPawel Jakub Dawidek 2703fa4a1febSPawel Jakub Dawidek again: 2704fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(3, "Changing disk %s state from %s to %s.", 2705fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), g_mirror_disk_state2str(disk->d_state), 2706fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(state)); 2707fa4a1febSPawel Jakub Dawidek switch (state) { 2708fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_NEW: 2709fa4a1febSPawel Jakub Dawidek /* 2710fa4a1febSPawel Jakub Dawidek * Possible scenarios: 2711fa4a1febSPawel Jakub Dawidek * 1. New disk arrive. 2712fa4a1febSPawel Jakub Dawidek */ 2713fa4a1febSPawel Jakub Dawidek /* Previous state should be NONE. */ 2714fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_NONE, 2715fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", g_mirror_get_diskname(disk), 2716fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2717fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2718fa4a1febSPawel Jakub Dawidek 2719fa4a1febSPawel Jakub Dawidek disk->d_state = state; 272040e80522SMark Johnston g_topology_lock(); 2721fe7c3780SPawel Jakub Dawidek if (LIST_EMPTY(&sc->sc_disks)) 2722fa4a1febSPawel Jakub Dawidek LIST_INSERT_HEAD(&sc->sc_disks, disk, d_next); 2723fe7c3780SPawel Jakub Dawidek else { 2724fe7c3780SPawel Jakub Dawidek struct g_mirror_disk *dp; 2725fe7c3780SPawel Jakub Dawidek 2726fe7c3780SPawel Jakub Dawidek LIST_FOREACH(dp, &sc->sc_disks, d_next) { 2727fe7c3780SPawel Jakub Dawidek if (disk->d_priority >= dp->d_priority) { 2728fe7c3780SPawel Jakub Dawidek LIST_INSERT_BEFORE(dp, disk, d_next); 2729fe7c3780SPawel Jakub Dawidek dp = NULL; 2730fe7c3780SPawel Jakub Dawidek break; 2731fe7c3780SPawel Jakub Dawidek } 2732fe7c3780SPawel Jakub Dawidek if (LIST_NEXT(dp, d_next) == NULL) 2733fe7c3780SPawel Jakub Dawidek break; 2734fe7c3780SPawel Jakub Dawidek } 2735fe7c3780SPawel Jakub Dawidek if (dp != NULL) 2736fe7c3780SPawel Jakub Dawidek LIST_INSERT_AFTER(dp, disk, d_next); 2737fe7c3780SPawel Jakub Dawidek } 273840e80522SMark Johnston g_topology_unlock(); 27390cca572eSJohn-Mark Gurney G_MIRROR_DEBUG(1, "Device %s: provider %s detected.", 2740fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2741fa4a1febSPawel Jakub Dawidek if (sc->sc_state == G_MIRROR_DEVICE_STATE_STARTING) 2742fa4a1febSPawel Jakub Dawidek break; 2743fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 2744fa4a1febSPawel Jakub Dawidek ("Wrong device state (%s, %s, %s, %s).", sc->sc_name, 2745fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2746fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2747fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2748fa4a1febSPawel Jakub Dawidek state = g_mirror_determine_state(disk); 2749fa4a1febSPawel Jakub Dawidek if (state != G_MIRROR_DISK_STATE_NONE) 2750fa4a1febSPawel Jakub Dawidek goto again; 2751fa4a1febSPawel Jakub Dawidek break; 2752fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_ACTIVE: 2753fa4a1febSPawel Jakub Dawidek /* 2754fa4a1febSPawel Jakub Dawidek * Possible scenarios: 2755fa4a1febSPawel Jakub Dawidek * 1. New disk does not need synchronization. 2756fa4a1febSPawel Jakub Dawidek * 2. Synchronization process finished successfully. 2757fa4a1febSPawel Jakub Dawidek */ 2758fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 2759fa4a1febSPawel Jakub Dawidek ("Wrong device state (%s, %s, %s, %s).", sc->sc_name, 2760fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2761fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2762fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2763fa4a1febSPawel Jakub Dawidek /* Previous state should be NEW or SYNCHRONIZING. */ 2764fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_NEW || 2765fa4a1febSPawel Jakub Dawidek disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING, 2766fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", g_mirror_get_diskname(disk), 2767fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2768fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2769fa4a1febSPawel Jakub Dawidek 2770bf31327cSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) { 2771fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_SYNCHRONIZING; 2772fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; 2773fa4a1febSPawel Jakub Dawidek g_mirror_sync_stop(disk, 0); 2774fa4a1febSPawel Jakub Dawidek } 2775fa4a1febSPawel Jakub Dawidek disk->d_state = state; 2776fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset = 0; 2777fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_offset_done = 0; 2778fe6f94eaSPawel Jakub Dawidek g_mirror_update_idle(sc, disk); 2779bf31327cSPawel Jakub Dawidek g_mirror_update_metadata(disk); 27800cca572eSJohn-Mark Gurney G_MIRROR_DEBUG(1, "Device %s: provider %s activated.", 2781fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2782fa4a1febSPawel Jakub Dawidek break; 2783fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_STALE: 2784fa4a1febSPawel Jakub Dawidek /* 2785fa4a1febSPawel Jakub Dawidek * Possible scenarios: 2786fa4a1febSPawel Jakub Dawidek * 1. Stale disk was connected. 2787fa4a1febSPawel Jakub Dawidek */ 2788fa4a1febSPawel Jakub Dawidek /* Previous state should be NEW. */ 2789fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_NEW, 2790fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", g_mirror_get_diskname(disk), 2791fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2792fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 2793fa4a1febSPawel Jakub Dawidek ("Wrong device state (%s, %s, %s, %s).", sc->sc_name, 2794fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2795fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2796fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2797fa4a1febSPawel Jakub Dawidek /* 2798fa4a1febSPawel Jakub Dawidek * STALE state is only possible if device is marked 2799fa4a1febSPawel Jakub Dawidek * NOAUTOSYNC. 2800fa4a1febSPawel Jakub Dawidek */ 2801fa4a1febSPawel Jakub Dawidek KASSERT((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0, 2802fa4a1febSPawel Jakub Dawidek ("Wrong device state (%s, %s, %s, %s).", sc->sc_name, 2803fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2804fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2805fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2806fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2807fa4a1febSPawel Jakub Dawidek 2808fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 2809fa4a1febSPawel Jakub Dawidek disk->d_state = state; 2810fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(disk); 2811fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: provider %s is stale.", 2812fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2813fa4a1febSPawel Jakub Dawidek break; 2814fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_SYNCHRONIZING: 2815fa4a1febSPawel Jakub Dawidek /* 2816fa4a1febSPawel Jakub Dawidek * Possible scenarios: 2817fa4a1febSPawel Jakub Dawidek * 1. Disk which needs synchronization was connected. 2818fa4a1febSPawel Jakub Dawidek */ 2819fa4a1febSPawel Jakub Dawidek /* Previous state should be NEW. */ 2820fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_NEW, 2821fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", g_mirror_get_diskname(disk), 2822fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2823fa4a1febSPawel Jakub Dawidek KASSERT(sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, 2824fa4a1febSPawel Jakub Dawidek ("Wrong device state (%s, %s, %s, %s).", sc->sc_name, 2825fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2826fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2827fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2828fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2829fa4a1febSPawel Jakub Dawidek 2830fa4a1febSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_NEW) 2831fa4a1febSPawel Jakub Dawidek disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 2832fa4a1febSPawel Jakub Dawidek disk->d_state = state; 2833fa4a1febSPawel Jakub Dawidek if (sc->sc_provider != NULL) { 2834fa4a1febSPawel Jakub Dawidek g_mirror_sync_start(disk); 2835fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(disk); 2836fa4a1febSPawel Jakub Dawidek } 2837fa4a1febSPawel Jakub Dawidek break; 2838fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_DISCONNECTED: 2839fa4a1febSPawel Jakub Dawidek /* 2840fa4a1febSPawel Jakub Dawidek * Possible scenarios: 2841fa4a1febSPawel Jakub Dawidek * 1. Device wasn't running yet, but disk disappear. 2842fa4a1febSPawel Jakub Dawidek * 2. Disk was active and disapppear. 2843fa4a1febSPawel Jakub Dawidek * 3. Disk disappear during synchronization process. 2844fa4a1febSPawel Jakub Dawidek */ 2845fa4a1febSPawel Jakub Dawidek if (sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING) { 2846fa4a1febSPawel Jakub Dawidek /* 2847fa4a1febSPawel Jakub Dawidek * Previous state should be ACTIVE, STALE or 2848fa4a1febSPawel Jakub Dawidek * SYNCHRONIZING. 2849fa4a1febSPawel Jakub Dawidek */ 2850fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_ACTIVE || 2851fa4a1febSPawel Jakub Dawidek disk->d_state == G_MIRROR_DISK_STATE_STALE || 2852fa4a1febSPawel Jakub Dawidek disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING, 2853fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", 2854fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2855fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2856fa4a1febSPawel Jakub Dawidek } else if (sc->sc_state == G_MIRROR_DEVICE_STATE_STARTING) { 2857fa4a1febSPawel Jakub Dawidek /* Previous state should be NEW. */ 2858fa4a1febSPawel Jakub Dawidek KASSERT(disk->d_state == G_MIRROR_DISK_STATE_NEW, 2859fa4a1febSPawel Jakub Dawidek ("Wrong disk state (%s, %s).", 2860fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2861fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2862fa4a1febSPawel Jakub Dawidek /* 2863fa4a1febSPawel Jakub Dawidek * Reset bumping syncid if disk disappeared in STARTING 2864fa4a1febSPawel Jakub Dawidek * state. 2865fa4a1febSPawel Jakub Dawidek */ 2866da844167SPawel Jakub Dawidek if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID) != 0) 28679a9f5041SPawel Jakub Dawidek sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID; 2868fa4a1febSPawel Jakub Dawidek #ifdef INVARIANTS 2869fa4a1febSPawel Jakub Dawidek } else { 2870fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("Wrong device state (%s, %s, %s, %s).", 2871fa4a1febSPawel Jakub Dawidek sc->sc_name, 2872fa4a1febSPawel Jakub Dawidek g_mirror_device_state2str(sc->sc_state), 2873fa4a1febSPawel Jakub Dawidek g_mirror_get_diskname(disk), 2874fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state))); 2875fa4a1febSPawel Jakub Dawidek #endif 2876fa4a1febSPawel Jakub Dawidek } 2877fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2878fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: provider %s disconnected.", 2879fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2880fa4a1febSPawel Jakub Dawidek 2881fa4a1febSPawel Jakub Dawidek g_mirror_destroy_disk(disk); 2882fa4a1febSPawel Jakub Dawidek break; 2883fa4a1febSPawel Jakub Dawidek case G_MIRROR_DISK_STATE_DESTROY: 2884fa4a1febSPawel Jakub Dawidek { 2885fa4a1febSPawel Jakub Dawidek int error; 2886fa4a1febSPawel Jakub Dawidek 2887fa4a1febSPawel Jakub Dawidek error = g_mirror_clear_metadata(disk); 28884bfb5853SMark Johnston if (error != 0) { 28894bfb5853SMark Johnston G_MIRROR_DEBUG(0, 28904bfb5853SMark Johnston "Device %s: failed to clear metadata on %s: %d.", 28914bfb5853SMark Johnston sc->sc_name, g_mirror_get_diskname(disk), error); 28924bfb5853SMark Johnston break; 28934bfb5853SMark Johnston } 2894fa4a1febSPawel Jakub Dawidek DISK_STATE_CHANGED(); 2895fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s: provider %s destroyed.", 2896fa4a1febSPawel Jakub Dawidek sc->sc_name, g_mirror_get_diskname(disk)); 2897fa4a1febSPawel Jakub Dawidek 2898fa4a1febSPawel Jakub Dawidek g_mirror_destroy_disk(disk); 2899fa4a1febSPawel Jakub Dawidek sc->sc_ndisks--; 2900fa4a1febSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 2901fa4a1febSPawel Jakub Dawidek g_mirror_update_metadata(disk); 2902fa4a1febSPawel Jakub Dawidek } 2903fa4a1febSPawel Jakub Dawidek break; 2904fa4a1febSPawel Jakub Dawidek } 2905fa4a1febSPawel Jakub Dawidek default: 2906fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("Unknown state (%u).", state)); 2907fa4a1febSPawel Jakub Dawidek break; 2908fa4a1febSPawel Jakub Dawidek } 2909fa4a1febSPawel Jakub Dawidek return (0); 2910fa4a1febSPawel Jakub Dawidek } 2911fa4a1febSPawel Jakub Dawidek #undef DISK_STATE_CHANGED 2912fa4a1febSPawel Jakub Dawidek 2913fd6d3120SPawel Jakub Dawidek int 2914fa4a1febSPawel Jakub Dawidek g_mirror_read_metadata(struct g_consumer *cp, struct g_mirror_metadata *md) 2915fa4a1febSPawel Jakub Dawidek { 2916fa4a1febSPawel Jakub Dawidek struct g_provider *pp; 2917fa4a1febSPawel Jakub Dawidek u_char *buf; 2918fa4a1febSPawel Jakub Dawidek int error; 2919fa4a1febSPawel Jakub Dawidek 2920fa4a1febSPawel Jakub Dawidek g_topology_assert(); 2921fa4a1febSPawel Jakub Dawidek 2922fa4a1febSPawel Jakub Dawidek error = g_access(cp, 1, 0, 0); 2923fa4a1febSPawel Jakub Dawidek if (error != 0) 2924fa4a1febSPawel Jakub Dawidek return (error); 2925fa4a1febSPawel Jakub Dawidek pp = cp->provider; 2926fa4a1febSPawel Jakub Dawidek g_topology_unlock(); 2927fa4a1febSPawel Jakub Dawidek /* Metadata are stored on last sector. */ 2928fa4a1febSPawel Jakub Dawidek buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 2929fa4a1febSPawel Jakub Dawidek &error); 2930fa4a1febSPawel Jakub Dawidek g_topology_lock(); 2931fa4a1febSPawel Jakub Dawidek g_access(cp, -1, 0, 0); 29328a4a44b5SMaxim Sobolev if (buf == NULL) { 29339a9f5041SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Cannot read metadata from %s (error=%d).", 29349a9f5041SPawel Jakub Dawidek cp->provider->name, error); 2935fa4a1febSPawel Jakub Dawidek return (error); 2936fa4a1febSPawel Jakub Dawidek } 2937fa4a1febSPawel Jakub Dawidek 2938fa4a1febSPawel Jakub Dawidek /* Decode metadata. */ 2939fa4a1febSPawel Jakub Dawidek error = mirror_metadata_decode(buf, md); 2940fa4a1febSPawel Jakub Dawidek g_free(buf); 2941fa4a1febSPawel Jakub Dawidek if (strcmp(md->md_magic, G_MIRROR_MAGIC) != 0) 2942fa4a1febSPawel Jakub Dawidek return (EINVAL); 29439a9f5041SPawel Jakub Dawidek if (md->md_version > G_MIRROR_VERSION) { 29449a9f5041SPawel Jakub Dawidek G_MIRROR_DEBUG(0, 29459a9f5041SPawel Jakub Dawidek "Kernel module is too old to handle metadata from %s.", 29469a9f5041SPawel Jakub Dawidek cp->provider->name); 29479a9f5041SPawel Jakub Dawidek return (EINVAL); 29489a9f5041SPawel Jakub Dawidek } 2949fa4a1febSPawel Jakub Dawidek if (error != 0) { 2950fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "MD5 metadata hash mismatch for provider %s.", 2951fa4a1febSPawel Jakub Dawidek cp->provider->name); 2952fa4a1febSPawel Jakub Dawidek return (error); 2953fa4a1febSPawel Jakub Dawidek } 2954fa4a1febSPawel Jakub Dawidek 2955fa4a1febSPawel Jakub Dawidek return (0); 2956fa4a1febSPawel Jakub Dawidek } 2957fa4a1febSPawel Jakub Dawidek 2958fa4a1febSPawel Jakub Dawidek static int 2959fa4a1febSPawel Jakub Dawidek g_mirror_check_metadata(struct g_mirror_softc *sc, struct g_provider *pp, 2960fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata *md) 2961fa4a1febSPawel Jakub Dawidek { 2962fa4a1febSPawel Jakub Dawidek 2963af7dcae0SConrad Meyer G_MIRROR_DEBUG(2, "%s: md_did 0x%u disk %s device %s md_all 0x%x " 2964af7dcae0SConrad Meyer "sc_ndisks 0x%x md_slice 0x%x sc_slice 0x%x md_balance 0x%x " 2965af7dcae0SConrad Meyer "sc_balance 0x%x sc_mediasize 0x%jx pp_mediasize 0x%jx " 2966af7dcae0SConrad Meyer "md_sectorsize 0x%x sc_sectorsize 0x%x md_mflags 0x%jx " 2967af7dcae0SConrad Meyer "md_dflags 0x%jx md_syncid 0x%x md_genid 0x%x md_priority 0x%x " 2968af7dcae0SConrad Meyer "sc_state 0x%x.", 2969af7dcae0SConrad Meyer __func__, md->md_did, pp->name, sc->sc_name, md->md_all, 2970af7dcae0SConrad Meyer sc->sc_ndisks, md->md_slice, sc->sc_slice, md->md_balance, 2971af7dcae0SConrad Meyer sc->sc_balance, (uintmax_t)sc->sc_mediasize, 2972af7dcae0SConrad Meyer (uintmax_t)pp->mediasize, md->md_sectorsize, sc->sc_sectorsize, 2973af7dcae0SConrad Meyer (uintmax_t)md->md_mflags, (uintmax_t)md->md_dflags, md->md_syncid, 2974af7dcae0SConrad Meyer md->md_genid, md->md_priority, sc->sc_state); 2975af7dcae0SConrad Meyer 2976fa4a1febSPawel Jakub Dawidek if (g_mirror_id2disk(sc, md->md_did) != NULL) { 2977fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Disk %s (id=%u) already exists, skipping.", 2978fa4a1febSPawel Jakub Dawidek pp->name, md->md_did); 2979fa4a1febSPawel Jakub Dawidek return (EEXIST); 2980fa4a1febSPawel Jakub Dawidek } 2981fa4a1febSPawel Jakub Dawidek if (sc->sc_mediasize > pp->mediasize) { 2982fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 2983fa4a1febSPawel Jakub Dawidek "Invalid size of disk %s (device %s), skipping.", pp->name, 2984fa4a1febSPawel Jakub Dawidek sc->sc_name); 2985fa4a1febSPawel Jakub Dawidek return (EINVAL); 2986fa4a1febSPawel Jakub Dawidek } 2987fa4a1febSPawel Jakub Dawidek if (md->md_sectorsize != sc->sc_sectorsize) { 2988fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 2989fa4a1febSPawel Jakub Dawidek "Invalid '%s' field on disk %s (device %s), skipping.", 2990fa4a1febSPawel Jakub Dawidek "md_sectorsize", pp->name, sc->sc_name); 2991fa4a1febSPawel Jakub Dawidek return (EINVAL); 2992fa4a1febSPawel Jakub Dawidek } 2993fa4a1febSPawel Jakub Dawidek if ((sc->sc_sectorsize % pp->sectorsize) != 0) { 2994fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 2995fa4a1febSPawel Jakub Dawidek "Invalid sector size of disk %s (device %s), skipping.", 2996fa4a1febSPawel Jakub Dawidek pp->name, sc->sc_name); 2997fa4a1febSPawel Jakub Dawidek return (EINVAL); 2998fa4a1febSPawel Jakub Dawidek } 2999fa4a1febSPawel Jakub Dawidek if ((md->md_mflags & ~G_MIRROR_DEVICE_FLAG_MASK) != 0) { 3000fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 3001fa4a1febSPawel Jakub Dawidek "Invalid device flags on disk %s (device %s), skipping.", 3002fa4a1febSPawel Jakub Dawidek pp->name, sc->sc_name); 3003fa4a1febSPawel Jakub Dawidek return (EINVAL); 3004fa4a1febSPawel Jakub Dawidek } 3005fa4a1febSPawel Jakub Dawidek if ((md->md_dflags & ~G_MIRROR_DISK_FLAG_MASK) != 0) { 3006fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 3007fa4a1febSPawel Jakub Dawidek "Invalid disk flags on disk %s (device %s), skipping.", 3008fa4a1febSPawel Jakub Dawidek pp->name, sc->sc_name); 3009fa4a1febSPawel Jakub Dawidek return (EINVAL); 3010fa4a1febSPawel Jakub Dawidek } 3011fa4a1febSPawel Jakub Dawidek return (0); 3012fa4a1febSPawel Jakub Dawidek } 3013fa4a1febSPawel Jakub Dawidek 3014fd6d3120SPawel Jakub Dawidek int 3015fa4a1febSPawel Jakub Dawidek g_mirror_add_disk(struct g_mirror_softc *sc, struct g_provider *pp, 3016fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata *md) 3017fa4a1febSPawel Jakub Dawidek { 3018fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 3019fa4a1febSPawel Jakub Dawidek int error; 3020fa4a1febSPawel Jakub Dawidek 3021855761d5SPawel Jakub Dawidek g_topology_assert_not(); 3022fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Adding disk %s.", pp->name); 3023fa4a1febSPawel Jakub Dawidek 3024fa4a1febSPawel Jakub Dawidek error = g_mirror_check_metadata(sc, pp, md); 3025fa4a1febSPawel Jakub Dawidek if (error != 0) 3026fa4a1febSPawel Jakub Dawidek return (error); 3027af7dcae0SConrad Meyer 3028af7dcae0SConrad Meyer if (md->md_genid < sc->sc_genid) { 30299a9f5041SPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Component %s (device %s) broken, skipping.", 30309a9f5041SPawel Jakub Dawidek pp->name, sc->sc_name); 30319a9f5041SPawel Jakub Dawidek return (EINVAL); 30329a9f5041SPawel Jakub Dawidek } 3033af7dcae0SConrad Meyer 3034af7dcae0SConrad Meyer /* 3035af7dcae0SConrad Meyer * If the component disk we're tasting has newer metadata than the 3036af7dcae0SConrad Meyer * STARTING gmirror device, refresh the device from the component. 3037af7dcae0SConrad Meyer */ 3038af7dcae0SConrad Meyer error = g_mirror_refresh_device(sc, pp, md); 3039af7dcae0SConrad Meyer if (error != 0) 3040af7dcae0SConrad Meyer return (error); 3041af7dcae0SConrad Meyer 3042fa4a1febSPawel Jakub Dawidek disk = g_mirror_init_disk(sc, pp, md, &error); 3043fa4a1febSPawel Jakub Dawidek if (disk == NULL) 3044fa4a1febSPawel Jakub Dawidek return (error); 3045fa4a1febSPawel Jakub Dawidek error = g_mirror_event_send(disk, G_MIRROR_DISK_STATE_NEW, 3046fa4a1febSPawel Jakub Dawidek G_MIRROR_EVENT_WAIT); 30479a9f5041SPawel Jakub Dawidek if (error != 0) 3048fa4a1febSPawel Jakub Dawidek return (error); 30499a9f5041SPawel Jakub Dawidek if (md->md_version < G_MIRROR_VERSION) { 30509a9f5041SPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Upgrading metadata on %s (v%d->v%d).", 30519a9f5041SPawel Jakub Dawidek pp->name, md->md_version, G_MIRROR_VERSION); 30529a9f5041SPawel Jakub Dawidek g_mirror_update_metadata(disk); 30539a9f5041SPawel Jakub Dawidek } 30549a9f5041SPawel Jakub Dawidek return (0); 3055fa4a1febSPawel Jakub Dawidek } 3056fa4a1febSPawel Jakub Dawidek 3057712fe9bdSPawel Jakub Dawidek static void 3058712fe9bdSPawel Jakub Dawidek g_mirror_destroy_delayed(void *arg, int flag) 3059712fe9bdSPawel Jakub Dawidek { 3060712fe9bdSPawel Jakub Dawidek struct g_mirror_softc *sc; 3061712fe9bdSPawel Jakub Dawidek int error; 3062712fe9bdSPawel Jakub Dawidek 3063712fe9bdSPawel Jakub Dawidek if (flag == EV_CANCEL) { 3064712fe9bdSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Destroying canceled."); 3065712fe9bdSPawel Jakub Dawidek return; 3066712fe9bdSPawel Jakub Dawidek } 3067712fe9bdSPawel Jakub Dawidek sc = arg; 3068712fe9bdSPawel Jakub Dawidek g_topology_unlock(); 3069712fe9bdSPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 3070712fe9bdSPawel Jakub Dawidek KASSERT((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) == 0, 3071712fe9bdSPawel Jakub Dawidek ("DESTROY flag set on %s.", sc->sc_name)); 3072a7d94fccSMark Johnston KASSERT((sc->sc_flags & G_MIRROR_DEVICE_FLAG_CLOSEWAIT) != 0, 3073a7d94fccSMark Johnston ("CLOSEWAIT flag not set on %s.", sc->sc_name)); 3074712fe9bdSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Destroying %s (delayed).", sc->sc_name); 3075712fe9bdSPawel Jakub Dawidek error = g_mirror_destroy(sc, G_MIRROR_DESTROY_SOFT); 3076712fe9bdSPawel Jakub Dawidek if (error != 0) { 307709adfca3SBryan Drewery G_MIRROR_DEBUG(0, "Cannot destroy %s (error=%d).", 307809adfca3SBryan Drewery sc->sc_name, error); 3079712fe9bdSPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3080712fe9bdSPawel Jakub Dawidek } 3081712fe9bdSPawel Jakub Dawidek g_topology_lock(); 3082712fe9bdSPawel Jakub Dawidek } 3083712fe9bdSPawel Jakub Dawidek 3084fa4a1febSPawel Jakub Dawidek static int 3085fa4a1febSPawel Jakub Dawidek g_mirror_access(struct g_provider *pp, int acr, int acw, int ace) 3086fa4a1febSPawel Jakub Dawidek { 3087fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 30885a236b0eSAlexander Motin int error = 0; 3089fa4a1febSPawel Jakub Dawidek 3090fa4a1febSPawel Jakub Dawidek g_topology_assert(); 3091fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Access request for %s: r%dw%de%d.", pp->name, acr, 3092fa4a1febSPawel Jakub Dawidek acw, ace); 3093fa4a1febSPawel Jakub Dawidek 3094dc399583SAlexander Motin sc = pp->private; 30951f7fec3cSPawel Jakub Dawidek KASSERT(sc != NULL, ("NULL softc (provider=%s).", pp->name)); 30961f7fec3cSPawel Jakub Dawidek 3097855761d5SPawel Jakub Dawidek g_topology_unlock(); 3098855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 3099712fe9bdSPawel Jakub Dawidek if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0 || 3100a7d94fccSMark Johnston (sc->sc_flags & G_MIRROR_DEVICE_FLAG_CLOSEWAIT) != 0 || 3101712fe9bdSPawel Jakub Dawidek LIST_EMPTY(&sc->sc_disks)) { 3102712fe9bdSPawel Jakub Dawidek if (acr > 0 || acw > 0 || ace > 0) 3103712fe9bdSPawel Jakub Dawidek error = ENXIO; 3104712fe9bdSPawel Jakub Dawidek goto end; 3105712fe9bdSPawel Jakub Dawidek } 31065a236b0eSAlexander Motin sc->sc_provider_open += acr + acw + ace; 31075a236b0eSAlexander Motin if (pp->acw + acw == 0) 31085a236b0eSAlexander Motin g_mirror_idle(sc, 0); 3109a7d94fccSMark Johnston if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_CLOSEWAIT) != 0 && 31105a236b0eSAlexander Motin sc->sc_provider_open == 0) 31115a236b0eSAlexander Motin g_post_event(g_mirror_destroy_delayed, sc, M_WAITOK, sc, NULL); 3112712fe9bdSPawel Jakub Dawidek end: 3113855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3114855761d5SPawel Jakub Dawidek g_topology_lock(); 3115712fe9bdSPawel Jakub Dawidek return (error); 3116fa4a1febSPawel Jakub Dawidek } 3117fa4a1febSPawel Jakub Dawidek 3118af7dcae0SConrad Meyer static void 3119af7dcae0SConrad Meyer g_mirror_reinit_from_metadata(struct g_mirror_softc *sc, 3120af7dcae0SConrad Meyer const struct g_mirror_metadata *md) 3121af7dcae0SConrad Meyer { 3122af7dcae0SConrad Meyer 3123af7dcae0SConrad Meyer sc->sc_genid = md->md_genid; 3124af7dcae0SConrad Meyer sc->sc_syncid = md->md_syncid; 3125af7dcae0SConrad Meyer 3126af7dcae0SConrad Meyer sc->sc_slice = md->md_slice; 3127af7dcae0SConrad Meyer sc->sc_balance = md->md_balance; 3128af7dcae0SConrad Meyer sc->sc_mediasize = md->md_mediasize; 3129af7dcae0SConrad Meyer sc->sc_ndisks = md->md_all; 313023c25bd8SConrad Meyer sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_MASK; 313123c25bd8SConrad Meyer sc->sc_flags |= (md->md_mflags & G_MIRROR_DEVICE_FLAG_MASK); 3132af7dcae0SConrad Meyer } 3133af7dcae0SConrad Meyer 3134b6fe583cSAlexander Motin struct g_geom * 3135b6fe583cSAlexander Motin g_mirror_create(struct g_class *mp, const struct g_mirror_metadata *md, 3136b6fe583cSAlexander Motin u_int type) 3137fa4a1febSPawel Jakub Dawidek { 3138fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 3139fa4a1febSPawel Jakub Dawidek struct g_geom *gp; 3140fa4a1febSPawel Jakub Dawidek int error, timeout; 3141fa4a1febSPawel Jakub Dawidek 3142fa4a1febSPawel Jakub Dawidek g_topology_assert(); 3143fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 3144fa4a1febSPawel Jakub Dawidek md->md_mid); 3145fa4a1febSPawel Jakub Dawidek 3146fa4a1febSPawel Jakub Dawidek /* One disk is minimum. */ 3147fa4a1febSPawel Jakub Dawidek if (md->md_all < 1) 3148fa4a1febSPawel Jakub Dawidek return (NULL); 3149fa4a1febSPawel Jakub Dawidek /* 3150fa4a1febSPawel Jakub Dawidek * Action geom. 3151fa4a1febSPawel Jakub Dawidek */ 3152fa4a1febSPawel Jakub Dawidek gp = g_new_geomf(mp, "%s", md->md_name); 3153fa4a1febSPawel Jakub Dawidek sc = malloc(sizeof(*sc), M_MIRROR, M_WAITOK | M_ZERO); 3154fa4a1febSPawel Jakub Dawidek gp->start = g_mirror_start; 3155fa4a1febSPawel Jakub Dawidek gp->orphan = g_mirror_orphan; 3156fa4a1febSPawel Jakub Dawidek gp->access = g_mirror_access; 3157fa4a1febSPawel Jakub Dawidek gp->dumpconf = g_mirror_dumpconf; 3158fa4a1febSPawel Jakub Dawidek 3159b6fe583cSAlexander Motin sc->sc_type = type; 3160fa4a1febSPawel Jakub Dawidek sc->sc_id = md->md_mid; 3161af7dcae0SConrad Meyer g_mirror_reinit_from_metadata(sc, md); 3162fa4a1febSPawel Jakub Dawidek sc->sc_sectorsize = md->md_sectorsize; 31639a9f5041SPawel Jakub Dawidek sc->sc_bump_id = 0; 3164fe6f94eaSPawel Jakub Dawidek sc->sc_idle = 1; 316501f1f41cSPawel Jakub Dawidek sc->sc_last_write = time_uptime; 3166fe6f94eaSPawel Jakub Dawidek sc->sc_writes = 0; 3167dc399583SAlexander Motin sc->sc_refcnt = 1; 3168855761d5SPawel Jakub Dawidek sx_init(&sc->sc_lock, "gmirror:lock"); 31699abe2e7eSMark Johnston TAILQ_INIT(&sc->sc_queue); 3170fa4a1febSPawel Jakub Dawidek mtx_init(&sc->sc_queue_mtx, "gmirror:queue", NULL, MTX_DEF); 31719abe2e7eSMark Johnston TAILQ_INIT(&sc->sc_regular_delayed); 31729abe2e7eSMark Johnston TAILQ_INIT(&sc->sc_inflight); 31739abe2e7eSMark Johnston TAILQ_INIT(&sc->sc_sync_delayed); 3174fa4a1febSPawel Jakub Dawidek LIST_INIT(&sc->sc_disks); 3175fa4a1febSPawel Jakub Dawidek TAILQ_INIT(&sc->sc_events); 3176fa4a1febSPawel Jakub Dawidek mtx_init(&sc->sc_events_mtx, "gmirror:events", NULL, MTX_DEF); 3177fd90e2edSJung-uk Kim callout_init(&sc->sc_callout, 1); 317840ea77a0SAlexander Motin mtx_init(&sc->sc_done_mtx, "gmirror:done", NULL, MTX_DEF); 3179fa4a1febSPawel Jakub Dawidek sc->sc_state = G_MIRROR_DEVICE_STATE_STARTING; 3180fa4a1febSPawel Jakub Dawidek gp->softc = sc; 3181fa4a1febSPawel Jakub Dawidek sc->sc_geom = gp; 3182fa4a1febSPawel Jakub Dawidek sc->sc_provider = NULL; 31835a236b0eSAlexander Motin sc->sc_provider_open = 0; 3184fa4a1febSPawel Jakub Dawidek /* 3185fa4a1febSPawel Jakub Dawidek * Synchronization geom. 3186fa4a1febSPawel Jakub Dawidek */ 3187fa4a1febSPawel Jakub Dawidek gp = g_new_geomf(mp, "%s.sync", md->md_name); 3188fa4a1febSPawel Jakub Dawidek gp->softc = sc; 3189fa4a1febSPawel Jakub Dawidek gp->orphan = g_mirror_orphan; 3190fa4a1febSPawel Jakub Dawidek sc->sc_sync.ds_geom = gp; 3191fa4a1febSPawel Jakub Dawidek sc->sc_sync.ds_ndisks = 0; 31923745c395SJulian Elischer error = kproc_create(g_mirror_worker, sc, &sc->sc_worker, 0, 0, 3193fa4a1febSPawel Jakub Dawidek "g_mirror %s", md->md_name); 3194fa4a1febSPawel Jakub Dawidek if (error != 0) { 3195fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Cannot create kernel thread for %s.", 3196fa4a1febSPawel Jakub Dawidek sc->sc_name); 3197fa4a1febSPawel Jakub Dawidek g_destroy_geom(sc->sc_sync.ds_geom); 3198fa4a1febSPawel Jakub Dawidek g_destroy_geom(sc->sc_geom); 3199dc399583SAlexander Motin g_mirror_free_device(sc); 3200fa4a1febSPawel Jakub Dawidek return (NULL); 3201fa4a1febSPawel Jakub Dawidek } 3202fa4a1febSPawel Jakub Dawidek 32030cca572eSJohn-Mark Gurney G_MIRROR_DEBUG(1, "Device %s created (%u components, id=%u).", 32040cca572eSJohn-Mark Gurney sc->sc_name, sc->sc_ndisks, sc->sc_id); 3205fa4a1febSPawel Jakub Dawidek 3206853a10a5SAndrew Thompson sc->sc_rootmount = root_mount_hold("GMIRROR"); 32074eafb037SPawel Jakub Dawidek G_MIRROR_DEBUG(1, "root_mount_hold %p", sc->sc_rootmount); 32082f1cfb7fSMark Johnston 3209fa4a1febSPawel Jakub Dawidek /* 32102f1cfb7fSMark Johnston * Schedule startup timeout. 3211fa4a1febSPawel Jakub Dawidek */ 321214089daeSPawel Jakub Dawidek timeout = g_mirror_timeout * hz; 32132f1cfb7fSMark Johnston sc->sc_timeout_event = malloc(sizeof(struct g_mirror_event), M_MIRROR, 32142f1cfb7fSMark Johnston M_WAITOK); 321514089daeSPawel Jakub Dawidek callout_reset(&sc->sc_callout, timeout, g_mirror_go, sc); 3216fa4a1febSPawel Jakub Dawidek return (sc->sc_geom); 3217fa4a1febSPawel Jakub Dawidek } 3218fa4a1febSPawel Jakub Dawidek 3219fa4a1febSPawel Jakub Dawidek int 3220712fe9bdSPawel Jakub Dawidek g_mirror_destroy(struct g_mirror_softc *sc, int how) 3221fa4a1febSPawel Jakub Dawidek { 3222712fe9bdSPawel Jakub Dawidek struct g_mirror_disk *disk; 3223fa4a1febSPawel Jakub Dawidek 3224855761d5SPawel Jakub Dawidek g_topology_assert_not(); 3225855761d5SPawel Jakub Dawidek sx_assert(&sc->sc_lock, SX_XLOCKED); 3226855761d5SPawel Jakub Dawidek 3227819cd913SMark Johnston if (sc->sc_provider_open != 0) { 3228712fe9bdSPawel Jakub Dawidek switch (how) { 3229712fe9bdSPawel Jakub Dawidek case G_MIRROR_DESTROY_SOFT: 3230fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 32315a236b0eSAlexander Motin "Device %s is still open (%d).", sc->sc_name, 32325a236b0eSAlexander Motin sc->sc_provider_open); 3233fa4a1febSPawel Jakub Dawidek return (EBUSY); 3234712fe9bdSPawel Jakub Dawidek case G_MIRROR_DESTROY_DELAYED: 3235712fe9bdSPawel Jakub Dawidek G_MIRROR_DEBUG(1, 3236712fe9bdSPawel Jakub Dawidek "Device %s will be destroyed on last close.", 32375a236b0eSAlexander Motin sc->sc_name); 3238712fe9bdSPawel Jakub Dawidek LIST_FOREACH(disk, &sc->sc_disks, d_next) { 3239712fe9bdSPawel Jakub Dawidek if (disk->d_state == 3240712fe9bdSPawel Jakub Dawidek G_MIRROR_DISK_STATE_SYNCHRONIZING) { 3241712fe9bdSPawel Jakub Dawidek g_mirror_sync_stop(disk, 1); 3242712fe9bdSPawel Jakub Dawidek } 3243712fe9bdSPawel Jakub Dawidek } 3244a7d94fccSMark Johnston sc->sc_flags |= G_MIRROR_DEVICE_FLAG_CLOSEWAIT; 3245712fe9bdSPawel Jakub Dawidek return (EBUSY); 3246712fe9bdSPawel Jakub Dawidek case G_MIRROR_DESTROY_HARD: 3247712fe9bdSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Device %s is still open, so it " 32485a236b0eSAlexander Motin "can't be definitely removed.", sc->sc_name); 3249fa4a1febSPawel Jakub Dawidek } 3250fa4a1febSPawel Jakub Dawidek } 3251fa4a1febSPawel Jakub Dawidek 3252cef5abd1SMark Johnston if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 3253cef5abd1SMark Johnston sx_xunlock(&sc->sc_lock); 3254a0636676SPawel Jakub Dawidek return (0); 3255cef5abd1SMark Johnston } 3256fa4a1febSPawel Jakub Dawidek sc->sc_flags |= G_MIRROR_DEVICE_FLAG_DESTROY; 3257a7d94fccSMark Johnston sc->sc_flags |= G_MIRROR_DEVICE_FLAG_DRAIN; 3258fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); 3259855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3260fa4a1febSPawel Jakub Dawidek mtx_lock(&sc->sc_queue_mtx); 3261fa4a1febSPawel Jakub Dawidek wakeup(sc); 3262fa4a1febSPawel Jakub Dawidek mtx_unlock(&sc->sc_queue_mtx); 3263fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Sleeping %p.", __func__, &sc->sc_worker); 3264fa4a1febSPawel Jakub Dawidek while (sc->sc_worker != NULL) 3265fa4a1febSPawel Jakub Dawidek tsleep(&sc->sc_worker, PRIBIO, "m:destroy", hz / 5); 3266fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(4, "%s: Woken up %p.", __func__, &sc->sc_worker); 3267855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 3268fa4a1febSPawel Jakub Dawidek g_mirror_destroy_device(sc); 3269fa4a1febSPawel Jakub Dawidek return (0); 3270fa4a1febSPawel Jakub Dawidek } 3271fa4a1febSPawel Jakub Dawidek 3272fa4a1febSPawel Jakub Dawidek static void 3273fa4a1febSPawel Jakub Dawidek g_mirror_taste_orphan(struct g_consumer *cp) 3274fa4a1febSPawel Jakub Dawidek { 3275fa4a1febSPawel Jakub Dawidek 3276fa4a1febSPawel Jakub Dawidek KASSERT(1 == 0, ("%s called while tasting %s.", __func__, 3277fa4a1febSPawel Jakub Dawidek cp->provider->name)); 3278fa4a1febSPawel Jakub Dawidek } 3279fa4a1febSPawel Jakub Dawidek 3280fa4a1febSPawel Jakub Dawidek static struct g_geom * 3281fa4a1febSPawel Jakub Dawidek g_mirror_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 3282fa4a1febSPawel Jakub Dawidek { 3283fa4a1febSPawel Jakub Dawidek struct g_mirror_metadata md; 3284fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 3285fa4a1febSPawel Jakub Dawidek struct g_consumer *cp; 3286fa4a1febSPawel Jakub Dawidek struct g_geom *gp; 3287fa4a1febSPawel Jakub Dawidek int error; 3288fa4a1febSPawel Jakub Dawidek 3289fa4a1febSPawel Jakub Dawidek g_topology_assert(); 3290fa4a1febSPawel Jakub Dawidek g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 3291fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(2, "Tasting %s.", pp->name); 3292fa4a1febSPawel Jakub Dawidek 3293fa4a1febSPawel Jakub Dawidek gp = g_new_geomf(mp, "mirror:taste"); 3294fa4a1febSPawel Jakub Dawidek /* 3295fa4a1febSPawel Jakub Dawidek * This orphan function should be never called. 3296fa4a1febSPawel Jakub Dawidek */ 3297fa4a1febSPawel Jakub Dawidek gp->orphan = g_mirror_taste_orphan; 3298fa4a1febSPawel Jakub Dawidek cp = g_new_consumer(gp); 329910ae42ccSAlexander Motin cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 3300d22ff249SEdward Tomasz Napierala error = g_attach(cp, pp); 3301d22ff249SEdward Tomasz Napierala if (error == 0) { 3302fa4a1febSPawel Jakub Dawidek error = g_mirror_read_metadata(cp, &md); 3303fa4a1febSPawel Jakub Dawidek g_detach(cp); 3304d22ff249SEdward Tomasz Napierala } 3305fa4a1febSPawel Jakub Dawidek g_destroy_consumer(cp); 3306fa4a1febSPawel Jakub Dawidek g_destroy_geom(gp); 3307fa4a1febSPawel Jakub Dawidek if (error != 0) 3308fa4a1febSPawel Jakub Dawidek return (NULL); 3309fa4a1febSPawel Jakub Dawidek gp = NULL; 3310fa4a1febSPawel Jakub Dawidek 331190f2be24SAlexander Motin if (md.md_provider[0] != '\0' && 331290f2be24SAlexander Motin !g_compare_names(md.md_provider, pp->name)) 33136c74f517SPawel Jakub Dawidek return (NULL); 3314e6890985SPawel Jakub Dawidek if (md.md_provsize != 0 && md.md_provsize != pp->mediasize) 3315e6890985SPawel Jakub Dawidek return (NULL); 3316fa4a1febSPawel Jakub Dawidek if ((md.md_dflags & G_MIRROR_DISK_FLAG_INACTIVE) != 0) { 3317fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, 3318fa4a1febSPawel Jakub Dawidek "Device %s: provider %s marked as inactive, skipping.", 3319fa4a1febSPawel Jakub Dawidek md.md_name, pp->name); 3320fa4a1febSPawel Jakub Dawidek return (NULL); 3321fa4a1febSPawel Jakub Dawidek } 3322fa4a1febSPawel Jakub Dawidek if (g_mirror_debug >= 2) 3323fa4a1febSPawel Jakub Dawidek mirror_metadata_dump(&md); 3324fa4a1febSPawel Jakub Dawidek 3325fa4a1febSPawel Jakub Dawidek /* 3326fa4a1febSPawel Jakub Dawidek * Let's check if device already exists. 3327fa4a1febSPawel Jakub Dawidek */ 332845d5e85aSPawel Jakub Dawidek sc = NULL; 3329fa4a1febSPawel Jakub Dawidek LIST_FOREACH(gp, &mp->geom, geom) { 3330fa4a1febSPawel Jakub Dawidek sc = gp->softc; 3331fa4a1febSPawel Jakub Dawidek if (sc == NULL) 3332fa4a1febSPawel Jakub Dawidek continue; 3333b6fe583cSAlexander Motin if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) 3334b6fe583cSAlexander Motin continue; 3335fa4a1febSPawel Jakub Dawidek if (sc->sc_sync.ds_geom == gp) 3336fa4a1febSPawel Jakub Dawidek continue; 3337fa4a1febSPawel Jakub Dawidek if (strcmp(md.md_name, sc->sc_name) != 0) 3338fa4a1febSPawel Jakub Dawidek continue; 3339fa4a1febSPawel Jakub Dawidek if (md.md_mid != sc->sc_id) { 3340fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Device %s already configured.", 3341fa4a1febSPawel Jakub Dawidek sc->sc_name); 3342fa4a1febSPawel Jakub Dawidek return (NULL); 3343fa4a1febSPawel Jakub Dawidek } 3344fa4a1febSPawel Jakub Dawidek break; 3345fa4a1febSPawel Jakub Dawidek } 3346fa4a1febSPawel Jakub Dawidek if (gp == NULL) { 3347b6fe583cSAlexander Motin gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_AUTOMATIC); 3348fa4a1febSPawel Jakub Dawidek if (gp == NULL) { 33496b1c71eeSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Cannot create device %s.", 3350fa4a1febSPawel Jakub Dawidek md.md_name); 3351fa4a1febSPawel Jakub Dawidek return (NULL); 3352fa4a1febSPawel Jakub Dawidek } 3353fa4a1febSPawel Jakub Dawidek sc = gp->softc; 3354fa4a1febSPawel Jakub Dawidek } 3355fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 3356855761d5SPawel Jakub Dawidek g_topology_unlock(); 3357855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 33581ee0138dSAndrey V. Elsukov sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING; 3359fa4a1febSPawel Jakub Dawidek error = g_mirror_add_disk(sc, pp, &md); 33609167705cSRyan Libby sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING; 3361fa4a1febSPawel Jakub Dawidek if (error != 0) { 3362fa4a1febSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Cannot add disk %s to %s (error=%d).", 3363fa4a1febSPawel Jakub Dawidek pp->name, gp->name, error); 3364855761d5SPawel Jakub Dawidek if (LIST_EMPTY(&sc->sc_disks)) { 3365712fe9bdSPawel Jakub Dawidek g_cancel_event(sc); 33663525bb6bSPawel Jakub Dawidek g_mirror_destroy(sc, G_MIRROR_DESTROY_HARD); 3367855761d5SPawel Jakub Dawidek g_topology_lock(); 3368fa4a1febSPawel Jakub Dawidek return (NULL); 3369fa4a1febSPawel Jakub Dawidek } 3370855761d5SPawel Jakub Dawidek gp = NULL; 3371855761d5SPawel Jakub Dawidek } 33721ee0138dSAndrey V. Elsukov if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 33731ee0138dSAndrey V. Elsukov g_mirror_destroy(sc, G_MIRROR_DESTROY_HARD); 33741ee0138dSAndrey V. Elsukov g_topology_lock(); 33751ee0138dSAndrey V. Elsukov return (NULL); 33761ee0138dSAndrey V. Elsukov } 3377855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3378855761d5SPawel Jakub Dawidek g_topology_lock(); 3379fa4a1febSPawel Jakub Dawidek return (gp); 3380fa4a1febSPawel Jakub Dawidek } 3381fa4a1febSPawel Jakub Dawidek 338232cea4caSAndrey V. Elsukov static void 338332cea4caSAndrey V. Elsukov g_mirror_resize(struct g_consumer *cp) 338432cea4caSAndrey V. Elsukov { 338532cea4caSAndrey V. Elsukov struct g_mirror_disk *disk; 338632cea4caSAndrey V. Elsukov 338732cea4caSAndrey V. Elsukov g_topology_assert(); 338832cea4caSAndrey V. Elsukov g_trace(G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name); 338932cea4caSAndrey V. Elsukov 339032cea4caSAndrey V. Elsukov disk = cp->private; 339132cea4caSAndrey V. Elsukov if (disk == NULL) 339232cea4caSAndrey V. Elsukov return; 339332cea4caSAndrey V. Elsukov g_topology_unlock(); 339432cea4caSAndrey V. Elsukov g_mirror_update_metadata(disk); 339532cea4caSAndrey V. Elsukov g_topology_lock(); 339632cea4caSAndrey V. Elsukov } 339732cea4caSAndrey V. Elsukov 3398fa4a1febSPawel Jakub Dawidek static int 3399fa4a1febSPawel Jakub Dawidek g_mirror_destroy_geom(struct gctl_req *req __unused, 3400fa4a1febSPawel Jakub Dawidek struct g_class *mp __unused, struct g_geom *gp) 3401fa4a1febSPawel Jakub Dawidek { 3402855761d5SPawel Jakub Dawidek struct g_mirror_softc *sc; 3403855761d5SPawel Jakub Dawidek int error; 3404fa4a1febSPawel Jakub Dawidek 3405855761d5SPawel Jakub Dawidek g_topology_unlock(); 3406855761d5SPawel Jakub Dawidek sc = gp->softc; 3407855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 3408712fe9bdSPawel Jakub Dawidek g_cancel_event(sc); 34093525bb6bSPawel Jakub Dawidek error = g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_SOFT); 3410855761d5SPawel Jakub Dawidek if (error != 0) 3411855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3412855761d5SPawel Jakub Dawidek g_topology_lock(); 3413855761d5SPawel Jakub Dawidek return (error); 3414fa4a1febSPawel Jakub Dawidek } 3415fa4a1febSPawel Jakub Dawidek 3416fa4a1febSPawel Jakub Dawidek static void 3417fa4a1febSPawel Jakub Dawidek g_mirror_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 3418fa4a1febSPawel Jakub Dawidek struct g_consumer *cp, struct g_provider *pp) 3419fa4a1febSPawel Jakub Dawidek { 3420fa4a1febSPawel Jakub Dawidek struct g_mirror_softc *sc; 3421fa4a1febSPawel Jakub Dawidek 3422fa4a1febSPawel Jakub Dawidek g_topology_assert(); 3423fa4a1febSPawel Jakub Dawidek 3424fa4a1febSPawel Jakub Dawidek sc = gp->softc; 3425fa4a1febSPawel Jakub Dawidek if (sc == NULL) 3426fa4a1febSPawel Jakub Dawidek return; 3427fa4a1febSPawel Jakub Dawidek /* Skip synchronization geom. */ 3428fa4a1febSPawel Jakub Dawidek if (gp == sc->sc_sync.ds_geom) 3429fa4a1febSPawel Jakub Dawidek return; 3430fa4a1febSPawel Jakub Dawidek if (pp != NULL) { 3431fa4a1febSPawel Jakub Dawidek /* Nothing here. */ 3432fa4a1febSPawel Jakub Dawidek } else if (cp != NULL) { 3433fa4a1febSPawel Jakub Dawidek struct g_mirror_disk *disk; 3434fa4a1febSPawel Jakub Dawidek 3435fa4a1febSPawel Jakub Dawidek disk = cp->private; 3436fa4a1febSPawel Jakub Dawidek if (disk == NULL) 3437fa4a1febSPawel Jakub Dawidek return; 3438fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)disk->d_id); 3439fa4a1febSPawel Jakub Dawidek if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) { 3440fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Synchronized>", indent); 3441855761d5SPawel Jakub Dawidek if (disk->d_sync.ds_offset == 0) 344249ee0fceSAlexander Motin sbuf_cat(sb, "0%"); 344340e80522SMark Johnston else 3444fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%u%%", 3445855761d5SPawel Jakub Dawidek (u_int)((disk->d_sync.ds_offset * 100) / 344640e80522SMark Johnston sc->sc_mediasize)); 344749ee0fceSAlexander Motin sbuf_cat(sb, "</Synchronized>\n"); 344840e80522SMark Johnston if (disk->d_sync.ds_offset > 0) 34494a7f7b10SGleb Smirnoff sbuf_printf(sb, "%s<BytesSynced>%jd" 34504a7f7b10SGleb Smirnoff "</BytesSynced>\n", indent, 34514a7f7b10SGleb Smirnoff (intmax_t)disk->d_sync.ds_offset); 34524a7f7b10SGleb Smirnoff } 3453fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<SyncID>%u</SyncID>\n", indent, 3454fa4a1febSPawel Jakub Dawidek disk->d_sync.ds_syncid); 34559a9f5041SPawel Jakub Dawidek sbuf_printf(sb, "%s<GenID>%u</GenID>\n", indent, 34569a9f5041SPawel Jakub Dawidek disk->d_genid); 3457fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Flags>", indent); 3458fa4a1febSPawel Jakub Dawidek if (disk->d_flags == 0) 345949ee0fceSAlexander Motin sbuf_cat(sb, "NONE"); 3460fa4a1febSPawel Jakub Dawidek else { 3461fa4a1febSPawel Jakub Dawidek int first = 1; 3462fa4a1febSPawel Jakub Dawidek 34636b2b3e87SPawel Jakub Dawidek #define ADD_FLAG(flag, name) do { \ 34646b2b3e87SPawel Jakub Dawidek if ((disk->d_flags & (flag)) != 0) { \ 34656b2b3e87SPawel Jakub Dawidek if (!first) \ 346649ee0fceSAlexander Motin sbuf_cat(sb, ", "); \ 34676b2b3e87SPawel Jakub Dawidek else \ 34686b2b3e87SPawel Jakub Dawidek first = 0; \ 346949ee0fceSAlexander Motin sbuf_cat(sb, name); \ 34706b2b3e87SPawel Jakub Dawidek } \ 34716b2b3e87SPawel Jakub Dawidek } while (0) 34726b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_DIRTY, "DIRTY"); 34736b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_HARDCODED, "HARDCODED"); 34746b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_INACTIVE, "INACTIVE"); 34756b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_SYNCHRONIZING, 34766b2b3e87SPawel Jakub Dawidek "SYNCHRONIZING"); 34776b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_FORCE_SYNC, "FORCE_SYNC"); 3478d4b0268aSPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DISK_FLAG_BROKEN, "BROKEN"); 34796b2b3e87SPawel Jakub Dawidek #undef ADD_FLAG 3480fa4a1febSPawel Jakub Dawidek } 348149ee0fceSAlexander Motin sbuf_cat(sb, "</Flags>\n"); 3482fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Priority>%u</Priority>\n", indent, 3483f251dfbfSPawel Jakub Dawidek disk->d_priority); 3484fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<State>%s</State>\n", indent, 3485fa4a1febSPawel Jakub Dawidek g_mirror_disk_state2str(disk->d_state)); 3486fa4a1febSPawel Jakub Dawidek } else { 3487b6fe583cSAlexander Motin sbuf_printf(sb, "%s<Type>", indent); 3488b6fe583cSAlexander Motin switch (sc->sc_type) { 3489b6fe583cSAlexander Motin case G_MIRROR_TYPE_AUTOMATIC: 349049ee0fceSAlexander Motin sbuf_cat(sb, "AUTOMATIC"); 3491b6fe583cSAlexander Motin break; 3492b6fe583cSAlexander Motin case G_MIRROR_TYPE_MANUAL: 349349ee0fceSAlexander Motin sbuf_cat(sb, "MANUAL"); 3494b6fe583cSAlexander Motin break; 3495b6fe583cSAlexander Motin default: 349649ee0fceSAlexander Motin sbuf_cat(sb, "UNKNOWN"); 3497b6fe583cSAlexander Motin break; 3498b6fe583cSAlexander Motin } 349949ee0fceSAlexander Motin sbuf_cat(sb, "</Type>\n"); 3500fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 3501fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<SyncID>%u</SyncID>\n", indent, sc->sc_syncid); 35029a9f5041SPawel Jakub Dawidek sbuf_printf(sb, "%s<GenID>%u</GenID>\n", indent, sc->sc_genid); 3503fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Flags>", indent); 3504fa4a1febSPawel Jakub Dawidek if (sc->sc_flags == 0) 350549ee0fceSAlexander Motin sbuf_cat(sb, "NONE"); 3506fa4a1febSPawel Jakub Dawidek else { 3507fa4a1febSPawel Jakub Dawidek int first = 1; 3508fa4a1febSPawel Jakub Dawidek 35096b2b3e87SPawel Jakub Dawidek #define ADD_FLAG(flag, name) do { \ 35106b2b3e87SPawel Jakub Dawidek if ((sc->sc_flags & (flag)) != 0) { \ 35116b2b3e87SPawel Jakub Dawidek if (!first) \ 351249ee0fceSAlexander Motin sbuf_cat(sb, ", "); \ 35136b2b3e87SPawel Jakub Dawidek else \ 35146b2b3e87SPawel Jakub Dawidek first = 0; \ 351549ee0fceSAlexander Motin sbuf_cat(sb, name); \ 35166b2b3e87SPawel Jakub Dawidek } \ 35176b2b3e87SPawel Jakub Dawidek } while (0) 3518501250baSPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DEVICE_FLAG_NOFAILSYNC, "NOFAILSYNC"); 35196b2b3e87SPawel Jakub Dawidek ADD_FLAG(G_MIRROR_DEVICE_FLAG_NOAUTOSYNC, "NOAUTOSYNC"); 35206b2b3e87SPawel Jakub Dawidek #undef ADD_FLAG 3521fa4a1febSPawel Jakub Dawidek } 352249ee0fceSAlexander Motin sbuf_cat(sb, "</Flags>\n"); 3523fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Slice>%u</Slice>\n", indent, 3524fa4a1febSPawel Jakub Dawidek (u_int)sc->sc_slice); 3525fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Balance>%s</Balance>\n", indent, 3526fa4a1febSPawel Jakub Dawidek balance_name(sc->sc_balance)); 3527fa4a1febSPawel Jakub Dawidek sbuf_printf(sb, "%s<Components>%u</Components>\n", indent, 3528fa4a1febSPawel Jakub Dawidek sc->sc_ndisks); 3529f7b4d339SPawel Jakub Dawidek sbuf_printf(sb, "%s<State>", indent); 3530f7b4d339SPawel Jakub Dawidek if (sc->sc_state == G_MIRROR_DEVICE_STATE_STARTING) 3531f7b4d339SPawel Jakub Dawidek sbuf_printf(sb, "%s", "STARTING"); 3532f7b4d339SPawel Jakub Dawidek else if (sc->sc_ndisks == 3533f7b4d339SPawel Jakub Dawidek g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE)) 3534f7b4d339SPawel Jakub Dawidek sbuf_printf(sb, "%s", "COMPLETE"); 3535f7b4d339SPawel Jakub Dawidek else 3536f7b4d339SPawel Jakub Dawidek sbuf_printf(sb, "%s", "DEGRADED"); 353749ee0fceSAlexander Motin sbuf_cat(sb, "</State>\n"); 3538fa4a1febSPawel Jakub Dawidek } 3539fa4a1febSPawel Jakub Dawidek } 3540fa4a1febSPawel Jakub Dawidek 35416349471bSPawel Jakub Dawidek static void 3542cbab6161SAlexander Motin g_mirror_shutdown_post_sync(void *arg, int howto) 35436349471bSPawel Jakub Dawidek { 35446349471bSPawel Jakub Dawidek struct g_class *mp; 35456349471bSPawel Jakub Dawidek struct g_geom *gp, *gp2; 3546855761d5SPawel Jakub Dawidek struct g_mirror_softc *sc; 3547712fe9bdSPawel Jakub Dawidek int error; 35486349471bSPawel Jakub Dawidek 35494eb861d3SMitchell Horne if ((howto & RB_NOSYNC) != 0) 3550819cd913SMark Johnston return; 3551819cd913SMark Johnston 35526349471bSPawel Jakub Dawidek mp = arg; 35536349471bSPawel Jakub Dawidek g_topology_lock(); 3554cbab6161SAlexander Motin g_mirror_shutdown = 1; 35556349471bSPawel Jakub Dawidek LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2) { 3556855761d5SPawel Jakub Dawidek if ((sc = gp->softc) == NULL) 35576349471bSPawel Jakub Dawidek continue; 3558712fe9bdSPawel Jakub Dawidek /* Skip synchronization geom. */ 3559712fe9bdSPawel Jakub Dawidek if (gp == sc->sc_sync.ds_geom) 3560712fe9bdSPawel Jakub Dawidek continue; 3561855761d5SPawel Jakub Dawidek g_topology_unlock(); 3562855761d5SPawel Jakub Dawidek sx_xlock(&sc->sc_lock); 3563cbab6161SAlexander Motin g_mirror_idle(sc, -1); 3564712fe9bdSPawel Jakub Dawidek g_cancel_event(sc); 3565712fe9bdSPawel Jakub Dawidek error = g_mirror_destroy(sc, G_MIRROR_DESTROY_DELAYED); 3566712fe9bdSPawel Jakub Dawidek if (error != 0) 3567855761d5SPawel Jakub Dawidek sx_xunlock(&sc->sc_lock); 3568855761d5SPawel Jakub Dawidek g_topology_lock(); 3569855761d5SPawel Jakub Dawidek } 3570855761d5SPawel Jakub Dawidek g_topology_unlock(); 3571855761d5SPawel Jakub Dawidek } 3572855761d5SPawel Jakub Dawidek 3573855761d5SPawel Jakub Dawidek static void 35746349471bSPawel Jakub Dawidek g_mirror_init(struct g_class *mp) 35756349471bSPawel Jakub Dawidek { 35766349471bSPawel Jakub Dawidek 3577cbab6161SAlexander Motin g_mirror_post_sync = EVENTHANDLER_REGISTER(shutdown_post_sync, 3578cbab6161SAlexander Motin g_mirror_shutdown_post_sync, mp, SHUTDOWN_PRI_FIRST); 3579cbab6161SAlexander Motin if (g_mirror_post_sync == NULL) 35806349471bSPawel Jakub Dawidek G_MIRROR_DEBUG(0, "Warning! Cannot register shutdown event."); 35816349471bSPawel Jakub Dawidek } 35826349471bSPawel Jakub Dawidek 35836349471bSPawel Jakub Dawidek static void 35846349471bSPawel Jakub Dawidek g_mirror_fini(struct g_class *mp) 35856349471bSPawel Jakub Dawidek { 35866349471bSPawel Jakub Dawidek 3587cbab6161SAlexander Motin if (g_mirror_post_sync != NULL) 3588cbab6161SAlexander Motin EVENTHANDLER_DEREGISTER(shutdown_post_sync, g_mirror_post_sync); 35896349471bSPawel Jakub Dawidek } 35906349471bSPawel Jakub Dawidek 3591af7dcae0SConrad Meyer /* 3592af7dcae0SConrad Meyer * Refresh the mirror device's metadata when gmirror encounters a newer 3593af7dcae0SConrad Meyer * generation as the individual components are being added to the mirror set. 3594af7dcae0SConrad Meyer */ 3595af7dcae0SConrad Meyer static int 3596af7dcae0SConrad Meyer g_mirror_refresh_device(struct g_mirror_softc *sc, const struct g_provider *pp, 3597af7dcae0SConrad Meyer const struct g_mirror_metadata *md) 3598af7dcae0SConrad Meyer { 3599af7dcae0SConrad Meyer 3600af7dcae0SConrad Meyer g_topology_assert_not(); 3601af7dcae0SConrad Meyer sx_assert(&sc->sc_lock, SX_XLOCKED); 3602af7dcae0SConrad Meyer 3603af7dcae0SConrad Meyer KASSERT(sc->sc_genid <= md->md_genid, 3604af7dcae0SConrad Meyer ("%s: attempted to refresh from stale component %s (device %s) " 3605af7dcae0SConrad Meyer "(%u < %u).", __func__, pp->name, sc->sc_name, md->md_genid, 3606af7dcae0SConrad Meyer sc->sc_genid)); 3607af7dcae0SConrad Meyer 3608af7dcae0SConrad Meyer if (sc->sc_genid > md->md_genid || (sc->sc_genid == md->md_genid && 3609af7dcae0SConrad Meyer sc->sc_syncid >= md->md_syncid)) 3610af7dcae0SConrad Meyer return (0); 3611af7dcae0SConrad Meyer 3612af7dcae0SConrad Meyer G_MIRROR_DEBUG(0, "Found newer version for device %s (genid: curr=%u " 3613af7dcae0SConrad Meyer "new=%u; syncid: curr=%u new=%u; ndisks: curr=%u new=%u; " 3614af7dcae0SConrad Meyer "provider=%s).", sc->sc_name, sc->sc_genid, md->md_genid, 3615af7dcae0SConrad Meyer sc->sc_syncid, md->md_syncid, sc->sc_ndisks, md->md_all, pp->name); 3616af7dcae0SConrad Meyer 3617af7dcae0SConrad Meyer if (sc->sc_state != G_MIRROR_DEVICE_STATE_STARTING) { 3618af7dcae0SConrad Meyer /* Probable data corruption detected */ 3619af7dcae0SConrad Meyer G_MIRROR_DEBUG(0, "Cannot refresh metadata in %s state " 3620af7dcae0SConrad Meyer "(device=%s genid=%u). A stale mirror device was launched.", 3621af7dcae0SConrad Meyer g_mirror_device_state2str(sc->sc_state), sc->sc_name, 3622af7dcae0SConrad Meyer sc->sc_genid); 3623af7dcae0SConrad Meyer return (EINVAL); 3624af7dcae0SConrad Meyer } 3625af7dcae0SConrad Meyer 3626af7dcae0SConrad Meyer /* Update softc */ 3627af7dcae0SConrad Meyer g_mirror_reinit_from_metadata(sc, md); 3628af7dcae0SConrad Meyer 3629af7dcae0SConrad Meyer G_MIRROR_DEBUG(1, "Refresh device %s (id=%u, state=%s) from disk %s " 3630af7dcae0SConrad Meyer "(genid=%u syncid=%u md_all=%u).", sc->sc_name, md->md_mid, 3631af7dcae0SConrad Meyer g_mirror_device_state2str(sc->sc_state), pp->name, md->md_genid, 3632af7dcae0SConrad Meyer md->md_syncid, (unsigned)md->md_all); 3633af7dcae0SConrad Meyer 3634af7dcae0SConrad Meyer return (0); 3635af7dcae0SConrad Meyer } 3636af7dcae0SConrad Meyer 3637fa4a1febSPawel Jakub Dawidek DECLARE_GEOM_CLASS(g_mirror_class, g_mirror); 363874d6c131SKyle Evans MODULE_VERSION(geom_mirror, 0); 3639