xref: /freebsd/sys/geom/mirror/g_mirror_ctl.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1fa4a1febSPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
33728855aSPedro F. Giffuni  *
4b740e905SPawel Jakub Dawidek  * Copyright (c) 2004-2009 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>
32fa4a1febSPawel Jakub Dawidek #include <sys/kernel.h>
3332cea4caSAndrey V. Elsukov #include <sys/limits.h>
34fa4a1febSPawel Jakub Dawidek #include <sys/lock.h>
35fa4a1febSPawel Jakub Dawidek #include <sys/malloc.h>
368b0a00b7SMark Johnston #include <sys/sbuf.h>
378b0a00b7SMark Johnston #include <sys/sx.h>
388b0a00b7SMark Johnston 
39fa4a1febSPawel Jakub Dawidek #include <geom/geom.h>
40ac03832eSConrad Meyer #include <geom/geom_dbg.h>
4132cea4caSAndrey V. Elsukov #include <geom/geom_int.h>
42fa4a1febSPawel Jakub Dawidek #include <geom/mirror/g_mirror.h>
43fa4a1febSPawel Jakub Dawidek 
44844b743dSConrad Meyer /*
45844b743dSConrad Meyer  * Configure, Rebuild, Remove, Deactivate, Forget, and Stop operations do not
46844b743dSConrad Meyer  * seem to depend on any particular g_mirror initialization state.
47844b743dSConrad Meyer  */
48fa4a1febSPawel Jakub Dawidek static struct g_mirror_softc *
g_mirror_find_device(struct g_class * mp,const char * name)49fa4a1febSPawel Jakub Dawidek g_mirror_find_device(struct g_class *mp, const char *name)
50fa4a1febSPawel Jakub Dawidek {
51fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
52fa4a1febSPawel Jakub Dawidek 	struct g_geom *gp;
53fa4a1febSPawel Jakub Dawidek 
54855761d5SPawel Jakub Dawidek 	g_topology_lock();
55fa4a1febSPawel Jakub Dawidek 	LIST_FOREACH(gp, &mp->geom, geom) {
56fa4a1febSPawel Jakub Dawidek 		sc = gp->softc;
57fa4a1febSPawel Jakub Dawidek 		if (sc == NULL)
58fa4a1febSPawel Jakub Dawidek 			continue;
59fa4a1febSPawel Jakub Dawidek 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
60fa4a1febSPawel Jakub Dawidek 			continue;
61fa4a1febSPawel Jakub Dawidek 		if (strcmp(gp->name, name) == 0 ||
62fa4a1febSPawel Jakub Dawidek 		    strcmp(sc->sc_name, name) == 0) {
63855761d5SPawel Jakub Dawidek 			g_topology_unlock();
64855761d5SPawel Jakub Dawidek 			sx_xlock(&sc->sc_lock);
65844b743dSConrad Meyer 			if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
66844b743dSConrad Meyer 				sx_xunlock(&sc->sc_lock);
67844b743dSConrad Meyer 				return (NULL);
68844b743dSConrad Meyer 			}
69fa4a1febSPawel Jakub Dawidek 			return (sc);
70fa4a1febSPawel Jakub Dawidek 		}
71fa4a1febSPawel Jakub Dawidek 	}
72855761d5SPawel Jakub Dawidek 	g_topology_unlock();
73fa4a1febSPawel Jakub Dawidek 	return (NULL);
74fa4a1febSPawel Jakub Dawidek }
75fa4a1febSPawel Jakub Dawidek 
76844b743dSConrad Meyer /* Insert and Resize operations depend on a launched GEOM (sc_provider). */
77844b743dSConrad Meyer #define	GMFL_VALID_FLAGS	(M_WAITOK | M_NOWAIT)
78844b743dSConrad Meyer static struct g_mirror_softc *
g_mirror_find_launched_device(struct g_class * mp,const char * name,int flags)79844b743dSConrad Meyer g_mirror_find_launched_device(struct g_class *mp, const char *name, int flags)
80844b743dSConrad Meyer {
81844b743dSConrad Meyer 	struct g_mirror_softc *sc;
82844b743dSConrad Meyer 	int error;
83844b743dSConrad Meyer 
84844b743dSConrad Meyer 	KASSERT((flags & ~GMFL_VALID_FLAGS) == 0 &&
85844b743dSConrad Meyer 	    flags != GMFL_VALID_FLAGS && flags != 0,
86844b743dSConrad Meyer 	    ("%s: Invalid flags %x\n", __func__, (unsigned)flags));
87844b743dSConrad Meyer #undef	GMFL_VALID_FLAGS
88844b743dSConrad Meyer 
89844b743dSConrad Meyer 	while (true) {
90844b743dSConrad Meyer 		sc = g_mirror_find_device(mp, name);
91844b743dSConrad Meyer 		if (sc == NULL)
92844b743dSConrad Meyer 			return (NULL);
93844b743dSConrad Meyer 		if (sc->sc_provider != NULL)
94844b743dSConrad Meyer 			return (sc);
95844b743dSConrad Meyer 		if (flags & M_NOWAIT) {
96844b743dSConrad Meyer 			sx_xunlock(&sc->sc_lock);
97844b743dSConrad Meyer 			return (NULL);
98844b743dSConrad Meyer 		}
99844b743dSConrad Meyer 
100844b743dSConrad Meyer 		/*
101844b743dSConrad Meyer 		 * This is a dumb hack.  G_mirror does not expose any real
102844b743dSConrad Meyer 		 * wakeup API for observing state changes, and even if it did,
103844b743dSConrad Meyer 		 * its "RUNNING" state does not actually reflect all softc
104844b743dSConrad Meyer 		 * elements being initialized.
105844b743dSConrad Meyer 		 *
106844b743dSConrad Meyer 		 * Revamping g_mirror to have a 3rd, ACTUALLY_RUNNING state and
107844b743dSConrad Meyer 		 * updating all assertions and sc_state checks is a large work
108844b743dSConrad Meyer 		 * and would be easy to introduce regressions.
109844b743dSConrad Meyer 		 *
110844b743dSConrad Meyer 		 * Revamping g_mirror to have a wakeup for state changes would
111844b743dSConrad Meyer 		 * be difficult if one wanted to capture more than just
112844b743dSConrad Meyer 		 * sc_state and sc_provider.
113844b743dSConrad Meyer 		 *
114844b743dSConrad Meyer 		 * For now, just dummy sleep-poll until sc_provider shows up,
115844b743dSConrad Meyer 		 * the user cancels, or the g_mirror is destroyed.
116844b743dSConrad Meyer 		 */
117844b743dSConrad Meyer 		error = sx_sleep(&sc, &sc->sc_lock, PRIBIO | PCATCH | PDROP,
118844b743dSConrad Meyer 		    "GM:launched", 1);
119844b743dSConrad Meyer 		if (error != 0 && error != EWOULDBLOCK)
120844b743dSConrad Meyer 			return (NULL);
121844b743dSConrad Meyer 	}
122844b743dSConrad Meyer 	__unreachable();
123844b743dSConrad Meyer }
124844b743dSConrad Meyer 
125fa4a1febSPawel Jakub Dawidek static struct g_mirror_disk *
g_mirror_find_disk(struct g_mirror_softc * sc,const char * name)126fa4a1febSPawel Jakub Dawidek g_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
127fa4a1febSPawel Jakub Dawidek {
128fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
129fa4a1febSPawel Jakub Dawidek 
130855761d5SPawel Jakub Dawidek 	sx_assert(&sc->sc_lock, SX_XLOCKED);
1318510f61aSXin LI 	if (strncmp(name, _PATH_DEV, 5) == 0)
13286ed3c25SPawel Jakub Dawidek 		name += 5;
133fa4a1febSPawel Jakub Dawidek 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
134fa4a1febSPawel Jakub Dawidek 		if (disk->d_consumer == NULL)
135fa4a1febSPawel Jakub Dawidek 			continue;
136fa4a1febSPawel Jakub Dawidek 		if (disk->d_consumer->provider == NULL)
137fa4a1febSPawel Jakub Dawidek 			continue;
138fa4a1febSPawel Jakub Dawidek 		if (strcmp(disk->d_consumer->provider->name, name) == 0)
139fa4a1febSPawel Jakub Dawidek 			return (disk);
140fa4a1febSPawel Jakub Dawidek 	}
141fa4a1febSPawel Jakub Dawidek 	return (NULL);
142fa4a1febSPawel Jakub Dawidek }
143fa4a1febSPawel Jakub Dawidek 
144fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_configure(struct gctl_req * req,struct g_class * mp)145fa4a1febSPawel Jakub Dawidek g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
146fa4a1febSPawel Jakub Dawidek {
147fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
148fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
149b740e905SPawel Jakub Dawidek 	const char *name, *balancep, *prov;
150b740e905SPawel Jakub Dawidek 	intmax_t *slicep, *priority;
151fa4a1febSPawel Jakub Dawidek 	uint32_t slice;
152fa4a1febSPawel Jakub Dawidek 	uint8_t balance;
153501250baSPawel Jakub Dawidek 	int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
154b740e905SPawel Jakub Dawidek 	int *nargs, do_sync = 0, dirty = 1, do_priority = 0;
155fa4a1febSPawel Jakub Dawidek 
156fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
157cc6aa917SPawel Jakub Dawidek 	if (nargs == NULL) {
158cc6aa917SPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
159cc6aa917SPawel Jakub Dawidek 		return;
160cc6aa917SPawel Jakub Dawidek 	}
161b740e905SPawel Jakub Dawidek 	if (*nargs != 1 && *nargs != 2) {
162fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Invalid number of arguments.");
163fa4a1febSPawel Jakub Dawidek 		return;
164fa4a1febSPawel Jakub Dawidek 	}
165fa4a1febSPawel Jakub Dawidek 	name = gctl_get_asciiparam(req, "arg0");
166cc6aa917SPawel Jakub Dawidek 	if (name == NULL) {
167cc6aa917SPawel Jakub Dawidek 		gctl_error(req, "No 'arg%u' argument.", 0);
168cc6aa917SPawel Jakub Dawidek 		return;
169cc6aa917SPawel Jakub Dawidek 	}
170fa4a1febSPawel Jakub Dawidek 	balancep = gctl_get_asciiparam(req, "balance");
171cc6aa917SPawel Jakub Dawidek 	if (balancep == NULL) {
172cc6aa917SPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "balance");
173cc6aa917SPawel Jakub Dawidek 		return;
174cc6aa917SPawel Jakub Dawidek 	}
175fa4a1febSPawel Jakub Dawidek 	autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
176fa4a1febSPawel Jakub Dawidek 	if (autosync == NULL) {
177fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "autosync");
178fa4a1febSPawel Jakub Dawidek 		return;
179fa4a1febSPawel Jakub Dawidek 	}
180fa4a1febSPawel Jakub Dawidek 	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
181fa4a1febSPawel Jakub Dawidek 	if (noautosync == NULL) {
182fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "noautosync");
183fa4a1febSPawel Jakub Dawidek 		return;
184fa4a1febSPawel Jakub Dawidek 	}
185501250baSPawel Jakub Dawidek 	failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
186501250baSPawel Jakub Dawidek 	if (failsync == NULL) {
187501250baSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "failsync");
188501250baSPawel Jakub Dawidek 		return;
189501250baSPawel Jakub Dawidek 	}
190501250baSPawel Jakub Dawidek 	nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
191501250baSPawel Jakub Dawidek 	if (nofailsync == NULL) {
192501250baSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nofailsync");
193501250baSPawel Jakub Dawidek 		return;
194501250baSPawel Jakub Dawidek 	}
195c38d2f4eSPawel Jakub Dawidek 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
196c38d2f4eSPawel Jakub Dawidek 	if (hardcode == NULL) {
197c38d2f4eSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "hardcode");
198c38d2f4eSPawel Jakub Dawidek 		return;
199c38d2f4eSPawel Jakub Dawidek 	}
200c38d2f4eSPawel Jakub Dawidek 	dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
201c38d2f4eSPawel Jakub Dawidek 	if (dynamic == NULL) {
202c38d2f4eSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "dynamic");
203c38d2f4eSPawel Jakub Dawidek 		return;
204c38d2f4eSPawel Jakub Dawidek 	}
205b740e905SPawel Jakub Dawidek 	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
206b740e905SPawel Jakub Dawidek 	if (priority == NULL) {
207b740e905SPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "priority");
208b740e905SPawel Jakub Dawidek 		return;
209b740e905SPawel Jakub Dawidek 	}
210b740e905SPawel Jakub Dawidek 	if (*priority < -1 || *priority > 255) {
211b740e905SPawel Jakub Dawidek 		gctl_error(req, "Priority range is 0 to 255, %jd given",
212b740e905SPawel Jakub Dawidek 		    *priority);
213b740e905SPawel Jakub Dawidek 		return;
214b740e905SPawel Jakub Dawidek 	}
215b740e905SPawel Jakub Dawidek 	/*
216b740e905SPawel Jakub Dawidek 	 * Since we have a priority, we also need a provider now.
217b740e905SPawel Jakub Dawidek 	 * Note: be WARNS safe, by always assigning prov and only throw an
218b740e905SPawel Jakub Dawidek 	 * error if *priority != -1.
219b740e905SPawel Jakub Dawidek 	 */
220b740e905SPawel Jakub Dawidek 	prov = gctl_get_asciiparam(req, "arg1");
221b740e905SPawel Jakub Dawidek 	if (*priority > -1) {
222b740e905SPawel Jakub Dawidek 		if (prov == NULL) {
223b740e905SPawel Jakub Dawidek 			gctl_error(req, "Priority needs a disk name");
224b740e905SPawel Jakub Dawidek 			return;
225b740e905SPawel Jakub Dawidek 		}
226b740e905SPawel Jakub Dawidek 		do_priority = 1;
227b740e905SPawel Jakub Dawidek 	}
228fa4a1febSPawel Jakub Dawidek 	if (*autosync && *noautosync) {
229fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "'%s' and '%s' specified.", "autosync",
230fa4a1febSPawel Jakub Dawidek 		    "noautosync");
231fa4a1febSPawel Jakub Dawidek 		return;
232fa4a1febSPawel Jakub Dawidek 	}
233501250baSPawel Jakub Dawidek 	if (*failsync && *nofailsync) {
234501250baSPawel Jakub Dawidek 		gctl_error(req, "'%s' and '%s' specified.", "failsync",
235501250baSPawel Jakub Dawidek 		    "nofailsync");
236501250baSPawel Jakub Dawidek 		return;
237501250baSPawel Jakub Dawidek 	}
238c38d2f4eSPawel Jakub Dawidek 	if (*hardcode && *dynamic) {
239c38d2f4eSPawel Jakub Dawidek 		gctl_error(req, "'%s' and '%s' specified.", "hardcode",
240c38d2f4eSPawel Jakub Dawidek 		    "dynamic");
241c38d2f4eSPawel Jakub Dawidek 		return;
242c38d2f4eSPawel Jakub Dawidek 	}
243855761d5SPawel Jakub Dawidek 	sc = g_mirror_find_device(mp, name);
244855761d5SPawel Jakub Dawidek 	if (sc == NULL) {
245855761d5SPawel Jakub Dawidek 		gctl_error(req, "No such device: %s.", name);
246855761d5SPawel Jakub Dawidek 		return;
247855761d5SPawel Jakub Dawidek 	}
248a478ea74SPawel Jakub Dawidek 	if (*balancep == '\0')
249855761d5SPawel Jakub Dawidek 		balance = sc->sc_balance;
250855761d5SPawel Jakub Dawidek 	else {
251855761d5SPawel Jakub Dawidek 		if (balance_id(balancep) == -1) {
252855761d5SPawel Jakub Dawidek 			gctl_error(req, "Invalid balance algorithm.");
253855761d5SPawel Jakub Dawidek 			sx_xunlock(&sc->sc_lock);
254855761d5SPawel Jakub Dawidek 			return;
255855761d5SPawel Jakub Dawidek 		}
256855761d5SPawel Jakub Dawidek 		balance = balance_id(balancep);
257855761d5SPawel Jakub Dawidek 	}
258855761d5SPawel Jakub Dawidek 	slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
259855761d5SPawel Jakub Dawidek 	if (slicep == NULL) {
260855761d5SPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "slice");
261855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
262855761d5SPawel Jakub Dawidek 		return;
263855761d5SPawel Jakub Dawidek 	}
264855761d5SPawel Jakub Dawidek 	if (*slicep == -1)
265855761d5SPawel Jakub Dawidek 		slice = sc->sc_slice;
266855761d5SPawel Jakub Dawidek 	else
267855761d5SPawel Jakub Dawidek 		slice = *slicep;
268b740e905SPawel Jakub Dawidek 	/* Enforce usage() of -p not allowing any other options. */
269b740e905SPawel Jakub Dawidek 	if (do_priority && (*autosync || *noautosync || *failsync ||
270b740e905SPawel Jakub Dawidek 	    *nofailsync || *hardcode || *dynamic || *slicep != -1 ||
271a478ea74SPawel Jakub Dawidek 	    *balancep != '\0')) {
272855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
273b740e905SPawel Jakub Dawidek 		gctl_error(req, "only -p accepted when setting priority");
274855761d5SPawel Jakub Dawidek 		return;
275855761d5SPawel Jakub Dawidek 	}
276855761d5SPawel Jakub Dawidek 	if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
277501250baSPawel Jakub Dawidek 	    !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
278b740e905SPawel Jakub Dawidek 	    !*dynamic && !do_priority) {
279855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
280855761d5SPawel Jakub Dawidek 		gctl_error(req, "Nothing has changed.");
281855761d5SPawel Jakub Dawidek 		return;
282855761d5SPawel Jakub Dawidek 	}
283b740e905SPawel Jakub Dawidek 	if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) {
284b740e905SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
285b740e905SPawel Jakub Dawidek 		gctl_error(req, "Invalid number of arguments.");
286b740e905SPawel Jakub Dawidek 		return;
287b740e905SPawel Jakub Dawidek 	}
288b740e905SPawel Jakub Dawidek 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
289b740e905SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
290b740e905SPawel Jakub Dawidek 		gctl_error(req, "Not all disks connected. Try 'forget' command "
291b740e905SPawel Jakub Dawidek 		    "first.");
292b740e905SPawel Jakub Dawidek 		return;
293b740e905SPawel Jakub Dawidek 	}
294fa4a1febSPawel Jakub Dawidek 	sc->sc_balance = balance;
295fa4a1febSPawel Jakub Dawidek 	sc->sc_slice = slice;
296fa4a1febSPawel Jakub Dawidek 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
297fa4a1febSPawel Jakub Dawidek 		if (*autosync) {
298fa4a1febSPawel Jakub Dawidek 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
299fa4a1febSPawel Jakub Dawidek 			do_sync = 1;
300fa4a1febSPawel Jakub Dawidek 		}
301fa4a1febSPawel Jakub Dawidek 	} else {
302fa4a1febSPawel Jakub Dawidek 		if (*noautosync)
303fa4a1febSPawel Jakub Dawidek 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
304fa4a1febSPawel Jakub Dawidek 	}
305501250baSPawel Jakub Dawidek 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
306501250baSPawel Jakub Dawidek 		if (*failsync)
307501250baSPawel Jakub Dawidek 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
308501250baSPawel Jakub Dawidek 	} else {
309501250baSPawel Jakub Dawidek 		if (*nofailsync) {
310501250baSPawel Jakub Dawidek 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
311501250baSPawel Jakub Dawidek 			dirty = 0;
312501250baSPawel Jakub Dawidek 		}
313501250baSPawel Jakub Dawidek 	}
314fa4a1febSPawel Jakub Dawidek 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
315b740e905SPawel Jakub Dawidek 		/*
316b740e905SPawel Jakub Dawidek 		 * Handle priority first, since we only need one disk, do one
317b740e905SPawel Jakub Dawidek 		 * operation on it and then we're done. No need to check other
318b740e905SPawel Jakub Dawidek 		 * flags, as usage doesn't allow it.
319b740e905SPawel Jakub Dawidek 		 */
320b740e905SPawel Jakub Dawidek 		if (do_priority) {
321b740e905SPawel Jakub Dawidek 			if (strcmp(disk->d_name, prov) == 0) {
322b740e905SPawel Jakub Dawidek 				if (disk->d_priority == *priority)
323b740e905SPawel Jakub Dawidek 					gctl_error(req, "Nothing has changed.");
324b740e905SPawel Jakub Dawidek 				else {
325b740e905SPawel Jakub Dawidek 					disk->d_priority = *priority;
326b740e905SPawel Jakub Dawidek 					g_mirror_update_metadata(disk);
327b740e905SPawel Jakub Dawidek 				}
328b740e905SPawel Jakub Dawidek 				break;
329b740e905SPawel Jakub Dawidek 			}
330b740e905SPawel Jakub Dawidek 			continue;
331b740e905SPawel Jakub Dawidek 		}
332fa4a1febSPawel Jakub Dawidek 		if (do_sync) {
333fa4a1febSPawel Jakub Dawidek 			if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
334fa4a1febSPawel Jakub Dawidek 				disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
335fa4a1febSPawel Jakub Dawidek 		}
336c38d2f4eSPawel Jakub Dawidek 		if (*hardcode)
337c38d2f4eSPawel Jakub Dawidek 			disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
338c38d2f4eSPawel Jakub Dawidek 		else if (*dynamic)
339c38d2f4eSPawel Jakub Dawidek 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
340501250baSPawel Jakub Dawidek 		if (!dirty)
341501250baSPawel Jakub Dawidek 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
342fa4a1febSPawel Jakub Dawidek 		g_mirror_update_metadata(disk);
343fa4a1febSPawel Jakub Dawidek 		if (do_sync) {
344fa4a1febSPawel Jakub Dawidek 			if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
345fa4a1febSPawel Jakub Dawidek 				g_mirror_event_send(disk,
346fa4a1febSPawel Jakub Dawidek 				    G_MIRROR_DISK_STATE_DISCONNECTED,
347fa4a1febSPawel Jakub Dawidek 				    G_MIRROR_EVENT_DONTWAIT);
348fa4a1febSPawel Jakub Dawidek 			}
349fa4a1febSPawel Jakub Dawidek 		}
350fa4a1febSPawel Jakub Dawidek 	}
351855761d5SPawel Jakub Dawidek 	sx_xunlock(&sc->sc_lock);
352fa4a1febSPawel Jakub Dawidek }
353fa4a1febSPawel Jakub Dawidek 
354fa4a1febSPawel Jakub Dawidek static void
g_mirror_create_orphan(struct g_consumer * cp)355b6fe583cSAlexander Motin g_mirror_create_orphan(struct g_consumer *cp)
356b6fe583cSAlexander Motin {
357b6fe583cSAlexander Motin 
358b6fe583cSAlexander Motin 	KASSERT(1 == 0, ("%s called while creating %s.", __func__,
359b6fe583cSAlexander Motin 	    cp->provider->name));
360b6fe583cSAlexander Motin }
361b6fe583cSAlexander Motin 
362b6fe583cSAlexander Motin static void
g_mirror_ctl_create(struct gctl_req * req,struct g_class * mp)363b6fe583cSAlexander Motin g_mirror_ctl_create(struct gctl_req *req, struct g_class *mp)
364b6fe583cSAlexander Motin {
365b6fe583cSAlexander Motin 	struct g_mirror_metadata md;
366b6fe583cSAlexander Motin 	struct g_geom *gp;
367b6fe583cSAlexander Motin 	struct g_consumer *cp;
368b6fe583cSAlexander Motin 	struct g_provider *pp;
369b6fe583cSAlexander Motin 	struct g_mirror_softc *sc;
370b6fe583cSAlexander Motin 	struct sbuf *sb;
371b6fe583cSAlexander Motin 	const char *name;
372b6fe583cSAlexander Motin 	char param[16];
373b6fe583cSAlexander Motin 	int *nargs;
374b6fe583cSAlexander Motin 	intmax_t *val;
375b6fe583cSAlexander Motin 	int *ival;
376b6fe583cSAlexander Motin 	const char *sval;
377b6fe583cSAlexander Motin 	int bal;
378b6fe583cSAlexander Motin 	unsigned attached, no, sectorsize;
379b6fe583cSAlexander Motin 	off_t mediasize;
380b6fe583cSAlexander Motin 
381b6fe583cSAlexander Motin 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
382b6fe583cSAlexander Motin 	if (nargs == NULL) {
383b6fe583cSAlexander Motin 		gctl_error(req, "No '%s' argument.", "nargs");
384b6fe583cSAlexander Motin 		return;
385b6fe583cSAlexander Motin 	}
386b6fe583cSAlexander Motin 	if (*nargs <= 2) {
387b6fe583cSAlexander Motin 		gctl_error(req, "Too few arguments.");
388b6fe583cSAlexander Motin 		return;
389b6fe583cSAlexander Motin 	}
390b6fe583cSAlexander Motin 
391b6fe583cSAlexander Motin 	strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
392b6fe583cSAlexander Motin 	md.md_version = G_MIRROR_VERSION;
393b6fe583cSAlexander Motin 	name = gctl_get_asciiparam(req, "arg0");
394b6fe583cSAlexander Motin 	if (name == NULL) {
395b6fe583cSAlexander Motin 		gctl_error(req, "No 'arg%u' argument.", 0);
396b6fe583cSAlexander Motin 		return;
397b6fe583cSAlexander Motin 	}
398b6fe583cSAlexander Motin 	strlcpy(md.md_name, name, sizeof(md.md_name));
399b6fe583cSAlexander Motin 	md.md_mid = arc4random();
400b6fe583cSAlexander Motin 	md.md_all = *nargs - 1;
401b6fe583cSAlexander Motin 	md.md_genid = 0;
402b6fe583cSAlexander Motin 	md.md_syncid = 1;
403b6fe583cSAlexander Motin 	md.md_sync_offset = 0;
404b6fe583cSAlexander Motin 	val = gctl_get_paraml(req, "slice", sizeof(*val));
405b6fe583cSAlexander Motin 	if (val == NULL) {
406b6fe583cSAlexander Motin 		gctl_error(req, "No slice argument.");
407b6fe583cSAlexander Motin 		return;
408b6fe583cSAlexander Motin 	}
409b6fe583cSAlexander Motin 	md.md_slice = *val;
410b6fe583cSAlexander Motin 	sval = gctl_get_asciiparam(req, "balance");
411b6fe583cSAlexander Motin 	if (sval == NULL) {
412b6fe583cSAlexander Motin 		gctl_error(req, "No balance argument.");
413b6fe583cSAlexander Motin 		return;
414b6fe583cSAlexander Motin 	}
415b6fe583cSAlexander Motin 	bal = balance_id(sval);
416b6fe583cSAlexander Motin 	if (bal < 0) {
417b6fe583cSAlexander Motin 		gctl_error(req, "Invalid balance algorithm.");
418b6fe583cSAlexander Motin 		return;
419b6fe583cSAlexander Motin 	}
420b6fe583cSAlexander Motin 	md.md_balance = bal;
421b6fe583cSAlexander Motin 	md.md_mflags = 0;
422b6fe583cSAlexander Motin 	md.md_dflags = 0;
423b6fe583cSAlexander Motin 	ival = gctl_get_paraml(req, "noautosync", sizeof(*ival));
424b6fe583cSAlexander Motin 	if (ival != NULL && *ival)
425b6fe583cSAlexander Motin 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
426b6fe583cSAlexander Motin 	ival = gctl_get_paraml(req, "nofailsync", sizeof(*ival));
427b6fe583cSAlexander Motin 	if (ival != NULL && *ival)
428b6fe583cSAlexander Motin 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
429b6fe583cSAlexander Motin 	/* These fields not used in manual mode. */
430b6fe583cSAlexander Motin 	bzero(md.md_provider, sizeof(md.md_provider));
431b6fe583cSAlexander Motin 	md.md_provsize = 0;
432b6fe583cSAlexander Motin 
433b6fe583cSAlexander Motin 	g_topology_lock();
434b6fe583cSAlexander Motin 	mediasize = OFF_MAX;
435b6fe583cSAlexander Motin 	sectorsize = 0;
436b6fe583cSAlexander Motin 	gp = g_new_geomf(mp, "%s", md.md_name);
437b6fe583cSAlexander Motin 	gp->orphan = g_mirror_create_orphan;
438b6fe583cSAlexander Motin 	cp = g_new_consumer(gp);
439b6fe583cSAlexander Motin 	for (no = 1; no < *nargs; no++) {
440b6fe583cSAlexander Motin 		snprintf(param, sizeof(param), "arg%u", no);
441fcf69f3dSXin LI 		pp = gctl_get_provider(req, param);
442fcf69f3dSXin LI 		if (pp == NULL) {
443b6fe583cSAlexander Motin err:
444b6fe583cSAlexander Motin 			g_destroy_consumer(cp);
445b6fe583cSAlexander Motin 			g_destroy_geom(gp);
446b6fe583cSAlexander Motin 			g_topology_unlock();
447b6fe583cSAlexander Motin 			return;
448b6fe583cSAlexander Motin 		}
449d22ff249SEdward Tomasz Napierala 		if (g_attach(cp, pp) != 0) {
450d22ff249SEdward Tomasz Napierala 			G_MIRROR_DEBUG(1, "Can't attach disk %s.", pp->name);
451d22ff249SEdward Tomasz Napierala 			gctl_error(req, "Can't attach disk %s.", pp->name);
452d22ff249SEdward Tomasz Napierala 			goto err;
453d22ff249SEdward Tomasz Napierala 		}
454b6fe583cSAlexander Motin 		if (g_access(cp, 1, 0, 0) != 0) {
455fcf69f3dSXin LI 			G_MIRROR_DEBUG(1, "Can't open disk %s.", pp->name);
456fcf69f3dSXin LI 			gctl_error(req, "Can't open disk %s.", pp->name);
457b6fe583cSAlexander Motin err2:
458b6fe583cSAlexander Motin 			g_detach(cp);
459b6fe583cSAlexander Motin 			goto err;
460b6fe583cSAlexander Motin 		}
461b6fe583cSAlexander Motin 		if (pp->mediasize == 0 || pp->sectorsize == 0) {
462fcf69f3dSXin LI 			G_MIRROR_DEBUG(1, "Disk %s has no media.", pp->name);
463fcf69f3dSXin LI 			gctl_error(req, "Disk %s has no media.", pp->name);
464b6fe583cSAlexander Motin 			g_access(cp, -1, 0, 0);
465b6fe583cSAlexander Motin 			goto err2;
466b6fe583cSAlexander Motin 		}
467b6fe583cSAlexander Motin 		if (pp->mediasize < mediasize)
468b6fe583cSAlexander Motin 			mediasize = pp->mediasize;
469b6fe583cSAlexander Motin 		if (pp->sectorsize > sectorsize)
470b6fe583cSAlexander Motin 			sectorsize = pp->sectorsize;
471b6fe583cSAlexander Motin 		g_access(cp, -1, 0, 0);
472b6fe583cSAlexander Motin 		g_detach(cp);
473b6fe583cSAlexander Motin 	}
474b6fe583cSAlexander Motin 	g_destroy_consumer(cp);
475b6fe583cSAlexander Motin 	g_destroy_geom(gp);
476b6fe583cSAlexander Motin 	md.md_mediasize = mediasize;
477b6fe583cSAlexander Motin 	md.md_sectorsize = sectorsize;
478b6fe583cSAlexander Motin 	md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
479b6fe583cSAlexander Motin 
480b6fe583cSAlexander Motin 	gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_MANUAL);
481b6fe583cSAlexander Motin 	if (gp == NULL) {
482b6fe583cSAlexander Motin 		gctl_error(req, "Can't create %s.", md.md_name);
483b6fe583cSAlexander Motin 		g_topology_unlock();
484b6fe583cSAlexander Motin 		return;
485b6fe583cSAlexander Motin 	}
486b6fe583cSAlexander Motin 
487b6fe583cSAlexander Motin 	sc = gp->softc;
488b6fe583cSAlexander Motin 	g_topology_unlock();
489b6fe583cSAlexander Motin 	sx_xlock(&sc->sc_lock);
490b6fe583cSAlexander Motin 	sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING;
491b6fe583cSAlexander Motin 	sb = sbuf_new_auto();
492b6fe583cSAlexander Motin 	sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
493b6fe583cSAlexander Motin 	for (attached = 0, no = 1; no < *nargs; no++) {
494b6fe583cSAlexander Motin 		snprintf(param, sizeof(param), "arg%u", no);
495fcf69f3dSXin LI 		pp = gctl_get_provider(req, param);
496b6fe583cSAlexander Motin 		if (pp == NULL) {
497fcf69f3dSXin LI 			name = gctl_get_asciiparam(req, param);
498fcf69f3dSXin LI 			MPASS(name != NULL);
499b6fe583cSAlexander Motin 			sbuf_printf(sb, " %s", name);
500b6fe583cSAlexander Motin 			continue;
501b6fe583cSAlexander Motin 		}
502b6fe583cSAlexander Motin 		md.md_did = arc4random();
503b6fe583cSAlexander Motin 		md.md_priority = no - 1;
504b6fe583cSAlexander Motin 		if (g_mirror_add_disk(sc, pp, &md) != 0) {
505b6fe583cSAlexander Motin 			G_MIRROR_DEBUG(1, "Disk %u (%s) not attached to %s.",
506b6fe583cSAlexander Motin 			    no, pp->name, gp->name);
507b6fe583cSAlexander Motin 			sbuf_printf(sb, " %s", pp->name);
508b6fe583cSAlexander Motin 			continue;
509b6fe583cSAlexander Motin 		}
510b6fe583cSAlexander Motin 		attached++;
511b6fe583cSAlexander Motin 	}
512b6fe583cSAlexander Motin 	sbuf_finish(sb);
513b6fe583cSAlexander Motin 	sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING;
514b6fe583cSAlexander Motin 	if (md.md_all != attached ||
515b6fe583cSAlexander Motin 	    (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
516b6fe583cSAlexander Motin 		g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_HARD);
517b6fe583cSAlexander Motin 		gctl_error(req, "%s", sbuf_data(sb));
518b6fe583cSAlexander Motin 	} else
519b6fe583cSAlexander Motin 		sx_xunlock(&sc->sc_lock);
520b6fe583cSAlexander Motin 	sbuf_delete(sb);
521b6fe583cSAlexander Motin }
522b6fe583cSAlexander Motin 
523b6fe583cSAlexander Motin static void
g_mirror_ctl_rebuild(struct gctl_req * req,struct g_class * mp)524fa4a1febSPawel Jakub Dawidek g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
525fa4a1febSPawel Jakub Dawidek {
526fd6d3120SPawel Jakub Dawidek 	struct g_mirror_metadata md;
527fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
528fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
529fd6d3120SPawel Jakub Dawidek 	struct g_provider *pp;
530fa4a1febSPawel Jakub Dawidek 	const char *name;
531fa4a1febSPawel Jakub Dawidek 	char param[16];
532fd6d3120SPawel Jakub Dawidek 	int error, *nargs;
533fa4a1febSPawel Jakub Dawidek 	u_int i;
534fa4a1febSPawel Jakub Dawidek 
535fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
536fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
537fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
538fa4a1febSPawel Jakub Dawidek 		return;
539fa4a1febSPawel Jakub Dawidek 	}
540fa4a1febSPawel Jakub Dawidek 	if (*nargs < 2) {
541fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Too few arguments.");
542fa4a1febSPawel Jakub Dawidek 		return;
543fa4a1febSPawel Jakub Dawidek 	}
544fa4a1febSPawel Jakub Dawidek 	name = gctl_get_asciiparam(req, "arg0");
545fa4a1febSPawel Jakub Dawidek 	if (name == NULL) {
546fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No 'arg%u' argument.", 0);
547fa4a1febSPawel Jakub Dawidek 		return;
548fa4a1febSPawel Jakub Dawidek 	}
549fa4a1febSPawel Jakub Dawidek 	sc = g_mirror_find_device(mp, name);
550fa4a1febSPawel Jakub Dawidek 	if (sc == NULL) {
551fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No such device: %s.", name);
552fa4a1febSPawel Jakub Dawidek 		return;
553fa4a1febSPawel Jakub Dawidek 	}
554fa4a1febSPawel Jakub Dawidek 	for (i = 1; i < (u_int)*nargs; i++) {
555fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
556fa4a1febSPawel Jakub Dawidek 		name = gctl_get_asciiparam(req, param);
557fa4a1febSPawel Jakub Dawidek 		if (name == NULL) {
558fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No 'arg%u' argument.", i);
559c37e2f9bSPawel Jakub Dawidek 			continue;
560fa4a1febSPawel Jakub Dawidek 		}
561fa4a1febSPawel Jakub Dawidek 		disk = g_mirror_find_disk(sc, name);
562fa4a1febSPawel Jakub Dawidek 		if (disk == NULL) {
563fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No such provider: %s.", name);
564c37e2f9bSPawel Jakub Dawidek 			continue;
565fa4a1febSPawel Jakub Dawidek 		}
566fa4a1febSPawel Jakub Dawidek 		if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
567fa4a1febSPawel Jakub Dawidek 		    disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
568fa4a1febSPawel Jakub Dawidek 			/*
569fa4a1febSPawel Jakub Dawidek 			 * This is the last active disk. There will be nothing
570fa4a1febSPawel Jakub Dawidek 			 * to rebuild it from, so deny this request.
571fa4a1febSPawel Jakub Dawidek 			 */
572fa4a1febSPawel Jakub Dawidek 			gctl_error(req,
573fa4a1febSPawel Jakub Dawidek 			    "Provider %s is the last active provider in %s.",
574fa4a1febSPawel Jakub Dawidek 			    name, sc->sc_geom->name);
575855761d5SPawel Jakub Dawidek 			break;
576fa4a1febSPawel Jakub Dawidek 		}
577fa4a1febSPawel Jakub Dawidek 		/*
578fd6d3120SPawel Jakub Dawidek 		 * Do rebuild by resetting syncid, disconnecting the disk and
579fd6d3120SPawel Jakub Dawidek 		 * connecting it again.
580fa4a1febSPawel Jakub Dawidek 		 */
581fa4a1febSPawel Jakub Dawidek 		disk->d_sync.ds_syncid = 0;
582fa4a1febSPawel Jakub Dawidek 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
583fa4a1febSPawel Jakub Dawidek 			disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
584fa4a1febSPawel Jakub Dawidek 		g_mirror_update_metadata(disk);
585fd6d3120SPawel Jakub Dawidek 		pp = disk->d_consumer->provider;
586855761d5SPawel Jakub Dawidek 		g_topology_lock();
587fd6d3120SPawel Jakub Dawidek 		error = g_mirror_read_metadata(disk->d_consumer, &md);
588855761d5SPawel Jakub Dawidek 		g_topology_unlock();
589fa4a1febSPawel Jakub Dawidek 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
590fa4a1febSPawel Jakub Dawidek 		    G_MIRROR_EVENT_WAIT);
591fd6d3120SPawel Jakub Dawidek 		if (error != 0) {
592fd6d3120SPawel Jakub Dawidek 			gctl_error(req, "Cannot read metadata from %s.",
593fd6d3120SPawel Jakub Dawidek 			    pp->name);
594fd6d3120SPawel Jakub Dawidek 			continue;
595fd6d3120SPawel Jakub Dawidek 		}
596fd6d3120SPawel Jakub Dawidek 		error = g_mirror_add_disk(sc, pp, &md);
597fd6d3120SPawel Jakub Dawidek 		if (error != 0) {
598fd6d3120SPawel Jakub Dawidek 			gctl_error(req, "Cannot reconnect component %s.",
599fd6d3120SPawel Jakub Dawidek 			    pp->name);
600fd6d3120SPawel Jakub Dawidek 			continue;
601fd6d3120SPawel Jakub Dawidek 		}
602fa4a1febSPawel Jakub Dawidek 	}
603855761d5SPawel Jakub Dawidek 	sx_xunlock(&sc->sc_lock);
604fa4a1febSPawel Jakub Dawidek }
605fa4a1febSPawel Jakub Dawidek 
606fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_insert(struct gctl_req * req,struct g_class * mp)607fa4a1febSPawel Jakub Dawidek g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
608fa4a1febSPawel Jakub Dawidek {
609fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
610fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
611fa4a1febSPawel Jakub Dawidek 	struct g_mirror_metadata md;
612fa4a1febSPawel Jakub Dawidek 	struct g_provider *pp;
613fa4a1febSPawel Jakub Dawidek 	struct g_consumer *cp;
61455d6eb9fSPawel Jakub Dawidek 	intmax_t *priority;
615fa4a1febSPawel Jakub Dawidek 	const char *name;
616fa4a1febSPawel Jakub Dawidek 	char param[16];
617fa4a1febSPawel Jakub Dawidek 	u_char *sector;
618fa4a1febSPawel Jakub Dawidek 	u_int i, n;
619c38d2f4eSPawel Jakub Dawidek 	int error, *nargs, *hardcode, *inactive;
620fa4a1febSPawel Jakub Dawidek 	struct {
621fa4a1febSPawel Jakub Dawidek 		struct g_provider	*provider;
622fa4a1febSPawel Jakub Dawidek 		struct g_consumer	*consumer;
623fa4a1febSPawel Jakub Dawidek 	} *disks;
624b6fe583cSAlexander Motin 	off_t mdsize;
625fa4a1febSPawel Jakub Dawidek 
626fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
627fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
628fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
629fa4a1febSPawel Jakub Dawidek 		return;
630fa4a1febSPawel Jakub Dawidek 	}
631fa4a1febSPawel Jakub Dawidek 	if (*nargs < 2) {
632fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Too few arguments.");
633fa4a1febSPawel Jakub Dawidek 		return;
634fa4a1febSPawel Jakub Dawidek 	}
63555d6eb9fSPawel Jakub Dawidek 	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
63655d6eb9fSPawel Jakub Dawidek 	if (priority == NULL) {
63755d6eb9fSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "priority");
63855d6eb9fSPawel Jakub Dawidek 		return;
63955d6eb9fSPawel Jakub Dawidek 	}
640fa4a1febSPawel Jakub Dawidek 	inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
641fa4a1febSPawel Jakub Dawidek 	if (inactive == NULL) {
642fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "inactive");
643fa4a1febSPawel Jakub Dawidek 		return;
644fa4a1febSPawel Jakub Dawidek 	}
645c38d2f4eSPawel Jakub Dawidek 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
646c38d2f4eSPawel Jakub Dawidek 	if (hardcode == NULL) {
647c38d2f4eSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "hardcode");
648c38d2f4eSPawel Jakub Dawidek 		return;
649c38d2f4eSPawel Jakub Dawidek 	}
650fa4a1febSPawel Jakub Dawidek 	name = gctl_get_asciiparam(req, "arg0");
651fa4a1febSPawel Jakub Dawidek 	if (name == NULL) {
652fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No 'arg%u' argument.", 0);
653fa4a1febSPawel Jakub Dawidek 		return;
654fa4a1febSPawel Jakub Dawidek 	}
655844b743dSConrad Meyer 	sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
656fa4a1febSPawel Jakub Dawidek 	if (sc == NULL) {
657fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No such device: %s.", name);
658fa4a1febSPawel Jakub Dawidek 		return;
659fa4a1febSPawel Jakub Dawidek 	}
660fa4a1febSPawel Jakub Dawidek 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
661fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Not all disks connected.");
662855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
663fa4a1febSPawel Jakub Dawidek 		return;
664fa4a1febSPawel Jakub Dawidek 	}
665fa4a1febSPawel Jakub Dawidek 
666fa4a1febSPawel Jakub Dawidek 	disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
667855761d5SPawel Jakub Dawidek 	g_topology_lock();
668fa4a1febSPawel Jakub Dawidek 	for (i = 1, n = 0; i < (u_int)*nargs; i++) {
669fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
670fcf69f3dSXin LI 		pp = gctl_get_provider(req, param);
671fcf69f3dSXin LI 		if (pp == NULL)
672fa4a1febSPawel Jakub Dawidek 			continue;
673fcf69f3dSXin LI 		if (g_mirror_find_disk(sc, pp->name) != NULL) {
674fcf69f3dSXin LI 			gctl_error(req, "Provider %s already inserted.", pp->name);
675fa4a1febSPawel Jakub Dawidek 			continue;
676fa4a1febSPawel Jakub Dawidek 		}
677fa4a1febSPawel Jakub Dawidek 		cp = g_new_consumer(sc->sc_geom);
678fa4a1febSPawel Jakub Dawidek 		if (g_attach(cp, pp) != 0) {
679fa4a1febSPawel Jakub Dawidek 			g_destroy_consumer(cp);
680fcf69f3dSXin LI 			gctl_error(req, "Cannot attach to provider %s.", pp->name);
681fa4a1febSPawel Jakub Dawidek 			continue;
682fa4a1febSPawel Jakub Dawidek 		}
683fa4a1febSPawel Jakub Dawidek 		if (g_access(cp, 0, 1, 1) != 0) {
684fcf69f3dSXin LI 			gctl_error(req, "Cannot access provider %s.", pp->name);
685b6fe583cSAlexander Motin err:
686fa4a1febSPawel Jakub Dawidek 			g_detach(cp);
687fa4a1febSPawel Jakub Dawidek 			g_destroy_consumer(cp);
688b6fe583cSAlexander Motin 			continue;
689b6fe583cSAlexander Motin 		}
690b6fe583cSAlexander Motin 		mdsize = (sc->sc_type == G_MIRROR_TYPE_AUTOMATIC) ?
691b6fe583cSAlexander Motin 		    pp->sectorsize : 0;
692b6fe583cSAlexander Motin 		if (sc->sc_provider->mediasize > pp->mediasize - mdsize) {
693fcf69f3dSXin LI 			gctl_error(req, "Provider %s too small.", pp->name);
694b6fe583cSAlexander Motin err2:
695b6fe583cSAlexander Motin 			g_access(cp, 0, -1, -1);
696b6fe583cSAlexander Motin 			goto err;
697b6fe583cSAlexander Motin 		}
698b6fe583cSAlexander Motin 		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
699b6fe583cSAlexander Motin 			gctl_error(req, "Invalid sectorsize of provider %s.",
700fcf69f3dSXin LI 			    pp->name);
701b6fe583cSAlexander Motin 			goto err2;
702b6fe583cSAlexander Motin 		}
703b6fe583cSAlexander Motin 		if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) {
704b6fe583cSAlexander Motin 			g_access(cp, 0, -1, -1);
705b6fe583cSAlexander Motin 			g_detach(cp);
706b6fe583cSAlexander Motin 			g_destroy_consumer(cp);
707b6fe583cSAlexander Motin 			g_topology_unlock();
708b6fe583cSAlexander Motin 			sc->sc_ndisks++;
709b6fe583cSAlexander Motin 			g_mirror_fill_metadata(sc, NULL, &md);
710b6fe583cSAlexander Motin 			md.md_priority = *priority;
711b6fe583cSAlexander Motin 			if (*inactive)
712b6fe583cSAlexander Motin 				md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
713b6fe583cSAlexander Motin 			if (g_mirror_add_disk(sc, pp, &md) != 0) {
714b6fe583cSAlexander Motin 				sc->sc_ndisks--;
715fcf69f3dSXin LI 				gctl_error(req, "Disk %s not inserted.", pp->name);
716b6fe583cSAlexander Motin 			}
717b6fe583cSAlexander Motin 			g_topology_lock();
718fa4a1febSPawel Jakub Dawidek 			continue;
719fa4a1febSPawel Jakub Dawidek 		}
720fa4a1febSPawel Jakub Dawidek 		disks[n].provider = pp;
721fa4a1febSPawel Jakub Dawidek 		disks[n].consumer = cp;
722fa4a1febSPawel Jakub Dawidek 		n++;
723fa4a1febSPawel Jakub Dawidek 	}
724fa4a1febSPawel Jakub Dawidek 	if (n == 0) {
725855761d5SPawel Jakub Dawidek 		g_topology_unlock();
726855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
727fa4a1febSPawel Jakub Dawidek 		g_free(disks);
728fa4a1febSPawel Jakub Dawidek 		return;
729fa4a1febSPawel Jakub Dawidek 	}
730fa4a1febSPawel Jakub Dawidek 	sc->sc_ndisks += n;
731fa4a1febSPawel Jakub Dawidek again:
732fa4a1febSPawel Jakub Dawidek 	for (i = 0; i < n; i++) {
733fa4a1febSPawel Jakub Dawidek 		if (disks[i].consumer == NULL)
734fa4a1febSPawel Jakub Dawidek 			continue;
735fa4a1febSPawel Jakub Dawidek 		g_mirror_fill_metadata(sc, NULL, &md);
73655d6eb9fSPawel Jakub Dawidek 		md.md_priority = *priority;
737fa4a1febSPawel Jakub Dawidek 		if (*inactive)
738fa4a1febSPawel Jakub Dawidek 			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
739fa4a1febSPawel Jakub Dawidek 		pp = disks[i].provider;
740c38d2f4eSPawel Jakub Dawidek 		if (*hardcode) {
741c38d2f4eSPawel Jakub Dawidek 			strlcpy(md.md_provider, pp->name,
742c38d2f4eSPawel Jakub Dawidek 			    sizeof(md.md_provider));
743c38d2f4eSPawel Jakub Dawidek 		} else {
744c38d2f4eSPawel Jakub Dawidek 			bzero(md.md_provider, sizeof(md.md_provider));
745c38d2f4eSPawel Jakub Dawidek 		}
7469d793bddSPawel Jakub Dawidek 		md.md_provsize = pp->mediasize;
7477f053a44SMark Johnston 		sector = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
748fa4a1febSPawel Jakub Dawidek 		mirror_metadata_encode(&md, sector);
749fa4a1febSPawel Jakub Dawidek 		error = g_write_data(disks[i].consumer,
750fa4a1febSPawel Jakub Dawidek 		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
751fa4a1febSPawel Jakub Dawidek 		g_free(sector);
752fa4a1febSPawel Jakub Dawidek 		if (error != 0) {
753fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "Cannot store metadata on %s.",
754fa4a1febSPawel Jakub Dawidek 			    pp->name);
755fa4a1febSPawel Jakub Dawidek 			g_access(disks[i].consumer, 0, -1, -1);
756fa4a1febSPawel Jakub Dawidek 			g_detach(disks[i].consumer);
757fa4a1febSPawel Jakub Dawidek 			g_destroy_consumer(disks[i].consumer);
758fa4a1febSPawel Jakub Dawidek 			disks[i].consumer = NULL;
759fa4a1febSPawel Jakub Dawidek 			disks[i].provider = NULL;
760fa4a1febSPawel Jakub Dawidek 			sc->sc_ndisks--;
761fa4a1febSPawel Jakub Dawidek 			goto again;
762fa4a1febSPawel Jakub Dawidek 		}
763fa4a1febSPawel Jakub Dawidek 	}
764855761d5SPawel Jakub Dawidek 	g_topology_unlock();
765fa4a1febSPawel Jakub Dawidek 	if (i == 0) {
766fa4a1febSPawel Jakub Dawidek 		/* All writes failed. */
767855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
768fa4a1febSPawel Jakub Dawidek 		g_free(disks);
769fa4a1febSPawel Jakub Dawidek 		return;
770fa4a1febSPawel Jakub Dawidek 	}
771fa4a1febSPawel Jakub Dawidek 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
772fa4a1febSPawel Jakub Dawidek 		g_mirror_update_metadata(disk);
773fa4a1febSPawel Jakub Dawidek 	}
774fa4a1febSPawel Jakub Dawidek 	/*
775fa4a1febSPawel Jakub Dawidek 	 * Release provider and wait for retaste.
776fa4a1febSPawel Jakub Dawidek 	 */
777855761d5SPawel Jakub Dawidek 	g_topology_lock();
778fa4a1febSPawel Jakub Dawidek 	for (i = 0; i < n; i++) {
779fa4a1febSPawel Jakub Dawidek 		if (disks[i].consumer == NULL)
780fa4a1febSPawel Jakub Dawidek 			continue;
781fa4a1febSPawel Jakub Dawidek 		g_access(disks[i].consumer, 0, -1, -1);
782fa4a1febSPawel Jakub Dawidek 		g_detach(disks[i].consumer);
783fa4a1febSPawel Jakub Dawidek 		g_destroy_consumer(disks[i].consumer);
784fa4a1febSPawel Jakub Dawidek 	}
785855761d5SPawel Jakub Dawidek 	g_topology_unlock();
786855761d5SPawel Jakub Dawidek 	sx_xunlock(&sc->sc_lock);
787fa4a1febSPawel Jakub Dawidek 	g_free(disks);
788fa4a1febSPawel Jakub Dawidek }
789fa4a1febSPawel Jakub Dawidek 
790fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_remove(struct gctl_req * req,struct g_class * mp)791fa4a1febSPawel Jakub Dawidek g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
792fa4a1febSPawel Jakub Dawidek {
793fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
794fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
795fa4a1febSPawel Jakub Dawidek 	const char *name;
796fa4a1febSPawel Jakub Dawidek 	char param[16];
797fa4a1febSPawel Jakub Dawidek 	int *nargs;
798f931cd70SAndrey V. Elsukov 	u_int i, active;
799fa4a1febSPawel Jakub Dawidek 
800fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
801fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
802fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
803fa4a1febSPawel Jakub Dawidek 		return;
804fa4a1febSPawel Jakub Dawidek 	}
805fa4a1febSPawel Jakub Dawidek 	if (*nargs < 2) {
806fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Too few arguments.");
807fa4a1febSPawel Jakub Dawidek 		return;
808fa4a1febSPawel Jakub Dawidek 	}
809fa4a1febSPawel Jakub Dawidek 	name = gctl_get_asciiparam(req, "arg0");
810fa4a1febSPawel Jakub Dawidek 	if (name == NULL) {
811fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No 'arg%u' argument.", 0);
812fa4a1febSPawel Jakub Dawidek 		return;
813fa4a1febSPawel Jakub Dawidek 	}
814fa4a1febSPawel Jakub Dawidek 	sc = g_mirror_find_device(mp, name);
815fa4a1febSPawel Jakub Dawidek 	if (sc == NULL) {
816fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No such device: %s.", name);
817fa4a1febSPawel Jakub Dawidek 		return;
818fa4a1febSPawel Jakub Dawidek 	}
819fa4a1febSPawel Jakub Dawidek 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
820855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
821855761d5SPawel Jakub Dawidek 		gctl_error(req, "Not all disks connected. Try 'forget' command "
822855761d5SPawel Jakub Dawidek 		    "first.");
823fa4a1febSPawel Jakub Dawidek 		return;
824fa4a1febSPawel Jakub Dawidek 	}
825f931cd70SAndrey V. Elsukov 	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
826fa4a1febSPawel Jakub Dawidek 	for (i = 1; i < (u_int)*nargs; i++) {
827fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
828fa4a1febSPawel Jakub Dawidek 		name = gctl_get_asciiparam(req, param);
829fa4a1febSPawel Jakub Dawidek 		if (name == NULL) {
830fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No 'arg%u' argument.", i);
831c37e2f9bSPawel Jakub Dawidek 			continue;
832fa4a1febSPawel Jakub Dawidek 		}
833fa4a1febSPawel Jakub Dawidek 		disk = g_mirror_find_disk(sc, name);
834fa4a1febSPawel Jakub Dawidek 		if (disk == NULL) {
835fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No such provider: %s.", name);
836c37e2f9bSPawel Jakub Dawidek 			continue;
837fa4a1febSPawel Jakub Dawidek 		}
838f931cd70SAndrey V. Elsukov 		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
839f931cd70SAndrey V. Elsukov 			if (active > 1)
840f931cd70SAndrey V. Elsukov 				active--;
841f931cd70SAndrey V. Elsukov 			else {
842f931cd70SAndrey V. Elsukov 				gctl_error(req, "%s: Can't remove the last "
843f931cd70SAndrey V. Elsukov 				    "ACTIVE component %s.", sc->sc_geom->name,
844f931cd70SAndrey V. Elsukov 				    name);
845f931cd70SAndrey V. Elsukov 				continue;
846f931cd70SAndrey V. Elsukov 			}
847f931cd70SAndrey V. Elsukov 		}
848fa4a1febSPawel Jakub Dawidek 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
849855761d5SPawel Jakub Dawidek 		    G_MIRROR_EVENT_DONTWAIT);
850fa4a1febSPawel Jakub Dawidek 	}
851855761d5SPawel Jakub Dawidek 	sx_xunlock(&sc->sc_lock);
852fa4a1febSPawel Jakub Dawidek }
853fa4a1febSPawel Jakub Dawidek 
854fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_resize(struct gctl_req * req,struct g_class * mp)85532cea4caSAndrey V. Elsukov g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
85632cea4caSAndrey V. Elsukov {
85732cea4caSAndrey V. Elsukov 	struct g_mirror_softc *sc;
85832cea4caSAndrey V. Elsukov 	struct g_mirror_disk *disk;
85932cea4caSAndrey V. Elsukov 	uint64_t mediasize;
86032cea4caSAndrey V. Elsukov 	const char *name, *s;
86132cea4caSAndrey V. Elsukov 	char *x;
86232cea4caSAndrey V. Elsukov 	int *nargs;
86332cea4caSAndrey V. Elsukov 
86432cea4caSAndrey V. Elsukov 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
86532cea4caSAndrey V. Elsukov 	if (nargs == NULL) {
86632cea4caSAndrey V. Elsukov 		gctl_error(req, "No '%s' argument.", "nargs");
86732cea4caSAndrey V. Elsukov 		return;
86832cea4caSAndrey V. Elsukov 	}
86932cea4caSAndrey V. Elsukov 	if (*nargs != 1) {
87032cea4caSAndrey V. Elsukov 		gctl_error(req, "Missing device.");
87132cea4caSAndrey V. Elsukov 		return;
87232cea4caSAndrey V. Elsukov 	}
87332cea4caSAndrey V. Elsukov 	name = gctl_get_asciiparam(req, "arg0");
87432cea4caSAndrey V. Elsukov 	if (name == NULL) {
87532cea4caSAndrey V. Elsukov 		gctl_error(req, "No 'arg%u' argument.", 0);
87632cea4caSAndrey V. Elsukov 		return;
87732cea4caSAndrey V. Elsukov 	}
87832cea4caSAndrey V. Elsukov 	s = gctl_get_asciiparam(req, "size");
87932cea4caSAndrey V. Elsukov 	if (s == NULL) {
88032cea4caSAndrey V. Elsukov 		gctl_error(req, "No '%s' argument.", "size");
88132cea4caSAndrey V. Elsukov 		return;
88232cea4caSAndrey V. Elsukov 	}
88332cea4caSAndrey V. Elsukov 	mediasize = strtouq(s, &x, 0);
88432cea4caSAndrey V. Elsukov 	if (*x != '\0' || mediasize == 0) {
88532cea4caSAndrey V. Elsukov 		gctl_error(req, "Invalid '%s' argument.", "size");
88632cea4caSAndrey V. Elsukov 		return;
88732cea4caSAndrey V. Elsukov 	}
888844b743dSConrad Meyer 	sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
88932cea4caSAndrey V. Elsukov 	if (sc == NULL) {
89032cea4caSAndrey V. Elsukov 		gctl_error(req, "No such device: %s.", name);
89132cea4caSAndrey V. Elsukov 		return;
89232cea4caSAndrey V. Elsukov 	}
89332cea4caSAndrey V. Elsukov 	/* Deny shrinking of an opened provider */
894c4c88d47SAlexander Motin 	if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && sc->sc_provider_open > 0) {
89532cea4caSAndrey V. Elsukov 		if (sc->sc_mediasize > mediasize) {
89632cea4caSAndrey V. Elsukov 			gctl_error(req, "Device %s is busy.",
89732cea4caSAndrey V. Elsukov 			    sc->sc_provider->name);
89832cea4caSAndrey V. Elsukov 			sx_xunlock(&sc->sc_lock);
89932cea4caSAndrey V. Elsukov 			return;
90032cea4caSAndrey V. Elsukov 		}
90132cea4caSAndrey V. Elsukov 	}
90232cea4caSAndrey V. Elsukov 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
90332cea4caSAndrey V. Elsukov 		if (mediasize > disk->d_consumer->provider->mediasize -
90432cea4caSAndrey V. Elsukov 		    disk->d_consumer->provider->sectorsize) {
90532cea4caSAndrey V. Elsukov 			gctl_error(req, "Provider %s is too small.",
90632cea4caSAndrey V. Elsukov 			    disk->d_name);
90732cea4caSAndrey V. Elsukov 			sx_xunlock(&sc->sc_lock);
90832cea4caSAndrey V. Elsukov 			return;
90932cea4caSAndrey V. Elsukov 		}
91032cea4caSAndrey V. Elsukov 	}
91132cea4caSAndrey V. Elsukov 	/* Update the size. */
91232cea4caSAndrey V. Elsukov 	sc->sc_mediasize = mediasize;
91332cea4caSAndrey V. Elsukov 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
91432cea4caSAndrey V. Elsukov 		g_mirror_update_metadata(disk);
91532cea4caSAndrey V. Elsukov 	}
91632cea4caSAndrey V. Elsukov 	g_topology_lock();
91732cea4caSAndrey V. Elsukov 	g_resize_provider(sc->sc_provider, mediasize);
91832cea4caSAndrey V. Elsukov 	g_topology_unlock();
91932cea4caSAndrey V. Elsukov 	sx_xunlock(&sc->sc_lock);
92032cea4caSAndrey V. Elsukov }
92132cea4caSAndrey V. Elsukov 
92232cea4caSAndrey V. Elsukov static void
g_mirror_ctl_deactivate(struct gctl_req * req,struct g_class * mp)923fa4a1febSPawel Jakub Dawidek g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
924fa4a1febSPawel Jakub Dawidek {
925fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
926fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
927fa4a1febSPawel Jakub Dawidek 	const char *name;
928fa4a1febSPawel Jakub Dawidek 	char param[16];
929fa4a1febSPawel Jakub Dawidek 	int *nargs;
9307c5710dbSAndrey V. Elsukov 	u_int i, active;
931fa4a1febSPawel Jakub Dawidek 
932fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
933fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
934fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
935fa4a1febSPawel Jakub Dawidek 		return;
936fa4a1febSPawel Jakub Dawidek 	}
937fa4a1febSPawel Jakub Dawidek 	if (*nargs < 2) {
938fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Too few arguments.");
939fa4a1febSPawel Jakub Dawidek 		return;
940fa4a1febSPawel Jakub Dawidek 	}
941fa4a1febSPawel Jakub Dawidek 	name = gctl_get_asciiparam(req, "arg0");
942fa4a1febSPawel Jakub Dawidek 	if (name == NULL) {
943fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No 'arg%u' argument.", 0);
944fa4a1febSPawel Jakub Dawidek 		return;
945fa4a1febSPawel Jakub Dawidek 	}
946fa4a1febSPawel Jakub Dawidek 	sc = g_mirror_find_device(mp, name);
947fa4a1febSPawel Jakub Dawidek 	if (sc == NULL) {
948fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No such device: %s.", name);
949fa4a1febSPawel Jakub Dawidek 		return;
950fa4a1febSPawel Jakub Dawidek 	}
9517c5710dbSAndrey V. Elsukov 	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
952fa4a1febSPawel Jakub Dawidek 	for (i = 1; i < (u_int)*nargs; i++) {
953fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
954fa4a1febSPawel Jakub Dawidek 		name = gctl_get_asciiparam(req, param);
955fa4a1febSPawel Jakub Dawidek 		if (name == NULL) {
956fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No 'arg%u' argument.", i);
957c37e2f9bSPawel Jakub Dawidek 			continue;
958fa4a1febSPawel Jakub Dawidek 		}
959fa4a1febSPawel Jakub Dawidek 		disk = g_mirror_find_disk(sc, name);
960fa4a1febSPawel Jakub Dawidek 		if (disk == NULL) {
961fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No such provider: %s.", name);
962c37e2f9bSPawel Jakub Dawidek 			continue;
963fa4a1febSPawel Jakub Dawidek 		}
9647c5710dbSAndrey V. Elsukov 		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
9657c5710dbSAndrey V. Elsukov 			if (active > 1)
9667c5710dbSAndrey V. Elsukov 				active--;
9677c5710dbSAndrey V. Elsukov 			else {
9687c5710dbSAndrey V. Elsukov 				gctl_error(req, "%s: Can't deactivate the "
9697c5710dbSAndrey V. Elsukov 				    "last ACTIVE component %s.",
9707c5710dbSAndrey V. Elsukov 				    sc->sc_geom->name, name);
9717c5710dbSAndrey V. Elsukov 				continue;
9727c5710dbSAndrey V. Elsukov 			}
9737c5710dbSAndrey V. Elsukov 		}
974fa4a1febSPawel Jakub Dawidek 		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
975fa4a1febSPawel Jakub Dawidek 		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
976fa4a1febSPawel Jakub Dawidek 		g_mirror_update_metadata(disk);
977da844167SPawel Jakub Dawidek 		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
978fa4a1febSPawel Jakub Dawidek 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
979855761d5SPawel Jakub Dawidek 		    G_MIRROR_EVENT_DONTWAIT);
980fa4a1febSPawel Jakub Dawidek 	}
981855761d5SPawel Jakub Dawidek 	sx_xunlock(&sc->sc_lock);
982fa4a1febSPawel Jakub Dawidek }
983fa4a1febSPawel Jakub Dawidek 
984fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_forget(struct gctl_req * req,struct g_class * mp)985fa4a1febSPawel Jakub Dawidek g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
986fa4a1febSPawel Jakub Dawidek {
987fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
988fa4a1febSPawel Jakub Dawidek 	struct g_mirror_disk *disk;
989fa4a1febSPawel Jakub Dawidek 	const char *name;
990fa4a1febSPawel Jakub Dawidek 	char param[16];
991fa4a1febSPawel Jakub Dawidek 	int *nargs;
992fa4a1febSPawel Jakub Dawidek 	u_int i;
993fa4a1febSPawel Jakub Dawidek 
994fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
995fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
996fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
997fa4a1febSPawel Jakub Dawidek 		return;
998fa4a1febSPawel Jakub Dawidek 	}
999fa4a1febSPawel Jakub Dawidek 	if (*nargs < 1) {
1000fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Missing device(s).");
1001fa4a1febSPawel Jakub Dawidek 		return;
1002fa4a1febSPawel Jakub Dawidek 	}
1003fa4a1febSPawel Jakub Dawidek 
1004fa4a1febSPawel Jakub Dawidek 	for (i = 0; i < (u_int)*nargs; i++) {
1005fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
1006fa4a1febSPawel Jakub Dawidek 		name = gctl_get_asciiparam(req, param);
1007fa4a1febSPawel Jakub Dawidek 		if (name == NULL) {
1008fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No 'arg%u' argument.", i);
1009fa4a1febSPawel Jakub Dawidek 			return;
1010fa4a1febSPawel Jakub Dawidek 		}
1011fa4a1febSPawel Jakub Dawidek 		sc = g_mirror_find_device(mp, name);
1012fa4a1febSPawel Jakub Dawidek 		if (sc == NULL) {
1013fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No such device: %s.", name);
1014fa4a1febSPawel Jakub Dawidek 			return;
1015fa4a1febSPawel Jakub Dawidek 		}
1016fa4a1febSPawel Jakub Dawidek 		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
1017855761d5SPawel Jakub Dawidek 			sx_xunlock(&sc->sc_lock);
1018fa4a1febSPawel Jakub Dawidek 			G_MIRROR_DEBUG(1,
1019fa4a1febSPawel Jakub Dawidek 			    "All disks connected in %s, skipping.",
1020fa4a1febSPawel Jakub Dawidek 			    sc->sc_name);
1021fa4a1febSPawel Jakub Dawidek 			continue;
1022fa4a1febSPawel Jakub Dawidek 		}
1023fa4a1febSPawel Jakub Dawidek 		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
1024fa4a1febSPawel Jakub Dawidek 		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
1025fa4a1febSPawel Jakub Dawidek 			g_mirror_update_metadata(disk);
1026fa4a1febSPawel Jakub Dawidek 		}
1027855761d5SPawel Jakub Dawidek 		sx_xunlock(&sc->sc_lock);
1028fa4a1febSPawel Jakub Dawidek 	}
1029fa4a1febSPawel Jakub Dawidek }
1030fa4a1febSPawel Jakub Dawidek 
1031fa4a1febSPawel Jakub Dawidek static void
g_mirror_ctl_stop(struct gctl_req * req,struct g_class * mp,int wipe)1032ae3bc0acSAndrey V. Elsukov g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
1033fa4a1febSPawel Jakub Dawidek {
1034fa4a1febSPawel Jakub Dawidek 	struct g_mirror_softc *sc;
1035fa4a1febSPawel Jakub Dawidek 	int *force, *nargs, error;
1036fa4a1febSPawel Jakub Dawidek 	const char *name;
1037fa4a1febSPawel Jakub Dawidek 	char param[16];
1038fa4a1febSPawel Jakub Dawidek 	u_int i;
1039712fe9bdSPawel Jakub Dawidek 	int how;
1040fa4a1febSPawel Jakub Dawidek 
1041fa4a1febSPawel Jakub Dawidek 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1042fa4a1febSPawel Jakub Dawidek 	if (nargs == NULL) {
1043fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "nargs");
1044fa4a1febSPawel Jakub Dawidek 		return;
1045fa4a1febSPawel Jakub Dawidek 	}
1046fa4a1febSPawel Jakub Dawidek 	if (*nargs < 1) {
1047fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Missing device(s).");
1048fa4a1febSPawel Jakub Dawidek 		return;
1049fa4a1febSPawel Jakub Dawidek 	}
1050fa4a1febSPawel Jakub Dawidek 	force = gctl_get_paraml(req, "force", sizeof(*force));
1051fa4a1febSPawel Jakub Dawidek 	if (force == NULL) {
1052fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "force");
1053fa4a1febSPawel Jakub Dawidek 		return;
1054fa4a1febSPawel Jakub Dawidek 	}
1055712fe9bdSPawel Jakub Dawidek 	if (*force)
1056712fe9bdSPawel Jakub Dawidek 		how = G_MIRROR_DESTROY_HARD;
1057712fe9bdSPawel Jakub Dawidek 	else
1058712fe9bdSPawel Jakub Dawidek 		how = G_MIRROR_DESTROY_SOFT;
1059fa4a1febSPawel Jakub Dawidek 
1060fa4a1febSPawel Jakub Dawidek 	for (i = 0; i < (u_int)*nargs; i++) {
1061fa4a1febSPawel Jakub Dawidek 		snprintf(param, sizeof(param), "arg%u", i);
1062fa4a1febSPawel Jakub Dawidek 		name = gctl_get_asciiparam(req, param);
1063fa4a1febSPawel Jakub Dawidek 		if (name == NULL) {
1064fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No 'arg%u' argument.", i);
1065fa4a1febSPawel Jakub Dawidek 			return;
1066fa4a1febSPawel Jakub Dawidek 		}
1067fa4a1febSPawel Jakub Dawidek 		sc = g_mirror_find_device(mp, name);
1068fa4a1febSPawel Jakub Dawidek 		if (sc == NULL) {
1069fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "No such device: %s.", name);
1070fa4a1febSPawel Jakub Dawidek 			return;
1071fa4a1febSPawel Jakub Dawidek 		}
1072712fe9bdSPawel Jakub Dawidek 		g_cancel_event(sc);
1073ae3bc0acSAndrey V. Elsukov 		if (wipe)
1074ae3bc0acSAndrey V. Elsukov 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
1075712fe9bdSPawel Jakub Dawidek 		error = g_mirror_destroy(sc, how);
1076fa4a1febSPawel Jakub Dawidek 		if (error != 0) {
1077fa4a1febSPawel Jakub Dawidek 			gctl_error(req, "Cannot destroy device %s (error=%d).",
1078fa4a1febSPawel Jakub Dawidek 			    sc->sc_geom->name, error);
1079ae3bc0acSAndrey V. Elsukov 			if (wipe)
1080ae3bc0acSAndrey V. Elsukov 				sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
1081855761d5SPawel Jakub Dawidek 			sx_xunlock(&sc->sc_lock);
1082fa4a1febSPawel Jakub Dawidek 			return;
1083fa4a1febSPawel Jakub Dawidek 		}
1084855761d5SPawel Jakub Dawidek 		/* No need to unlock, because lock is already dead. */
1085fa4a1febSPawel Jakub Dawidek 	}
1086fa4a1febSPawel Jakub Dawidek }
1087fa4a1febSPawel Jakub Dawidek 
1088fa4a1febSPawel Jakub Dawidek void
g_mirror_config(struct gctl_req * req,struct g_class * mp,const char * verb)1089fa4a1febSPawel Jakub Dawidek g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1090fa4a1febSPawel Jakub Dawidek {
1091fa4a1febSPawel Jakub Dawidek 	uint32_t *version;
1092fa4a1febSPawel Jakub Dawidek 
1093fa4a1febSPawel Jakub Dawidek 	g_topology_assert();
1094fa4a1febSPawel Jakub Dawidek 
1095fa4a1febSPawel Jakub Dawidek 	version = gctl_get_paraml(req, "version", sizeof(*version));
1096fa4a1febSPawel Jakub Dawidek 	if (version == NULL) {
1097fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "No '%s' argument.", "version");
1098fa4a1febSPawel Jakub Dawidek 		return;
1099fa4a1febSPawel Jakub Dawidek 	}
1100fa4a1febSPawel Jakub Dawidek 	if (*version != G_MIRROR_VERSION) {
1101fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Userland and kernel parts are out of sync.");
1102fa4a1febSPawel Jakub Dawidek 		return;
1103fa4a1febSPawel Jakub Dawidek 	}
1104fa4a1febSPawel Jakub Dawidek 
1105855761d5SPawel Jakub Dawidek 	g_topology_unlock();
1106fa4a1febSPawel Jakub Dawidek 	if (strcmp(verb, "configure") == 0)
1107fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_configure(req, mp);
1108b6fe583cSAlexander Motin 	else if (strcmp(verb, "create") == 0)
1109b6fe583cSAlexander Motin 		g_mirror_ctl_create(req, mp);
1110fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "rebuild") == 0)
1111fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_rebuild(req, mp);
1112fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "insert") == 0)
1113fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_insert(req, mp);
1114fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "remove") == 0)
1115fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_remove(req, mp);
111632cea4caSAndrey V. Elsukov 	else if (strcmp(verb, "resize") == 0)
111732cea4caSAndrey V. Elsukov 		g_mirror_ctl_resize(req, mp);
1118fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "deactivate") == 0)
1119fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_deactivate(req, mp);
1120fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "forget") == 0)
1121fa4a1febSPawel Jakub Dawidek 		g_mirror_ctl_forget(req, mp);
1122fa4a1febSPawel Jakub Dawidek 	else if (strcmp(verb, "stop") == 0)
1123ae3bc0acSAndrey V. Elsukov 		g_mirror_ctl_stop(req, mp, 0);
1124ae3bc0acSAndrey V. Elsukov 	else if (strcmp(verb, "destroy") == 0)
1125ae3bc0acSAndrey V. Elsukov 		g_mirror_ctl_stop(req, mp, 1);
1126fa4a1febSPawel Jakub Dawidek 	else
1127fa4a1febSPawel Jakub Dawidek 		gctl_error(req, "Unknown verb.");
1128855761d5SPawel Jakub Dawidek 	g_topology_lock();
1129fa4a1febSPawel Jakub Dawidek }
1130