xref: /freebsd-src/sys/geom/mirror/g_mirror.c (revision 01e186731a141d106ff5bee5a41412e7e2582a78)
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