xref: /freebsd/sys/geom/mirror/g_mirror_ctl.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*-
2  * Copyright (c) 2004-2006 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$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/bio.h>
37 #include <sys/sysctl.h>
38 #include <sys/malloc.h>
39 #include <sys/bitstring.h>
40 #include <vm/uma.h>
41 #include <machine/atomic.h>
42 #include <geom/geom.h>
43 #include <sys/proc.h>
44 #include <sys/kthread.h>
45 #include <geom/mirror/g_mirror.h>
46 
47 
48 static struct g_mirror_softc *
49 g_mirror_find_device(struct g_class *mp, const char *name)
50 {
51 	struct g_mirror_softc *sc;
52 	struct g_geom *gp;
53 
54 	g_topology_lock();
55 	LIST_FOREACH(gp, &mp->geom, geom) {
56 		sc = gp->softc;
57 		if (sc == NULL)
58 			continue;
59 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
60 			continue;
61 		if (strcmp(gp->name, name) == 0 ||
62 		    strcmp(sc->sc_name, name) == 0) {
63 			g_topology_unlock();
64 			sx_xlock(&sc->sc_lock);
65 			return (sc);
66 		}
67 	}
68 	g_topology_unlock();
69 	return (NULL);
70 }
71 
72 static struct g_mirror_disk *
73 g_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
74 {
75 	struct g_mirror_disk *disk;
76 
77 	sx_assert(&sc->sc_lock, SX_XLOCKED);
78 	if (strncmp(name, "/dev/", 5) == 0)
79 		name += 5;
80 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
81 		if (disk->d_consumer == NULL)
82 			continue;
83 		if (disk->d_consumer->provider == NULL)
84 			continue;
85 		if (strcmp(disk->d_consumer->provider->name, name) == 0)
86 			return (disk);
87 	}
88 	return (NULL);
89 }
90 
91 static void
92 g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
93 {
94 	struct g_mirror_softc *sc;
95 	struct g_mirror_disk *disk;
96 	const char *name, *balancep;
97 	intmax_t *slicep;
98 	uint32_t slice;
99 	uint8_t balance;
100 	int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
101 	int *nargs, do_sync = 0, dirty = 1;
102 
103 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
104 	if (nargs == NULL) {
105 		gctl_error(req, "No '%s' argument.", "nargs");
106 		return;
107 	}
108 	if (*nargs != 1) {
109 		gctl_error(req, "Invalid number of arguments.");
110 		return;
111 	}
112 	name = gctl_get_asciiparam(req, "arg0");
113 	if (name == NULL) {
114 		gctl_error(req, "No 'arg%u' argument.", 0);
115 		return;
116 	}
117 	balancep = gctl_get_asciiparam(req, "balance");
118 	if (balancep == NULL) {
119 		gctl_error(req, "No '%s' argument.", "balance");
120 		return;
121 	}
122 	autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
123 	if (autosync == NULL) {
124 		gctl_error(req, "No '%s' argument.", "autosync");
125 		return;
126 	}
127 	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
128 	if (noautosync == NULL) {
129 		gctl_error(req, "No '%s' argument.", "noautosync");
130 		return;
131 	}
132 	failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
133 	if (failsync == NULL) {
134 		gctl_error(req, "No '%s' argument.", "failsync");
135 		return;
136 	}
137 	nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
138 	if (nofailsync == NULL) {
139 		gctl_error(req, "No '%s' argument.", "nofailsync");
140 		return;
141 	}
142 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
143 	if (hardcode == NULL) {
144 		gctl_error(req, "No '%s' argument.", "hardcode");
145 		return;
146 	}
147 	dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
148 	if (dynamic == NULL) {
149 		gctl_error(req, "No '%s' argument.", "dynamic");
150 		return;
151 	}
152 	if (*autosync && *noautosync) {
153 		gctl_error(req, "'%s' and '%s' specified.", "autosync",
154 		    "noautosync");
155 		return;
156 	}
157 	if (*failsync && *nofailsync) {
158 		gctl_error(req, "'%s' and '%s' specified.", "failsync",
159 		    "nofailsync");
160 		return;
161 	}
162 	if (*hardcode && *dynamic) {
163 		gctl_error(req, "'%s' and '%s' specified.", "hardcode",
164 		    "dynamic");
165 		return;
166 	}
167 	sc = g_mirror_find_device(mp, name);
168 	if (sc == NULL) {
169 		gctl_error(req, "No such device: %s.", name);
170 		return;
171 	}
172 	if (strcmp(balancep, "none") == 0)
173 		balance = sc->sc_balance;
174 	else {
175 		if (balance_id(balancep) == -1) {
176 			gctl_error(req, "Invalid balance algorithm.");
177 			sx_xunlock(&sc->sc_lock);
178 			return;
179 		}
180 		balance = balance_id(balancep);
181 	}
182 	slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
183 	if (slicep == NULL) {
184 		gctl_error(req, "No '%s' argument.", "slice");
185 		sx_xunlock(&sc->sc_lock);
186 		return;
187 	}
188 	if (*slicep == -1)
189 		slice = sc->sc_slice;
190 	else
191 		slice = *slicep;
192 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
193 		sx_xunlock(&sc->sc_lock);
194 		gctl_error(req, "Not all disks connected. Try 'forget' command "
195 		    "first.");
196 		return;
197 	}
198 	if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
199 	    !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
200 	    !*dynamic) {
201 		sx_xunlock(&sc->sc_lock);
202 		gctl_error(req, "Nothing has changed.");
203 		return;
204 	}
205 	sc->sc_balance = balance;
206 	sc->sc_slice = slice;
207 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
208 		if (*autosync) {
209 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
210 			do_sync = 1;
211 		}
212 	} else {
213 		if (*noautosync)
214 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
215 	}
216 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
217 		if (*failsync)
218 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
219 	} else {
220 		if (*nofailsync) {
221 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
222 			dirty = 0;
223 		}
224 	}
225 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
226 		if (do_sync) {
227 			if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
228 				disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
229 		}
230 		if (*hardcode)
231 			disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
232 		else if (*dynamic)
233 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
234 		if (!dirty)
235 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
236 		g_mirror_update_metadata(disk);
237 		if (do_sync) {
238 			if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
239 				g_mirror_event_send(disk,
240 				    G_MIRROR_DISK_STATE_DISCONNECTED,
241 				    G_MIRROR_EVENT_DONTWAIT);
242 			}
243 		}
244 	}
245 	sx_xunlock(&sc->sc_lock);
246 }
247 
248 static void
249 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
250 {
251 	struct g_mirror_metadata md;
252 	struct g_mirror_softc *sc;
253 	struct g_mirror_disk *disk;
254 	struct g_provider *pp;
255 	const char *name;
256 	char param[16];
257 	int error, *nargs;
258 	u_int i;
259 
260 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
261 	if (nargs == NULL) {
262 		gctl_error(req, "No '%s' argument.", "nargs");
263 		return;
264 	}
265 	if (*nargs < 2) {
266 		gctl_error(req, "Too few arguments.");
267 		return;
268 	}
269 	name = gctl_get_asciiparam(req, "arg0");
270 	if (name == NULL) {
271 		gctl_error(req, "No 'arg%u' argument.", 0);
272 		return;
273 	}
274 	sc = g_mirror_find_device(mp, name);
275 	if (sc == NULL) {
276 		gctl_error(req, "No such device: %s.", name);
277 		return;
278 	}
279 	for (i = 1; i < (u_int)*nargs; i++) {
280 		snprintf(param, sizeof(param), "arg%u", i);
281 		name = gctl_get_asciiparam(req, param);
282 		if (name == NULL) {
283 			gctl_error(req, "No 'arg%u' argument.", i);
284 			continue;
285 		}
286 		disk = g_mirror_find_disk(sc, name);
287 		if (disk == NULL) {
288 			gctl_error(req, "No such provider: %s.", name);
289 			continue;
290 		}
291 		if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
292 		    disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
293 			/*
294 			 * This is the last active disk. There will be nothing
295 			 * to rebuild it from, so deny this request.
296 			 */
297 			gctl_error(req,
298 			    "Provider %s is the last active provider in %s.",
299 			    name, sc->sc_geom->name);
300 			break;
301 		}
302 		/*
303 		 * Do rebuild by resetting syncid, disconnecting the disk and
304 		 * connecting it again.
305 		 */
306 		disk->d_sync.ds_syncid = 0;
307 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
308 			disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
309 		g_mirror_update_metadata(disk);
310 		pp = disk->d_consumer->provider;
311 		g_topology_lock();
312 		error = g_mirror_read_metadata(disk->d_consumer, &md);
313 		g_topology_unlock();
314 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
315 		    G_MIRROR_EVENT_WAIT);
316 		if (error != 0) {
317 			gctl_error(req, "Cannot read metadata from %s.",
318 			    pp->name);
319 			continue;
320 		}
321 		error = g_mirror_add_disk(sc, pp, &md);
322 		if (error != 0) {
323 			gctl_error(req, "Cannot reconnect component %s.",
324 			    pp->name);
325 			continue;
326 		}
327 	}
328 	sx_xunlock(&sc->sc_lock);
329 }
330 
331 static void
332 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
333 {
334 	struct g_mirror_softc *sc;
335 	struct g_mirror_disk *disk;
336 	struct g_mirror_metadata md;
337 	struct g_provider *pp;
338 	struct g_consumer *cp;
339 	intmax_t *priority;
340 	const char *name;
341 	char param[16];
342 	u_char *sector;
343 	u_int i, n;
344 	int error, *nargs, *hardcode, *inactive;
345 	struct {
346 		struct g_provider	*provider;
347 		struct g_consumer	*consumer;
348 	} *disks;
349 
350 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
351 	if (nargs == NULL) {
352 		gctl_error(req, "No '%s' argument.", "nargs");
353 		return;
354 	}
355 	if (*nargs < 2) {
356 		gctl_error(req, "Too few arguments.");
357 		return;
358 	}
359 	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
360 	if (priority == NULL) {
361 		gctl_error(req, "No '%s' argument.", "priority");
362 		return;
363 	}
364 	inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
365 	if (inactive == NULL) {
366 		gctl_error(req, "No '%s' argument.", "inactive");
367 		return;
368 	}
369 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
370 	if (hardcode == NULL) {
371 		gctl_error(req, "No '%s' argument.", "hardcode");
372 		return;
373 	}
374 	name = gctl_get_asciiparam(req, "arg0");
375 	if (name == NULL) {
376 		gctl_error(req, "No 'arg%u' argument.", 0);
377 		return;
378 	}
379 	sc = g_mirror_find_device(mp, name);
380 	if (sc == NULL) {
381 		gctl_error(req, "No such device: %s.", name);
382 		return;
383 	}
384 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
385 		gctl_error(req, "Not all disks connected.");
386 		sx_xunlock(&sc->sc_lock);
387 		return;
388 	}
389 
390 	disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
391 	g_topology_lock();
392 	for (i = 1, n = 0; i < (u_int)*nargs; i++) {
393 		snprintf(param, sizeof(param), "arg%u", i);
394 		name = gctl_get_asciiparam(req, param);
395 		if (name == NULL) {
396 			gctl_error(req, "No 'arg%u' argument.", i);
397 			continue;
398 		}
399 		if (g_mirror_find_disk(sc, name) != NULL) {
400 			gctl_error(req, "Provider %s already inserted.", name);
401 			continue;
402 		}
403 		if (strncmp(name, "/dev/", 5) == 0)
404 			name += 5;
405 		pp = g_provider_by_name(name);
406 		if (pp == NULL) {
407 			gctl_error(req, "Unknown provider %s.", name);
408 			continue;
409 		}
410 		if (sc->sc_provider->mediasize >
411 		    pp->mediasize - pp->sectorsize) {
412 			gctl_error(req, "Provider %s too small.", name);
413 			continue;
414 		}
415 		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
416 			gctl_error(req, "Invalid sectorsize of provider %s.",
417 			    name);
418 			continue;
419 		}
420 		cp = g_new_consumer(sc->sc_geom);
421 		if (g_attach(cp, pp) != 0) {
422 			g_destroy_consumer(cp);
423 			gctl_error(req, "Cannot attach to provider %s.", name);
424 			continue;
425 		}
426 		if (g_access(cp, 0, 1, 1) != 0) {
427 			g_detach(cp);
428 			g_destroy_consumer(cp);
429 			gctl_error(req, "Cannot access provider %s.", name);
430 			continue;
431 		}
432 		disks[n].provider = pp;
433 		disks[n].consumer = cp;
434 		n++;
435 	}
436 	if (n == 0) {
437 		g_topology_unlock();
438 		sx_xunlock(&sc->sc_lock);
439 		g_free(disks);
440 		return;
441 	}
442 	sc->sc_ndisks += n;
443 again:
444 	for (i = 0; i < n; i++) {
445 		if (disks[i].consumer == NULL)
446 			continue;
447 		g_mirror_fill_metadata(sc, NULL, &md);
448 		md.md_priority = *priority;
449 		if (*inactive)
450 			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
451 		pp = disks[i].provider;
452 		if (*hardcode) {
453 			strlcpy(md.md_provider, pp->name,
454 			    sizeof(md.md_provider));
455 		} else {
456 			bzero(md.md_provider, sizeof(md.md_provider));
457 		}
458 		md.md_provsize = pp->mediasize;
459 		sector = g_malloc(pp->sectorsize, M_WAITOK);
460 		mirror_metadata_encode(&md, sector);
461 		error = g_write_data(disks[i].consumer,
462 		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
463 		g_free(sector);
464 		if (error != 0) {
465 			gctl_error(req, "Cannot store metadata on %s.",
466 			    pp->name);
467 			g_access(disks[i].consumer, 0, -1, -1);
468 			g_detach(disks[i].consumer);
469 			g_destroy_consumer(disks[i].consumer);
470 			disks[i].consumer = NULL;
471 			disks[i].provider = NULL;
472 			sc->sc_ndisks--;
473 			goto again;
474 		}
475 	}
476 	g_topology_unlock();
477 	if (i == 0) {
478 		/* All writes failed. */
479 		sx_xunlock(&sc->sc_lock);
480 		g_free(disks);
481 		return;
482 	}
483 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
484 		g_mirror_update_metadata(disk);
485 	}
486 	/*
487 	 * Release provider and wait for retaste.
488 	 */
489 	g_topology_lock();
490 	for (i = 0; i < n; i++) {
491 		if (disks[i].consumer == NULL)
492 			continue;
493 		g_access(disks[i].consumer, 0, -1, -1);
494 		g_detach(disks[i].consumer);
495 		g_destroy_consumer(disks[i].consumer);
496 	}
497 	g_topology_unlock();
498 	sx_xunlock(&sc->sc_lock);
499 	g_free(disks);
500 }
501 
502 static void
503 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
504 {
505 	struct g_mirror_softc *sc;
506 	struct g_mirror_disk *disk;
507 	const char *name;
508 	char param[16];
509 	int *nargs;
510 	u_int i;
511 
512 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
513 	if (nargs == NULL) {
514 		gctl_error(req, "No '%s' argument.", "nargs");
515 		return;
516 	}
517 	if (*nargs < 2) {
518 		gctl_error(req, "Too few arguments.");
519 		return;
520 	}
521 	name = gctl_get_asciiparam(req, "arg0");
522 	if (name == NULL) {
523 		gctl_error(req, "No 'arg%u' argument.", 0);
524 		return;
525 	}
526 	sc = g_mirror_find_device(mp, name);
527 	if (sc == NULL) {
528 		gctl_error(req, "No such device: %s.", name);
529 		return;
530 	}
531 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
532 		sx_xunlock(&sc->sc_lock);
533 		gctl_error(req, "Not all disks connected. Try 'forget' command "
534 		    "first.");
535 		return;
536 	}
537 	for (i = 1; i < (u_int)*nargs; i++) {
538 		snprintf(param, sizeof(param), "arg%u", i);
539 		name = gctl_get_asciiparam(req, param);
540 		if (name == NULL) {
541 			gctl_error(req, "No 'arg%u' argument.", i);
542 			continue;
543 		}
544 		disk = g_mirror_find_disk(sc, name);
545 		if (disk == NULL) {
546 			gctl_error(req, "No such provider: %s.", name);
547 			continue;
548 		}
549 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
550 		    G_MIRROR_EVENT_DONTWAIT);
551 	}
552 	sx_xunlock(&sc->sc_lock);
553 }
554 
555 static void
556 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
557 {
558 	struct g_mirror_softc *sc;
559 	struct g_mirror_disk *disk;
560 	const char *name;
561 	char param[16];
562 	int *nargs;
563 	u_int i;
564 
565 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
566 	if (nargs == NULL) {
567 		gctl_error(req, "No '%s' argument.", "nargs");
568 		return;
569 	}
570 	if (*nargs < 2) {
571 		gctl_error(req, "Too few arguments.");
572 		return;
573 	}
574 	name = gctl_get_asciiparam(req, "arg0");
575 	if (name == NULL) {
576 		gctl_error(req, "No 'arg%u' argument.", 0);
577 		return;
578 	}
579 	sc = g_mirror_find_device(mp, name);
580 	if (sc == NULL) {
581 		gctl_error(req, "No such device: %s.", name);
582 		return;
583 	}
584 	for (i = 1; i < (u_int)*nargs; i++) {
585 		snprintf(param, sizeof(param), "arg%u", i);
586 		name = gctl_get_asciiparam(req, param);
587 		if (name == NULL) {
588 			gctl_error(req, "No 'arg%u' argument.", i);
589 			continue;
590 		}
591 		disk = g_mirror_find_disk(sc, name);
592 		if (disk == NULL) {
593 			gctl_error(req, "No such provider: %s.", name);
594 			continue;
595 		}
596 		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
597 		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
598 		g_mirror_update_metadata(disk);
599 		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
600 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
601 		    G_MIRROR_EVENT_DONTWAIT);
602 	}
603 	sx_xunlock(&sc->sc_lock);
604 }
605 
606 static void
607 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
608 {
609 	struct g_mirror_softc *sc;
610 	struct g_mirror_disk *disk;
611 	const char *name;
612 	char param[16];
613 	int *nargs;
614 	u_int i;
615 
616 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
617 	if (nargs == NULL) {
618 		gctl_error(req, "No '%s' argument.", "nargs");
619 		return;
620 	}
621 	if (*nargs < 1) {
622 		gctl_error(req, "Missing device(s).");
623 		return;
624 	}
625 
626 	for (i = 0; i < (u_int)*nargs; i++) {
627 		snprintf(param, sizeof(param), "arg%u", i);
628 		name = gctl_get_asciiparam(req, param);
629 		if (name == NULL) {
630 			gctl_error(req, "No 'arg%u' argument.", i);
631 			return;
632 		}
633 		sc = g_mirror_find_device(mp, name);
634 		if (sc == NULL) {
635 			gctl_error(req, "No such device: %s.", name);
636 			return;
637 		}
638 		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
639 			sx_xunlock(&sc->sc_lock);
640 			G_MIRROR_DEBUG(1,
641 			    "All disks connected in %s, skipping.",
642 			    sc->sc_name);
643 			continue;
644 		}
645 		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
646 		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
647 			g_mirror_update_metadata(disk);
648 		}
649 		sx_xunlock(&sc->sc_lock);
650 	}
651 }
652 
653 static void
654 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp)
655 {
656 	struct g_mirror_softc *sc;
657 	int *force, *nargs, error;
658 	const char *name;
659 	char param[16];
660 	u_int i;
661 	int how;
662 
663 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
664 	if (nargs == NULL) {
665 		gctl_error(req, "No '%s' argument.", "nargs");
666 		return;
667 	}
668 	if (*nargs < 1) {
669 		gctl_error(req, "Missing device(s).");
670 		return;
671 	}
672 	force = gctl_get_paraml(req, "force", sizeof(*force));
673 	if (force == NULL) {
674 		gctl_error(req, "No '%s' argument.", "force");
675 		return;
676 	}
677 	if (*force)
678 		how = G_MIRROR_DESTROY_HARD;
679 	else
680 		how = G_MIRROR_DESTROY_SOFT;
681 
682 	for (i = 0; i < (u_int)*nargs; i++) {
683 		snprintf(param, sizeof(param), "arg%u", i);
684 		name = gctl_get_asciiparam(req, param);
685 		if (name == NULL) {
686 			gctl_error(req, "No 'arg%u' argument.", i);
687 			return;
688 		}
689 		sc = g_mirror_find_device(mp, name);
690 		if (sc == NULL) {
691 			gctl_error(req, "No such device: %s.", name);
692 			return;
693 		}
694 		g_cancel_event(sc);
695 		error = g_mirror_destroy(sc, how);
696 		if (error != 0) {
697 			gctl_error(req, "Cannot destroy device %s (error=%d).",
698 			    sc->sc_geom->name, error);
699 			sx_xunlock(&sc->sc_lock);
700 			return;
701 		}
702 		/* No need to unlock, because lock is already dead. */
703 	}
704 }
705 
706 void
707 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
708 {
709 	uint32_t *version;
710 
711 	g_topology_assert();
712 
713 	version = gctl_get_paraml(req, "version", sizeof(*version));
714 	if (version == NULL) {
715 		gctl_error(req, "No '%s' argument.", "version");
716 		return;
717 	}
718 	if (*version != G_MIRROR_VERSION) {
719 		gctl_error(req, "Userland and kernel parts are out of sync.");
720 		return;
721 	}
722 
723 	g_topology_unlock();
724 	if (strcmp(verb, "configure") == 0)
725 		g_mirror_ctl_configure(req, mp);
726 	else if (strcmp(verb, "rebuild") == 0)
727 		g_mirror_ctl_rebuild(req, mp);
728 	else if (strcmp(verb, "insert") == 0)
729 		g_mirror_ctl_insert(req, mp);
730 	else if (strcmp(verb, "remove") == 0)
731 		g_mirror_ctl_remove(req, mp);
732 	else if (strcmp(verb, "deactivate") == 0)
733 		g_mirror_ctl_deactivate(req, mp);
734 	else if (strcmp(verb, "forget") == 0)
735 		g_mirror_ctl_forget(req, mp);
736 	else if (strcmp(verb, "stop") == 0)
737 		g_mirror_ctl_stop(req, mp);
738 	else
739 		gctl_error(req, "Unknown verb.");
740 	g_topology_lock();
741 }
742