1132904Spjd/*-
2196879Spjd * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3132904Spjd * All rights reserved.
4132904Spjd *
5132904Spjd * Redistribution and use in source and binary forms, with or without
6132904Spjd * modification, are permitted provided that the following conditions
7132904Spjd * are met:
8132904Spjd * 1. Redistributions of source code must retain the above copyright
9132904Spjd *    notice, this list of conditions and the following disclaimer.
10132904Spjd * 2. Redistributions in binary form must reproduce the above copyright
11132904Spjd *    notice, this list of conditions and the following disclaimer in the
12132904Spjd *    documentation and/or other materials provided with the distribution.
13155174Spjd *
14132904Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15132904Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132904Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132904Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18132904Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19132904Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20132904Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132904Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22132904Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23132904Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24132904Spjd * SUCH DAMAGE.
25132904Spjd */
26132904Spjd
27132904Spjd#include <sys/cdefs.h>
28132904Spjd__FBSDID("$FreeBSD: releng/10.3/sys/geom/mirror/g_mirror_ctl.c 260503 2014-01-10 07:48:36Z ae $");
29132904Spjd
30132904Spjd#include <sys/param.h>
31132904Spjd#include <sys/systm.h>
32132904Spjd#include <sys/kernel.h>
33132904Spjd#include <sys/module.h>
34260502Sae#include <sys/limits.h>
35132904Spjd#include <sys/lock.h>
36132904Spjd#include <sys/mutex.h>
37132904Spjd#include <sys/bio.h>
38132904Spjd#include <sys/sysctl.h>
39132904Spjd#include <sys/malloc.h>
40132904Spjd#include <sys/bitstring.h>
41132904Spjd#include <vm/uma.h>
42132904Spjd#include <machine/atomic.h>
43132904Spjd#include <geom/geom.h>
44260502Sae#include <geom/geom_int.h>
45132904Spjd#include <sys/proc.h>
46132904Spjd#include <sys/kthread.h>
47132904Spjd#include <geom/mirror/g_mirror.h>
48132904Spjd
49132904Spjd
50132904Spjdstatic struct g_mirror_softc *
51132904Spjdg_mirror_find_device(struct g_class *mp, const char *name)
52132904Spjd{
53132904Spjd	struct g_mirror_softc *sc;
54132904Spjd	struct g_geom *gp;
55132904Spjd
56156610Spjd	g_topology_lock();
57132904Spjd	LIST_FOREACH(gp, &mp->geom, geom) {
58132904Spjd		sc = gp->softc;
59132904Spjd		if (sc == NULL)
60132904Spjd			continue;
61132904Spjd		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
62132904Spjd			continue;
63132904Spjd		if (strcmp(gp->name, name) == 0 ||
64132904Spjd		    strcmp(sc->sc_name, name) == 0) {
65156610Spjd			g_topology_unlock();
66156610Spjd			sx_xlock(&sc->sc_lock);
67132904Spjd			return (sc);
68132904Spjd		}
69132904Spjd	}
70156610Spjd	g_topology_unlock();
71132904Spjd	return (NULL);
72132904Spjd}
73132904Spjd
74132904Spjdstatic struct g_mirror_disk *
75132904Spjdg_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
76132904Spjd{
77132904Spjd	struct g_mirror_disk *disk;
78132904Spjd
79156610Spjd	sx_assert(&sc->sc_lock, SX_XLOCKED);
80160330Spjd	if (strncmp(name, "/dev/", 5) == 0)
81160330Spjd		name += 5;
82132904Spjd	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
83132904Spjd		if (disk->d_consumer == NULL)
84132904Spjd			continue;
85132904Spjd		if (disk->d_consumer->provider == NULL)
86132904Spjd			continue;
87132904Spjd		if (strcmp(disk->d_consumer->provider->name, name) == 0)
88132904Spjd			return (disk);
89132904Spjd	}
90132904Spjd	return (NULL);
91132904Spjd}
92132904Spjd
93132904Spjdstatic void
94132904Spjdg_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
95132904Spjd{
96132904Spjd	struct g_mirror_softc *sc;
97132904Spjd	struct g_mirror_disk *disk;
98196879Spjd	const char *name, *balancep, *prov;
99196879Spjd	intmax_t *slicep, *priority;
100132904Spjd	uint32_t slice;
101132904Spjd	uint8_t balance;
102163888Spjd	int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
103196879Spjd	int *nargs, do_sync = 0, dirty = 1, do_priority = 0;
104132904Spjd
105132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
106144142Spjd	if (nargs == NULL) {
107144142Spjd		gctl_error(req, "No '%s' argument.", "nargs");
108144142Spjd		return;
109144142Spjd	}
110196879Spjd	if (*nargs != 1 && *nargs != 2) {
111132904Spjd		gctl_error(req, "Invalid number of arguments.");
112132904Spjd		return;
113132904Spjd	}
114132904Spjd	name = gctl_get_asciiparam(req, "arg0");
115144142Spjd	if (name == NULL) {
116144142Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
117144142Spjd		return;
118144142Spjd	}
119132904Spjd	balancep = gctl_get_asciiparam(req, "balance");
120144142Spjd	if (balancep == NULL) {
121144142Spjd		gctl_error(req, "No '%s' argument.", "balance");
122144142Spjd		return;
123144142Spjd	}
124132904Spjd	autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
125132904Spjd	if (autosync == NULL) {
126132904Spjd		gctl_error(req, "No '%s' argument.", "autosync");
127132904Spjd		return;
128132904Spjd	}
129132904Spjd	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
130132904Spjd	if (noautosync == NULL) {
131132904Spjd		gctl_error(req, "No '%s' argument.", "noautosync");
132132904Spjd		return;
133132904Spjd	}
134163888Spjd	failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
135163888Spjd	if (failsync == NULL) {
136163888Spjd		gctl_error(req, "No '%s' argument.", "failsync");
137163888Spjd		return;
138163888Spjd	}
139163888Spjd	nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
140163888Spjd	if (nofailsync == NULL) {
141163888Spjd		gctl_error(req, "No '%s' argument.", "nofailsync");
142163888Spjd		return;
143163888Spjd	}
144133447Spjd	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
145133447Spjd	if (hardcode == NULL) {
146133447Spjd		gctl_error(req, "No '%s' argument.", "hardcode");
147133447Spjd		return;
148133447Spjd	}
149133447Spjd	dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
150133447Spjd	if (dynamic == NULL) {
151133447Spjd		gctl_error(req, "No '%s' argument.", "dynamic");
152133447Spjd		return;
153133447Spjd	}
154196879Spjd	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
155196879Spjd	if (priority == NULL) {
156196879Spjd		gctl_error(req, "No '%s' argument.", "priority");
157196879Spjd		return;
158196879Spjd	}
159196879Spjd	if (*priority < -1 || *priority > 255) {
160196879Spjd		gctl_error(req, "Priority range is 0 to 255, %jd given",
161196879Spjd		    *priority);
162196879Spjd		return;
163196879Spjd	}
164196879Spjd	/*
165196879Spjd	 * Since we have a priority, we also need a provider now.
166196879Spjd	 * Note: be WARNS safe, by always assigning prov and only throw an
167196879Spjd	 * error if *priority != -1.
168196879Spjd	 */
169196879Spjd	prov = gctl_get_asciiparam(req, "arg1");
170196879Spjd	if (*priority > -1) {
171196879Spjd		if (prov == NULL) {
172196879Spjd			gctl_error(req, "Priority needs a disk name");
173196879Spjd			return;
174196879Spjd		}
175196879Spjd		do_priority = 1;
176196879Spjd	}
177132904Spjd	if (*autosync && *noautosync) {
178132904Spjd		gctl_error(req, "'%s' and '%s' specified.", "autosync",
179132904Spjd		    "noautosync");
180132904Spjd		return;
181132904Spjd	}
182163888Spjd	if (*failsync && *nofailsync) {
183163888Spjd		gctl_error(req, "'%s' and '%s' specified.", "failsync",
184163888Spjd		    "nofailsync");
185163888Spjd		return;
186163888Spjd	}
187133447Spjd	if (*hardcode && *dynamic) {
188133447Spjd		gctl_error(req, "'%s' and '%s' specified.", "hardcode",
189133447Spjd		    "dynamic");
190133447Spjd		return;
191133447Spjd	}
192156610Spjd	sc = g_mirror_find_device(mp, name);
193156610Spjd	if (sc == NULL) {
194156610Spjd		gctl_error(req, "No such device: %s.", name);
195156610Spjd		return;
196156610Spjd	}
197212547Spjd	if (*balancep == '\0')
198156610Spjd		balance = sc->sc_balance;
199156610Spjd	else {
200156610Spjd		if (balance_id(balancep) == -1) {
201156610Spjd			gctl_error(req, "Invalid balance algorithm.");
202156610Spjd			sx_xunlock(&sc->sc_lock);
203156610Spjd			return;
204156610Spjd		}
205156610Spjd		balance = balance_id(balancep);
206156610Spjd	}
207156610Spjd	slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
208156610Spjd	if (slicep == NULL) {
209156610Spjd		gctl_error(req, "No '%s' argument.", "slice");
210156610Spjd		sx_xunlock(&sc->sc_lock);
211156610Spjd		return;
212156610Spjd	}
213156610Spjd	if (*slicep == -1)
214156610Spjd		slice = sc->sc_slice;
215156610Spjd	else
216156610Spjd		slice = *slicep;
217196879Spjd	/* Enforce usage() of -p not allowing any other options. */
218196879Spjd	if (do_priority && (*autosync || *noautosync || *failsync ||
219196879Spjd	    *nofailsync || *hardcode || *dynamic || *slicep != -1 ||
220212547Spjd	    *balancep != '\0')) {
221156610Spjd		sx_xunlock(&sc->sc_lock);
222196879Spjd		gctl_error(req, "only -p accepted when setting priority");
223156610Spjd		return;
224156610Spjd	}
225156610Spjd	if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
226163888Spjd	    !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
227196879Spjd	    !*dynamic && !do_priority) {
228156610Spjd		sx_xunlock(&sc->sc_lock);
229156610Spjd		gctl_error(req, "Nothing has changed.");
230156610Spjd		return;
231156610Spjd	}
232196879Spjd	if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) {
233196879Spjd		sx_xunlock(&sc->sc_lock);
234196879Spjd		gctl_error(req, "Invalid number of arguments.");
235196879Spjd		return;
236196879Spjd	}
237196879Spjd	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
238196879Spjd		sx_xunlock(&sc->sc_lock);
239196879Spjd		gctl_error(req, "Not all disks connected. Try 'forget' command "
240196879Spjd		    "first.");
241196879Spjd		return;
242196879Spjd	}
243132904Spjd	sc->sc_balance = balance;
244132904Spjd	sc->sc_slice = slice;
245132904Spjd	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
246132904Spjd		if (*autosync) {
247132904Spjd			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
248132904Spjd			do_sync = 1;
249132904Spjd		}
250132904Spjd	} else {
251132904Spjd		if (*noautosync)
252132904Spjd			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
253132904Spjd	}
254163888Spjd	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
255163888Spjd		if (*failsync)
256163888Spjd			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
257163888Spjd	} else {
258163888Spjd		if (*nofailsync) {
259163888Spjd			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
260163888Spjd			dirty = 0;
261163888Spjd		}
262163888Spjd	}
263132904Spjd	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
264196879Spjd		/*
265196879Spjd		 * Handle priority first, since we only need one disk, do one
266196879Spjd		 * operation on it and then we're done. No need to check other
267196879Spjd		 * flags, as usage doesn't allow it.
268196879Spjd		 */
269196879Spjd		if (do_priority) {
270196879Spjd			if (strcmp(disk->d_name, prov) == 0) {
271196879Spjd				if (disk->d_priority == *priority)
272196879Spjd					gctl_error(req, "Nothing has changed.");
273196879Spjd				else {
274196879Spjd					disk->d_priority = *priority;
275196879Spjd					g_mirror_update_metadata(disk);
276196879Spjd				}
277196879Spjd				break;
278196879Spjd			}
279196879Spjd			continue;
280196879Spjd		}
281132904Spjd		if (do_sync) {
282132904Spjd			if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
283132904Spjd				disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
284132904Spjd		}
285133447Spjd		if (*hardcode)
286133447Spjd			disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
287133447Spjd		else if (*dynamic)
288133447Spjd			disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
289163888Spjd		if (!dirty)
290163888Spjd			disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
291132904Spjd		g_mirror_update_metadata(disk);
292132904Spjd		if (do_sync) {
293132904Spjd			if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
294132904Spjd				g_mirror_event_send(disk,
295132904Spjd				    G_MIRROR_DISK_STATE_DISCONNECTED,
296132904Spjd				    G_MIRROR_EVENT_DONTWAIT);
297132904Spjd			}
298132904Spjd		}
299132904Spjd	}
300156610Spjd	sx_xunlock(&sc->sc_lock);
301132904Spjd}
302132904Spjd
303132904Spjdstatic void
304132904Spjdg_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
305132904Spjd{
306139650Spjd	struct g_mirror_metadata md;
307132904Spjd	struct g_mirror_softc *sc;
308132904Spjd	struct g_mirror_disk *disk;
309139650Spjd	struct g_provider *pp;
310132904Spjd	const char *name;
311132904Spjd	char param[16];
312139650Spjd	int error, *nargs;
313132904Spjd	u_int i;
314132904Spjd
315132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
316132904Spjd	if (nargs == NULL) {
317132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
318132904Spjd		return;
319132904Spjd	}
320132904Spjd	if (*nargs < 2) {
321132904Spjd		gctl_error(req, "Too few arguments.");
322132904Spjd		return;
323132904Spjd	}
324132904Spjd	name = gctl_get_asciiparam(req, "arg0");
325132904Spjd	if (name == NULL) {
326132904Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
327132904Spjd		return;
328132904Spjd	}
329132904Spjd	sc = g_mirror_find_device(mp, name);
330132904Spjd	if (sc == NULL) {
331132904Spjd		gctl_error(req, "No such device: %s.", name);
332132904Spjd		return;
333132904Spjd	}
334132904Spjd	for (i = 1; i < (u_int)*nargs; i++) {
335132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
336132904Spjd		name = gctl_get_asciiparam(req, param);
337132904Spjd		if (name == NULL) {
338132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
339139050Spjd			continue;
340132904Spjd		}
341132904Spjd		disk = g_mirror_find_disk(sc, name);
342132904Spjd		if (disk == NULL) {
343132904Spjd			gctl_error(req, "No such provider: %s.", name);
344139050Spjd			continue;
345132904Spjd		}
346132904Spjd		if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
347132904Spjd		    disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
348132904Spjd			/*
349132904Spjd			 * This is the last active disk. There will be nothing
350132904Spjd			 * to rebuild it from, so deny this request.
351132904Spjd			 */
352132904Spjd			gctl_error(req,
353132904Spjd			    "Provider %s is the last active provider in %s.",
354132904Spjd			    name, sc->sc_geom->name);
355156610Spjd			break;
356132904Spjd		}
357132904Spjd		/*
358139650Spjd		 * Do rebuild by resetting syncid, disconnecting the disk and
359139650Spjd		 * connecting it again.
360132904Spjd		 */
361132904Spjd		disk->d_sync.ds_syncid = 0;
362132904Spjd		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
363132904Spjd			disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
364132904Spjd		g_mirror_update_metadata(disk);
365139650Spjd		pp = disk->d_consumer->provider;
366156610Spjd		g_topology_lock();
367139650Spjd		error = g_mirror_read_metadata(disk->d_consumer, &md);
368156610Spjd		g_topology_unlock();
369132904Spjd		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
370132904Spjd		    G_MIRROR_EVENT_WAIT);
371139650Spjd		if (error != 0) {
372139650Spjd			gctl_error(req, "Cannot read metadata from %s.",
373139650Spjd			    pp->name);
374139650Spjd			continue;
375139650Spjd		}
376139650Spjd		error = g_mirror_add_disk(sc, pp, &md);
377139650Spjd		if (error != 0) {
378139650Spjd			gctl_error(req, "Cannot reconnect component %s.",
379139650Spjd			    pp->name);
380139650Spjd			continue;
381139650Spjd		}
382132904Spjd	}
383156610Spjd	sx_xunlock(&sc->sc_lock);
384132904Spjd}
385132904Spjd
386132904Spjdstatic void
387132904Spjdg_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
388132904Spjd{
389132904Spjd	struct g_mirror_softc *sc;
390132904Spjd	struct g_mirror_disk *disk;
391132904Spjd	struct g_mirror_metadata md;
392132904Spjd	struct g_provider *pp;
393132904Spjd	struct g_consumer *cp;
394132909Spjd	intmax_t *priority;
395132904Spjd	const char *name;
396132904Spjd	char param[16];
397132904Spjd	u_char *sector;
398132904Spjd	u_int i, n;
399133447Spjd	int error, *nargs, *hardcode, *inactive;
400132904Spjd	struct {
401132904Spjd		struct g_provider	*provider;
402132904Spjd		struct g_consumer	*consumer;
403132904Spjd	} *disks;
404132904Spjd
405132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
406132904Spjd	if (nargs == NULL) {
407132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
408132904Spjd		return;
409132904Spjd	}
410132904Spjd	if (*nargs < 2) {
411132904Spjd		gctl_error(req, "Too few arguments.");
412132904Spjd		return;
413132904Spjd	}
414132909Spjd	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
415132909Spjd	if (priority == NULL) {
416132909Spjd		gctl_error(req, "No '%s' argument.", "priority");
417132909Spjd		return;
418132909Spjd	}
419132904Spjd	inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
420132904Spjd	if (inactive == NULL) {
421132904Spjd		gctl_error(req, "No '%s' argument.", "inactive");
422132904Spjd		return;
423132904Spjd	}
424133447Spjd	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
425133447Spjd	if (hardcode == NULL) {
426133447Spjd		gctl_error(req, "No '%s' argument.", "hardcode");
427133447Spjd		return;
428133447Spjd	}
429132904Spjd	name = gctl_get_asciiparam(req, "arg0");
430132904Spjd	if (name == NULL) {
431132904Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
432132904Spjd		return;
433132904Spjd	}
434132904Spjd	sc = g_mirror_find_device(mp, name);
435132904Spjd	if (sc == NULL) {
436132904Spjd		gctl_error(req, "No such device: %s.", name);
437132904Spjd		return;
438132904Spjd	}
439132904Spjd	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
440132904Spjd		gctl_error(req, "Not all disks connected.");
441156610Spjd		sx_xunlock(&sc->sc_lock);
442132904Spjd		return;
443132904Spjd	}
444132904Spjd
445132904Spjd	disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
446156610Spjd	g_topology_lock();
447132904Spjd	for (i = 1, n = 0; i < (u_int)*nargs; i++) {
448132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
449132904Spjd		name = gctl_get_asciiparam(req, param);
450132904Spjd		if (name == NULL) {
451132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
452132904Spjd			continue;
453132904Spjd		}
454132908Spjd		if (g_mirror_find_disk(sc, name) != NULL) {
455132908Spjd			gctl_error(req, "Provider %s already inserted.", name);
456132908Spjd			continue;
457132908Spjd		}
458160330Spjd		if (strncmp(name, "/dev/", 5) == 0)
459160330Spjd			name += 5;
460132904Spjd		pp = g_provider_by_name(name);
461132904Spjd		if (pp == NULL) {
462132904Spjd			gctl_error(req, "Unknown provider %s.", name);
463132904Spjd			continue;
464132904Spjd		}
465145502Spjd		if (sc->sc_provider->mediasize >
466145502Spjd		    pp->mediasize - pp->sectorsize) {
467132904Spjd			gctl_error(req, "Provider %s too small.", name);
468132904Spjd			continue;
469132904Spjd		}
470132904Spjd		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
471132904Spjd			gctl_error(req, "Invalid sectorsize of provider %s.",
472132904Spjd			    name);
473132904Spjd			continue;
474132904Spjd		}
475132904Spjd		cp = g_new_consumer(sc->sc_geom);
476132904Spjd		if (g_attach(cp, pp) != 0) {
477132904Spjd			g_destroy_consumer(cp);
478132904Spjd			gctl_error(req, "Cannot attach to provider %s.", name);
479132904Spjd			continue;
480132904Spjd		}
481132904Spjd		if (g_access(cp, 0, 1, 1) != 0) {
482132904Spjd			g_detach(cp);
483132904Spjd			g_destroy_consumer(cp);
484132904Spjd			gctl_error(req, "Cannot access provider %s.", name);
485132904Spjd			continue;
486132904Spjd		}
487132904Spjd		disks[n].provider = pp;
488132904Spjd		disks[n].consumer = cp;
489132904Spjd		n++;
490132904Spjd	}
491132904Spjd	if (n == 0) {
492156610Spjd		g_topology_unlock();
493156610Spjd		sx_xunlock(&sc->sc_lock);
494132904Spjd		g_free(disks);
495132904Spjd		return;
496132904Spjd	}
497132904Spjd	sc->sc_ndisks += n;
498132904Spjdagain:
499132904Spjd	for (i = 0; i < n; i++) {
500132904Spjd		if (disks[i].consumer == NULL)
501132904Spjd			continue;
502132904Spjd		g_mirror_fill_metadata(sc, NULL, &md);
503132909Spjd		md.md_priority = *priority;
504132904Spjd		if (*inactive)
505132904Spjd			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
506132904Spjd		pp = disks[i].provider;
507133447Spjd		if (*hardcode) {
508133447Spjd			strlcpy(md.md_provider, pp->name,
509133447Spjd			    sizeof(md.md_provider));
510133447Spjd		} else {
511133447Spjd			bzero(md.md_provider, sizeof(md.md_provider));
512133447Spjd		}
513156527Spjd		md.md_provsize = pp->mediasize;
514132904Spjd		sector = g_malloc(pp->sectorsize, M_WAITOK);
515132904Spjd		mirror_metadata_encode(&md, sector);
516132904Spjd		error = g_write_data(disks[i].consumer,
517132904Spjd		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
518132904Spjd		g_free(sector);
519132904Spjd		if (error != 0) {
520132904Spjd			gctl_error(req, "Cannot store metadata on %s.",
521132904Spjd			    pp->name);
522132904Spjd			g_access(disks[i].consumer, 0, -1, -1);
523132904Spjd			g_detach(disks[i].consumer);
524132904Spjd			g_destroy_consumer(disks[i].consumer);
525132904Spjd			disks[i].consumer = NULL;
526132904Spjd			disks[i].provider = NULL;
527132904Spjd			sc->sc_ndisks--;
528132904Spjd			goto again;
529132904Spjd		}
530132904Spjd	}
531156610Spjd	g_topology_unlock();
532132904Spjd	if (i == 0) {
533132904Spjd		/* All writes failed. */
534156610Spjd		sx_xunlock(&sc->sc_lock);
535132904Spjd		g_free(disks);
536132904Spjd		return;
537132904Spjd	}
538132904Spjd	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
539132904Spjd		g_mirror_update_metadata(disk);
540132904Spjd	}
541132904Spjd	/*
542132904Spjd	 * Release provider and wait for retaste.
543132904Spjd	 */
544156610Spjd	g_topology_lock();
545132904Spjd	for (i = 0; i < n; i++) {
546132904Spjd		if (disks[i].consumer == NULL)
547132904Spjd			continue;
548132904Spjd		g_access(disks[i].consumer, 0, -1, -1);
549132904Spjd		g_detach(disks[i].consumer);
550132904Spjd		g_destroy_consumer(disks[i].consumer);
551132904Spjd	}
552156610Spjd	g_topology_unlock();
553156610Spjd	sx_xunlock(&sc->sc_lock);
554132904Spjd	g_free(disks);
555132904Spjd}
556132904Spjd
557132904Spjdstatic void
558132904Spjdg_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
559132904Spjd{
560132904Spjd	struct g_mirror_softc *sc;
561132904Spjd	struct g_mirror_disk *disk;
562132904Spjd	const char *name;
563132904Spjd	char param[16];
564132904Spjd	int *nargs;
565235600Sae	u_int i, active;
566132904Spjd
567132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
568132904Spjd	if (nargs == NULL) {
569132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
570132904Spjd		return;
571132904Spjd	}
572132904Spjd	if (*nargs < 2) {
573132904Spjd		gctl_error(req, "Too few arguments.");
574132904Spjd		return;
575132904Spjd	}
576132904Spjd	name = gctl_get_asciiparam(req, "arg0");
577132904Spjd	if (name == NULL) {
578132904Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
579132904Spjd		return;
580132904Spjd	}
581132904Spjd	sc = g_mirror_find_device(mp, name);
582132904Spjd	if (sc == NULL) {
583132904Spjd		gctl_error(req, "No such device: %s.", name);
584132904Spjd		return;
585132904Spjd	}
586132904Spjd	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
587156610Spjd		sx_xunlock(&sc->sc_lock);
588156610Spjd		gctl_error(req, "Not all disks connected. Try 'forget' command "
589156610Spjd		    "first.");
590132904Spjd		return;
591132904Spjd	}
592235600Sae	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
593132904Spjd	for (i = 1; i < (u_int)*nargs; i++) {
594132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
595132904Spjd		name = gctl_get_asciiparam(req, param);
596132904Spjd		if (name == NULL) {
597132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
598139050Spjd			continue;
599132904Spjd		}
600132904Spjd		disk = g_mirror_find_disk(sc, name);
601132904Spjd		if (disk == NULL) {
602132904Spjd			gctl_error(req, "No such provider: %s.", name);
603139050Spjd			continue;
604132904Spjd		}
605235600Sae		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
606235600Sae			if (active > 1)
607235600Sae				active--;
608235600Sae			else {
609235600Sae				gctl_error(req, "%s: Can't remove the last "
610235600Sae				    "ACTIVE component %s.", sc->sc_geom->name,
611235600Sae				    name);
612235600Sae				continue;
613235600Sae			}
614235600Sae		}
615132904Spjd		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
616156610Spjd		    G_MIRROR_EVENT_DONTWAIT);
617132904Spjd	}
618156610Spjd	sx_xunlock(&sc->sc_lock);
619132904Spjd}
620132904Spjd
621132904Spjdstatic void
622260502Saeg_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
623260502Sae{
624260502Sae	struct g_mirror_softc *sc;
625260502Sae	struct g_mirror_disk *disk;
626260502Sae	uint64_t mediasize;
627260502Sae	const char *name, *s;
628260502Sae	char *x;
629260502Sae	int *nargs;
630260502Sae
631260502Sae	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
632260502Sae	if (nargs == NULL) {
633260502Sae		gctl_error(req, "No '%s' argument.", "nargs");
634260502Sae		return;
635260502Sae	}
636260502Sae	if (*nargs != 1) {
637260502Sae		gctl_error(req, "Missing device.");
638260502Sae		return;
639260502Sae	}
640260502Sae	name = gctl_get_asciiparam(req, "arg0");
641260502Sae	if (name == NULL) {
642260502Sae		gctl_error(req, "No 'arg%u' argument.", 0);
643260502Sae		return;
644260502Sae	}
645260502Sae	s = gctl_get_asciiparam(req, "size");
646260502Sae	if (s == NULL) {
647260502Sae		gctl_error(req, "No '%s' argument.", "size");
648260502Sae		return;
649260502Sae	}
650260502Sae	mediasize = strtouq(s, &x, 0);
651260502Sae	if (*x != '\0' || mediasize == 0) {
652260502Sae		gctl_error(req, "Invalid '%s' argument.", "size");
653260502Sae		return;
654260502Sae	}
655260502Sae	sc = g_mirror_find_device(mp, name);
656260502Sae	if (sc == NULL) {
657260502Sae		gctl_error(req, "No such device: %s.", name);
658260502Sae		return;
659260502Sae	}
660260502Sae	/* Deny shrinking of an opened provider */
661260502Sae	if ((g_debugflags & 16) == 0 && (sc->sc_provider->acr > 0 ||
662260502Sae	    sc->sc_provider->acw > 0 || sc->sc_provider->ace > 0)) {
663260502Sae		if (sc->sc_mediasize > mediasize) {
664260502Sae			gctl_error(req, "Device %s is busy.",
665260502Sae			    sc->sc_provider->name);
666260502Sae			sx_xunlock(&sc->sc_lock);
667260502Sae			return;
668260502Sae		}
669260502Sae	}
670260502Sae	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
671260502Sae		if (mediasize > disk->d_consumer->provider->mediasize -
672260502Sae		    disk->d_consumer->provider->sectorsize) {
673260502Sae			gctl_error(req, "Provider %s is too small.",
674260502Sae			    disk->d_name);
675260502Sae			sx_xunlock(&sc->sc_lock);
676260502Sae			return;
677260502Sae		}
678260502Sae	}
679260502Sae	/* Update the size. */
680260502Sae	sc->sc_mediasize = mediasize;
681260502Sae	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
682260502Sae		g_mirror_update_metadata(disk);
683260502Sae	}
684260502Sae	g_topology_lock();
685260502Sae	g_resize_provider(sc->sc_provider, mediasize);
686260502Sae	g_topology_unlock();
687260502Sae	sx_xunlock(&sc->sc_lock);
688260502Sae}
689260502Sae
690260502Saestatic void
691132904Spjdg_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
692132904Spjd{
693132904Spjd	struct g_mirror_softc *sc;
694132904Spjd	struct g_mirror_disk *disk;
695132904Spjd	const char *name;
696132904Spjd	char param[16];
697132904Spjd	int *nargs;
698260503Sae	u_int i, active;
699132904Spjd
700132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
701132904Spjd	if (nargs == NULL) {
702132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
703132904Spjd		return;
704132904Spjd	}
705132904Spjd	if (*nargs < 2) {
706132904Spjd		gctl_error(req, "Too few arguments.");
707132904Spjd		return;
708132904Spjd	}
709132904Spjd	name = gctl_get_asciiparam(req, "arg0");
710132904Spjd	if (name == NULL) {
711132904Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
712132904Spjd		return;
713132904Spjd	}
714132904Spjd	sc = g_mirror_find_device(mp, name);
715132904Spjd	if (sc == NULL) {
716132904Spjd		gctl_error(req, "No such device: %s.", name);
717132904Spjd		return;
718132904Spjd	}
719260503Sae	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
720132904Spjd	for (i = 1; i < (u_int)*nargs; i++) {
721132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
722132904Spjd		name = gctl_get_asciiparam(req, param);
723132904Spjd		if (name == NULL) {
724132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
725139050Spjd			continue;
726132904Spjd		}
727132904Spjd		disk = g_mirror_find_disk(sc, name);
728132904Spjd		if (disk == NULL) {
729132904Spjd			gctl_error(req, "No such provider: %s.", name);
730139050Spjd			continue;
731132904Spjd		}
732260503Sae		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
733260503Sae			if (active > 1)
734260503Sae				active--;
735260503Sae			else {
736260503Sae				gctl_error(req, "%s: Can't deactivate the "
737260503Sae				    "last ACTIVE component %s.",
738260503Sae				    sc->sc_geom->name, name);
739260503Sae				continue;
740260503Sae			}
741260503Sae		}
742132904Spjd		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
743132904Spjd		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
744132904Spjd		g_mirror_update_metadata(disk);
745139670Spjd		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
746132904Spjd		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
747156610Spjd		    G_MIRROR_EVENT_DONTWAIT);
748132904Spjd	}
749156610Spjd	sx_xunlock(&sc->sc_lock);
750132904Spjd}
751132904Spjd
752132904Spjdstatic void
753132904Spjdg_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
754132904Spjd{
755132904Spjd	struct g_mirror_softc *sc;
756132904Spjd	struct g_mirror_disk *disk;
757132904Spjd	const char *name;
758132904Spjd	char param[16];
759132904Spjd	int *nargs;
760132904Spjd	u_int i;
761132904Spjd
762132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
763132904Spjd	if (nargs == NULL) {
764132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
765132904Spjd		return;
766132904Spjd	}
767132904Spjd	if (*nargs < 1) {
768132904Spjd		gctl_error(req, "Missing device(s).");
769132904Spjd		return;
770132904Spjd	}
771132904Spjd
772132904Spjd	for (i = 0; i < (u_int)*nargs; i++) {
773132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
774132904Spjd		name = gctl_get_asciiparam(req, param);
775132904Spjd		if (name == NULL) {
776132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
777132904Spjd			return;
778132904Spjd		}
779132904Spjd		sc = g_mirror_find_device(mp, name);
780132904Spjd		if (sc == NULL) {
781132904Spjd			gctl_error(req, "No such device: %s.", name);
782132904Spjd			return;
783132904Spjd		}
784132904Spjd		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
785156610Spjd			sx_xunlock(&sc->sc_lock);
786132904Spjd			G_MIRROR_DEBUG(1,
787132904Spjd			    "All disks connected in %s, skipping.",
788132904Spjd			    sc->sc_name);
789132904Spjd			continue;
790132904Spjd		}
791132904Spjd		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
792132904Spjd		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
793132904Spjd			g_mirror_update_metadata(disk);
794132904Spjd		}
795156610Spjd		sx_xunlock(&sc->sc_lock);
796132904Spjd	}
797132904Spjd}
798132904Spjd
799132904Spjdstatic void
800260503Saeg_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
801132904Spjd{
802132904Spjd	struct g_mirror_softc *sc;
803132904Spjd	int *force, *nargs, error;
804132904Spjd	const char *name;
805132904Spjd	char param[16];
806132904Spjd	u_int i;
807157630Spjd	int how;
808132904Spjd
809132904Spjd	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
810132904Spjd	if (nargs == NULL) {
811132904Spjd		gctl_error(req, "No '%s' argument.", "nargs");
812132904Spjd		return;
813132904Spjd	}
814132904Spjd	if (*nargs < 1) {
815132904Spjd		gctl_error(req, "Missing device(s).");
816132904Spjd		return;
817132904Spjd	}
818132904Spjd	force = gctl_get_paraml(req, "force", sizeof(*force));
819132904Spjd	if (force == NULL) {
820132904Spjd		gctl_error(req, "No '%s' argument.", "force");
821132904Spjd		return;
822132904Spjd	}
823157630Spjd	if (*force)
824157630Spjd		how = G_MIRROR_DESTROY_HARD;
825157630Spjd	else
826157630Spjd		how = G_MIRROR_DESTROY_SOFT;
827132904Spjd
828132904Spjd	for (i = 0; i < (u_int)*nargs; i++) {
829132904Spjd		snprintf(param, sizeof(param), "arg%u", i);
830132904Spjd		name = gctl_get_asciiparam(req, param);
831132904Spjd		if (name == NULL) {
832132904Spjd			gctl_error(req, "No 'arg%u' argument.", i);
833132904Spjd			return;
834132904Spjd		}
835132904Spjd		sc = g_mirror_find_device(mp, name);
836132904Spjd		if (sc == NULL) {
837132904Spjd			gctl_error(req, "No such device: %s.", name);
838132904Spjd			return;
839132904Spjd		}
840157630Spjd		g_cancel_event(sc);
841260503Sae		if (wipe)
842260503Sae			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
843157630Spjd		error = g_mirror_destroy(sc, how);
844132904Spjd		if (error != 0) {
845132904Spjd			gctl_error(req, "Cannot destroy device %s (error=%d).",
846132904Spjd			    sc->sc_geom->name, error);
847260503Sae			if (wipe)
848260503Sae				sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
849156610Spjd			sx_xunlock(&sc->sc_lock);
850132904Spjd			return;
851132904Spjd		}
852156610Spjd		/* No need to unlock, because lock is already dead. */
853132904Spjd	}
854132904Spjd}
855132904Spjd
856132904Spjdvoid
857132904Spjdg_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
858132904Spjd{
859132904Spjd	uint32_t *version;
860132904Spjd
861132904Spjd	g_topology_assert();
862132904Spjd
863132904Spjd	version = gctl_get_paraml(req, "version", sizeof(*version));
864132904Spjd	if (version == NULL) {
865132904Spjd		gctl_error(req, "No '%s' argument.", "version");
866132904Spjd		return;
867132904Spjd	}
868132904Spjd	if (*version != G_MIRROR_VERSION) {
869132904Spjd		gctl_error(req, "Userland and kernel parts are out of sync.");
870132904Spjd		return;
871132904Spjd	}
872132904Spjd
873156610Spjd	g_topology_unlock();
874132904Spjd	if (strcmp(verb, "configure") == 0)
875132904Spjd		g_mirror_ctl_configure(req, mp);
876132904Spjd	else if (strcmp(verb, "rebuild") == 0)
877132904Spjd		g_mirror_ctl_rebuild(req, mp);
878132904Spjd	else if (strcmp(verb, "insert") == 0)
879132904Spjd		g_mirror_ctl_insert(req, mp);
880132904Spjd	else if (strcmp(verb, "remove") == 0)
881132904Spjd		g_mirror_ctl_remove(req, mp);
882260502Sae	else if (strcmp(verb, "resize") == 0)
883260502Sae		g_mirror_ctl_resize(req, mp);
884132904Spjd	else if (strcmp(verb, "deactivate") == 0)
885132904Spjd		g_mirror_ctl_deactivate(req, mp);
886132904Spjd	else if (strcmp(verb, "forget") == 0)
887132904Spjd		g_mirror_ctl_forget(req, mp);
888132904Spjd	else if (strcmp(verb, "stop") == 0)
889260503Sae		g_mirror_ctl_stop(req, mp, 0);
890260503Sae	else if (strcmp(verb, "destroy") == 0)
891260503Sae		g_mirror_ctl_stop(req, mp, 1);
892132904Spjd	else
893132904Spjd		gctl_error(req, "Unknown verb.");
894156610Spjd	g_topology_lock();
895132904Spjd}
896