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