g_mirror_ctl.c revision 259634
1/*-
2 * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/mirror/g_mirror_ctl.c 259634 2013-12-19 22:13:12Z ae $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/limits.h>
35#include <sys/lock.h>
36#include <sys/mutex.h>
37#include <sys/bio.h>
38#include <sys/sysctl.h>
39#include <sys/malloc.h>
40#include <sys/bitstring.h>
41#include <vm/uma.h>
42#include <machine/atomic.h>
43#include <geom/geom.h>
44#include <geom/geom_int.h>
45#include <sys/proc.h>
46#include <sys/kthread.h>
47#include <geom/mirror/g_mirror.h>
48
49
50static struct g_mirror_softc *
51g_mirror_find_device(struct g_class *mp, const char *name)
52{
53	struct g_mirror_softc *sc;
54	struct g_geom *gp;
55
56	g_topology_lock();
57	LIST_FOREACH(gp, &mp->geom, geom) {
58		sc = gp->softc;
59		if (sc == NULL)
60			continue;
61		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
62			continue;
63		if (strcmp(gp->name, name) == 0 ||
64		    strcmp(sc->sc_name, name) == 0) {
65			g_topology_unlock();
66			sx_xlock(&sc->sc_lock);
67			return (sc);
68		}
69	}
70	g_topology_unlock();
71	return (NULL);
72}
73
74static struct g_mirror_disk *
75g_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
76{
77	struct g_mirror_disk *disk;
78
79	sx_assert(&sc->sc_lock, SX_XLOCKED);
80	if (strncmp(name, "/dev/", 5) == 0)
81		name += 5;
82	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
83		if (disk->d_consumer == NULL)
84			continue;
85		if (disk->d_consumer->provider == NULL)
86			continue;
87		if (strcmp(disk->d_consumer->provider->name, name) == 0)
88			return (disk);
89	}
90	return (NULL);
91}
92
93static void
94g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
95{
96	struct g_mirror_softc *sc;
97	struct g_mirror_disk *disk;
98	const char *name, *balancep, *prov;
99	intmax_t *slicep, *priority;
100	uint32_t slice;
101	uint8_t balance;
102	int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
103	int *nargs, do_sync = 0, dirty = 1, do_priority = 0;
104
105	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
106	if (nargs == NULL) {
107		gctl_error(req, "No '%s' argument.", "nargs");
108		return;
109	}
110	if (*nargs != 1 && *nargs != 2) {
111		gctl_error(req, "Invalid number of arguments.");
112		return;
113	}
114	name = gctl_get_asciiparam(req, "arg0");
115	if (name == NULL) {
116		gctl_error(req, "No 'arg%u' argument.", 0);
117		return;
118	}
119	balancep = gctl_get_asciiparam(req, "balance");
120	if (balancep == NULL) {
121		gctl_error(req, "No '%s' argument.", "balance");
122		return;
123	}
124	autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
125	if (autosync == NULL) {
126		gctl_error(req, "No '%s' argument.", "autosync");
127		return;
128	}
129	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
130	if (noautosync == NULL) {
131		gctl_error(req, "No '%s' argument.", "noautosync");
132		return;
133	}
134	failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
135	if (failsync == NULL) {
136		gctl_error(req, "No '%s' argument.", "failsync");
137		return;
138	}
139	nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
140	if (nofailsync == NULL) {
141		gctl_error(req, "No '%s' argument.", "nofailsync");
142		return;
143	}
144	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
145	if (hardcode == NULL) {
146		gctl_error(req, "No '%s' argument.", "hardcode");
147		return;
148	}
149	dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
150	if (dynamic == NULL) {
151		gctl_error(req, "No '%s' argument.", "dynamic");
152		return;
153	}
154	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
155	if (priority == NULL) {
156		gctl_error(req, "No '%s' argument.", "priority");
157		return;
158	}
159	if (*priority < -1 || *priority > 255) {
160		gctl_error(req, "Priority range is 0 to 255, %jd given",
161		    *priority);
162		return;
163	}
164	/*
165	 * Since we have a priority, we also need a provider now.
166	 * Note: be WARNS safe, by always assigning prov and only throw an
167	 * error if *priority != -1.
168	 */
169	prov = gctl_get_asciiparam(req, "arg1");
170	if (*priority > -1) {
171		if (prov == NULL) {
172			gctl_error(req, "Priority needs a disk name");
173			return;
174		}
175		do_priority = 1;
176	}
177	if (*autosync && *noautosync) {
178		gctl_error(req, "'%s' and '%s' specified.", "autosync",
179		    "noautosync");
180		return;
181	}
182	if (*failsync && *nofailsync) {
183		gctl_error(req, "'%s' and '%s' specified.", "failsync",
184		    "nofailsync");
185		return;
186	}
187	if (*hardcode && *dynamic) {
188		gctl_error(req, "'%s' and '%s' specified.", "hardcode",
189		    "dynamic");
190		return;
191	}
192	sc = g_mirror_find_device(mp, name);
193	if (sc == NULL) {
194		gctl_error(req, "No such device: %s.", name);
195		return;
196	}
197	if (*balancep == '\0')
198		balance = sc->sc_balance;
199	else {
200		if (balance_id(balancep) == -1) {
201			gctl_error(req, "Invalid balance algorithm.");
202			sx_xunlock(&sc->sc_lock);
203			return;
204		}
205		balance = balance_id(balancep);
206	}
207	slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
208	if (slicep == NULL) {
209		gctl_error(req, "No '%s' argument.", "slice");
210		sx_xunlock(&sc->sc_lock);
211		return;
212	}
213	if (*slicep == -1)
214		slice = sc->sc_slice;
215	else
216		slice = *slicep;
217	/* Enforce usage() of -p not allowing any other options. */
218	if (do_priority && (*autosync || *noautosync || *failsync ||
219	    *nofailsync || *hardcode || *dynamic || *slicep != -1 ||
220	    *balancep != '\0')) {
221		sx_xunlock(&sc->sc_lock);
222		gctl_error(req, "only -p accepted when setting priority");
223		return;
224	}
225	if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
226	    !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
227	    !*dynamic && !do_priority) {
228		sx_xunlock(&sc->sc_lock);
229		gctl_error(req, "Nothing has changed.");
230		return;
231	}
232	if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) {
233		sx_xunlock(&sc->sc_lock);
234		gctl_error(req, "Invalid number of arguments.");
235		return;
236	}
237	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
238		sx_xunlock(&sc->sc_lock);
239		gctl_error(req, "Not all disks connected. Try 'forget' command "
240		    "first.");
241		return;
242	}
243	sc->sc_balance = balance;
244	sc->sc_slice = slice;
245	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
246		if (*autosync) {
247			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
248			do_sync = 1;
249		}
250	} else {
251		if (*noautosync)
252			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
253	}
254	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
255		if (*failsync)
256			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
257	} else {
258		if (*nofailsync) {
259			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
260			dirty = 0;
261		}
262	}
263	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
264		/*
265		 * Handle priority first, since we only need one disk, do one
266		 * operation on it and then we're done. No need to check other
267		 * flags, as usage doesn't allow it.
268		 */
269		if (do_priority) {
270			if (strcmp(disk->d_name, prov) == 0) {
271				if (disk->d_priority == *priority)
272					gctl_error(req, "Nothing has changed.");
273				else {
274					disk->d_priority = *priority;
275					g_mirror_update_metadata(disk);
276				}
277				break;
278			}
279			continue;
280		}
281		if (do_sync) {
282			if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
283				disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
284		}
285		if (*hardcode)
286			disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
287		else if (*dynamic)
288			disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
289		if (!dirty)
290			disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
291		g_mirror_update_metadata(disk);
292		if (do_sync) {
293			if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
294				g_mirror_event_send(disk,
295				    G_MIRROR_DISK_STATE_DISCONNECTED,
296				    G_MIRROR_EVENT_DONTWAIT);
297			}
298		}
299	}
300	sx_xunlock(&sc->sc_lock);
301}
302
303static void
304g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
305{
306	struct g_mirror_metadata md;
307	struct g_mirror_softc *sc;
308	struct g_mirror_disk *disk;
309	struct g_provider *pp;
310	const char *name;
311	char param[16];
312	int error, *nargs;
313	u_int i;
314
315	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
316	if (nargs == NULL) {
317		gctl_error(req, "No '%s' argument.", "nargs");
318		return;
319	}
320	if (*nargs < 2) {
321		gctl_error(req, "Too few arguments.");
322		return;
323	}
324	name = gctl_get_asciiparam(req, "arg0");
325	if (name == NULL) {
326		gctl_error(req, "No 'arg%u' argument.", 0);
327		return;
328	}
329	sc = g_mirror_find_device(mp, name);
330	if (sc == NULL) {
331		gctl_error(req, "No such device: %s.", name);
332		return;
333	}
334	for (i = 1; i < (u_int)*nargs; i++) {
335		snprintf(param, sizeof(param), "arg%u", i);
336		name = gctl_get_asciiparam(req, param);
337		if (name == NULL) {
338			gctl_error(req, "No 'arg%u' argument.", i);
339			continue;
340		}
341		disk = g_mirror_find_disk(sc, name);
342		if (disk == NULL) {
343			gctl_error(req, "No such provider: %s.", name);
344			continue;
345		}
346		if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
347		    disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
348			/*
349			 * This is the last active disk. There will be nothing
350			 * to rebuild it from, so deny this request.
351			 */
352			gctl_error(req,
353			    "Provider %s is the last active provider in %s.",
354			    name, sc->sc_geom->name);
355			break;
356		}
357		/*
358		 * Do rebuild by resetting syncid, disconnecting the disk and
359		 * connecting it again.
360		 */
361		disk->d_sync.ds_syncid = 0;
362		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
363			disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
364		g_mirror_update_metadata(disk);
365		pp = disk->d_consumer->provider;
366		g_topology_lock();
367		error = g_mirror_read_metadata(disk->d_consumer, &md);
368		g_topology_unlock();
369		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
370		    G_MIRROR_EVENT_WAIT);
371		if (error != 0) {
372			gctl_error(req, "Cannot read metadata from %s.",
373			    pp->name);
374			continue;
375		}
376		error = g_mirror_add_disk(sc, pp, &md);
377		if (error != 0) {
378			gctl_error(req, "Cannot reconnect component %s.",
379			    pp->name);
380			continue;
381		}
382	}
383	sx_xunlock(&sc->sc_lock);
384}
385
386static void
387g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
388{
389	struct g_mirror_softc *sc;
390	struct g_mirror_disk *disk;
391	struct g_mirror_metadata md;
392	struct g_provider *pp;
393	struct g_consumer *cp;
394	intmax_t *priority;
395	const char *name;
396	char param[16];
397	u_char *sector;
398	u_int i, n;
399	int error, *nargs, *hardcode, *inactive;
400	struct {
401		struct g_provider	*provider;
402		struct g_consumer	*consumer;
403	} *disks;
404
405	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
406	if (nargs == NULL) {
407		gctl_error(req, "No '%s' argument.", "nargs");
408		return;
409	}
410	if (*nargs < 2) {
411		gctl_error(req, "Too few arguments.");
412		return;
413	}
414	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
415	if (priority == NULL) {
416		gctl_error(req, "No '%s' argument.", "priority");
417		return;
418	}
419	inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
420	if (inactive == NULL) {
421		gctl_error(req, "No '%s' argument.", "inactive");
422		return;
423	}
424	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
425	if (hardcode == NULL) {
426		gctl_error(req, "No '%s' argument.", "hardcode");
427		return;
428	}
429	name = gctl_get_asciiparam(req, "arg0");
430	if (name == NULL) {
431		gctl_error(req, "No 'arg%u' argument.", 0);
432		return;
433	}
434	sc = g_mirror_find_device(mp, name);
435	if (sc == NULL) {
436		gctl_error(req, "No such device: %s.", name);
437		return;
438	}
439	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
440		gctl_error(req, "Not all disks connected.");
441		sx_xunlock(&sc->sc_lock);
442		return;
443	}
444
445	disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
446	g_topology_lock();
447	for (i = 1, n = 0; i < (u_int)*nargs; i++) {
448		snprintf(param, sizeof(param), "arg%u", i);
449		name = gctl_get_asciiparam(req, param);
450		if (name == NULL) {
451			gctl_error(req, "No 'arg%u' argument.", i);
452			continue;
453		}
454		if (g_mirror_find_disk(sc, name) != NULL) {
455			gctl_error(req, "Provider %s already inserted.", name);
456			continue;
457		}
458		if (strncmp(name, "/dev/", 5) == 0)
459			name += 5;
460		pp = g_provider_by_name(name);
461		if (pp == NULL) {
462			gctl_error(req, "Unknown provider %s.", name);
463			continue;
464		}
465		if (sc->sc_provider->mediasize >
466		    pp->mediasize - pp->sectorsize) {
467			gctl_error(req, "Provider %s too small.", name);
468			continue;
469		}
470		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
471			gctl_error(req, "Invalid sectorsize of provider %s.",
472			    name);
473			continue;
474		}
475		cp = g_new_consumer(sc->sc_geom);
476		if (g_attach(cp, pp) != 0) {
477			g_destroy_consumer(cp);
478			gctl_error(req, "Cannot attach to provider %s.", name);
479			continue;
480		}
481		if (g_access(cp, 0, 1, 1) != 0) {
482			g_detach(cp);
483			g_destroy_consumer(cp);
484			gctl_error(req, "Cannot access provider %s.", name);
485			continue;
486		}
487		disks[n].provider = pp;
488		disks[n].consumer = cp;
489		n++;
490	}
491	if (n == 0) {
492		g_topology_unlock();
493		sx_xunlock(&sc->sc_lock);
494		g_free(disks);
495		return;
496	}
497	sc->sc_ndisks += n;
498again:
499	for (i = 0; i < n; i++) {
500		if (disks[i].consumer == NULL)
501			continue;
502		g_mirror_fill_metadata(sc, NULL, &md);
503		md.md_priority = *priority;
504		if (*inactive)
505			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
506		pp = disks[i].provider;
507		if (*hardcode) {
508			strlcpy(md.md_provider, pp->name,
509			    sizeof(md.md_provider));
510		} else {
511			bzero(md.md_provider, sizeof(md.md_provider));
512		}
513		md.md_provsize = pp->mediasize;
514		sector = g_malloc(pp->sectorsize, M_WAITOK);
515		mirror_metadata_encode(&md, sector);
516		error = g_write_data(disks[i].consumer,
517		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
518		g_free(sector);
519		if (error != 0) {
520			gctl_error(req, "Cannot store metadata on %s.",
521			    pp->name);
522			g_access(disks[i].consumer, 0, -1, -1);
523			g_detach(disks[i].consumer);
524			g_destroy_consumer(disks[i].consumer);
525			disks[i].consumer = NULL;
526			disks[i].provider = NULL;
527			sc->sc_ndisks--;
528			goto again;
529		}
530	}
531	g_topology_unlock();
532	if (i == 0) {
533		/* All writes failed. */
534		sx_xunlock(&sc->sc_lock);
535		g_free(disks);
536		return;
537	}
538	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
539		g_mirror_update_metadata(disk);
540	}
541	/*
542	 * Release provider and wait for retaste.
543	 */
544	g_topology_lock();
545	for (i = 0; i < n; i++) {
546		if (disks[i].consumer == NULL)
547			continue;
548		g_access(disks[i].consumer, 0, -1, -1);
549		g_detach(disks[i].consumer);
550		g_destroy_consumer(disks[i].consumer);
551	}
552	g_topology_unlock();
553	sx_xunlock(&sc->sc_lock);
554	g_free(disks);
555}
556
557static void
558g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
559{
560	struct g_mirror_softc *sc;
561	struct g_mirror_disk *disk;
562	const char *name;
563	char param[16];
564	int *nargs;
565	u_int i, active;
566
567	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
568	if (nargs == NULL) {
569		gctl_error(req, "No '%s' argument.", "nargs");
570		return;
571	}
572	if (*nargs < 2) {
573		gctl_error(req, "Too few arguments.");
574		return;
575	}
576	name = gctl_get_asciiparam(req, "arg0");
577	if (name == NULL) {
578		gctl_error(req, "No 'arg%u' argument.", 0);
579		return;
580	}
581	sc = g_mirror_find_device(mp, name);
582	if (sc == NULL) {
583		gctl_error(req, "No such device: %s.", name);
584		return;
585	}
586	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
587		sx_xunlock(&sc->sc_lock);
588		gctl_error(req, "Not all disks connected. Try 'forget' command "
589		    "first.");
590		return;
591	}
592	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
593	for (i = 1; i < (u_int)*nargs; i++) {
594		snprintf(param, sizeof(param), "arg%u", i);
595		name = gctl_get_asciiparam(req, param);
596		if (name == NULL) {
597			gctl_error(req, "No 'arg%u' argument.", i);
598			continue;
599		}
600		disk = g_mirror_find_disk(sc, name);
601		if (disk == NULL) {
602			gctl_error(req, "No such provider: %s.", name);
603			continue;
604		}
605		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
606			if (active > 1)
607				active--;
608			else {
609				gctl_error(req, "%s: Can't remove the last "
610				    "ACTIVE component %s.", sc->sc_geom->name,
611				    name);
612				continue;
613			}
614		}
615		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
616		    G_MIRROR_EVENT_DONTWAIT);
617	}
618	sx_xunlock(&sc->sc_lock);
619}
620
621static void
622g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
623{
624	struct g_mirror_softc *sc;
625	struct g_mirror_disk *disk;
626	uint64_t mediasize;
627	const char *name, *s;
628	char *x;
629	int *nargs;
630
631	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
632	if (nargs == NULL) {
633		gctl_error(req, "No '%s' argument.", "nargs");
634		return;
635	}
636	if (*nargs != 1) {
637		gctl_error(req, "Missing device.");
638		return;
639	}
640	name = gctl_get_asciiparam(req, "arg0");
641	if (name == NULL) {
642		gctl_error(req, "No 'arg%u' argument.", 0);
643		return;
644	}
645	s = gctl_get_asciiparam(req, "size");
646	if (s == NULL) {
647		gctl_error(req, "No '%s' argument.", "size");
648		return;
649	}
650	mediasize = strtouq(s, &x, 0);
651	if (*x != '\0' || mediasize == 0) {
652		gctl_error(req, "Invalid '%s' argument.", "size");
653		return;
654	}
655	sc = g_mirror_find_device(mp, name);
656	if (sc == NULL) {
657		gctl_error(req, "No such device: %s.", name);
658		return;
659	}
660	/* Deny shrinking of an opened provider */
661	if ((g_debugflags & 16) == 0 && (sc->sc_provider->acr > 0 ||
662	    sc->sc_provider->acw > 0 || sc->sc_provider->ace > 0)) {
663		if (sc->sc_mediasize > mediasize) {
664			gctl_error(req, "Device %s is busy.",
665			    sc->sc_provider->name);
666			sx_xunlock(&sc->sc_lock);
667			return;
668		}
669	}
670	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
671		if (mediasize > disk->d_consumer->provider->mediasize -
672		    disk->d_consumer->provider->sectorsize) {
673			gctl_error(req, "Provider %s is too small.",
674			    disk->d_name);
675			sx_xunlock(&sc->sc_lock);
676			return;
677		}
678	}
679	/* Update the size. */
680	sc->sc_mediasize = mediasize;
681	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
682		g_mirror_update_metadata(disk);
683	}
684	g_topology_lock();
685	g_resize_provider(sc->sc_provider, mediasize);
686	g_topology_unlock();
687	sx_xunlock(&sc->sc_lock);
688}
689
690static void
691g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
692{
693	struct g_mirror_softc *sc;
694	struct g_mirror_disk *disk;
695	const char *name;
696	char param[16];
697	int *nargs;
698	u_int i, active;
699
700	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
701	if (nargs == NULL) {
702		gctl_error(req, "No '%s' argument.", "nargs");
703		return;
704	}
705	if (*nargs < 2) {
706		gctl_error(req, "Too few arguments.");
707		return;
708	}
709	name = gctl_get_asciiparam(req, "arg0");
710	if (name == NULL) {
711		gctl_error(req, "No 'arg%u' argument.", 0);
712		return;
713	}
714	sc = g_mirror_find_device(mp, name);
715	if (sc == NULL) {
716		gctl_error(req, "No such device: %s.", name);
717		return;
718	}
719	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
720	for (i = 1; i < (u_int)*nargs; i++) {
721		snprintf(param, sizeof(param), "arg%u", i);
722		name = gctl_get_asciiparam(req, param);
723		if (name == NULL) {
724			gctl_error(req, "No 'arg%u' argument.", i);
725			continue;
726		}
727		disk = g_mirror_find_disk(sc, name);
728		if (disk == NULL) {
729			gctl_error(req, "No such provider: %s.", name);
730			continue;
731		}
732		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
733			if (active > 1)
734				active--;
735			else {
736				gctl_error(req, "%s: Can't deactivate the "
737				    "last ACTIVE component %s.",
738				    sc->sc_geom->name, name);
739				continue;
740			}
741		}
742		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
743		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
744		g_mirror_update_metadata(disk);
745		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
746		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
747		    G_MIRROR_EVENT_DONTWAIT);
748	}
749	sx_xunlock(&sc->sc_lock);
750}
751
752static void
753g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
754{
755	struct g_mirror_softc *sc;
756	struct g_mirror_disk *disk;
757	const char *name;
758	char param[16];
759	int *nargs;
760	u_int i;
761
762	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
763	if (nargs == NULL) {
764		gctl_error(req, "No '%s' argument.", "nargs");
765		return;
766	}
767	if (*nargs < 1) {
768		gctl_error(req, "Missing device(s).");
769		return;
770	}
771
772	for (i = 0; i < (u_int)*nargs; i++) {
773		snprintf(param, sizeof(param), "arg%u", i);
774		name = gctl_get_asciiparam(req, param);
775		if (name == NULL) {
776			gctl_error(req, "No 'arg%u' argument.", i);
777			return;
778		}
779		sc = g_mirror_find_device(mp, name);
780		if (sc == NULL) {
781			gctl_error(req, "No such device: %s.", name);
782			return;
783		}
784		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
785			sx_xunlock(&sc->sc_lock);
786			G_MIRROR_DEBUG(1,
787			    "All disks connected in %s, skipping.",
788			    sc->sc_name);
789			continue;
790		}
791		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
792		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
793			g_mirror_update_metadata(disk);
794		}
795		sx_xunlock(&sc->sc_lock);
796	}
797}
798
799static void
800g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp)
801{
802	struct g_mirror_softc *sc;
803	int *force, *nargs, error;
804	const char *name;
805	char param[16];
806	u_int i;
807	int how;
808
809	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
810	if (nargs == NULL) {
811		gctl_error(req, "No '%s' argument.", "nargs");
812		return;
813	}
814	if (*nargs < 1) {
815		gctl_error(req, "Missing device(s).");
816		return;
817	}
818	force = gctl_get_paraml(req, "force", sizeof(*force));
819	if (force == NULL) {
820		gctl_error(req, "No '%s' argument.", "force");
821		return;
822	}
823	if (*force)
824		how = G_MIRROR_DESTROY_HARD;
825	else
826		how = G_MIRROR_DESTROY_SOFT;
827
828	for (i = 0; i < (u_int)*nargs; i++) {
829		snprintf(param, sizeof(param), "arg%u", i);
830		name = gctl_get_asciiparam(req, param);
831		if (name == NULL) {
832			gctl_error(req, "No 'arg%u' argument.", i);
833			return;
834		}
835		sc = g_mirror_find_device(mp, name);
836		if (sc == NULL) {
837			gctl_error(req, "No such device: %s.", name);
838			return;
839		}
840		g_cancel_event(sc);
841		error = g_mirror_destroy(sc, how);
842		if (error != 0) {
843			gctl_error(req, "Cannot destroy device %s (error=%d).",
844			    sc->sc_geom->name, error);
845			sx_xunlock(&sc->sc_lock);
846			return;
847		}
848		/* No need to unlock, because lock is already dead. */
849	}
850}
851
852void
853g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
854{
855	uint32_t *version;
856
857	g_topology_assert();
858
859	version = gctl_get_paraml(req, "version", sizeof(*version));
860	if (version == NULL) {
861		gctl_error(req, "No '%s' argument.", "version");
862		return;
863	}
864	if (*version != G_MIRROR_VERSION) {
865		gctl_error(req, "Userland and kernel parts are out of sync.");
866		return;
867	}
868
869	g_topology_unlock();
870	if (strcmp(verb, "configure") == 0)
871		g_mirror_ctl_configure(req, mp);
872	else if (strcmp(verb, "rebuild") == 0)
873		g_mirror_ctl_rebuild(req, mp);
874	else if (strcmp(verb, "insert") == 0)
875		g_mirror_ctl_insert(req, mp);
876	else if (strcmp(verb, "remove") == 0)
877		g_mirror_ctl_remove(req, mp);
878	else if (strcmp(verb, "resize") == 0)
879		g_mirror_ctl_resize(req, mp);
880	else if (strcmp(verb, "deactivate") == 0)
881		g_mirror_ctl_deactivate(req, mp);
882	else if (strcmp(verb, "forget") == 0)
883		g_mirror_ctl_forget(req, mp);
884	else if (strcmp(verb, "stop") == 0)
885		g_mirror_ctl_stop(req, mp);
886	else
887		gctl_error(req, "Unknown verb.");
888	g_topology_lock();
889}
890