xref: /freebsd/sys/geom/mirror/g_mirror_ctl.c (revision ebccf1e3a6b11b97cbf5f813dd76636e892a9035)
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 > pp->mediasize) {
371 			gctl_error(req, "Provider %s too small.", name);
372 			continue;
373 		}
374 		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
375 			gctl_error(req, "Invalid sectorsize of provider %s.",
376 			    name);
377 			continue;
378 		}
379 		cp = g_new_consumer(sc->sc_geom);
380 		if (g_attach(cp, pp) != 0) {
381 			g_destroy_consumer(cp);
382 			gctl_error(req, "Cannot attach to provider %s.", name);
383 			continue;
384 		}
385 		if (g_access(cp, 0, 1, 1) != 0) {
386 			g_detach(cp);
387 			g_destroy_consumer(cp);
388 			gctl_error(req, "Cannot access provider %s.", name);
389 			continue;
390 		}
391 		disks[n].provider = pp;
392 		disks[n].consumer = cp;
393 		n++;
394 	}
395 	if (n == 0) {
396 		g_free(disks);
397 		return;
398 	}
399 	sc->sc_ndisks += n;
400 again:
401 	for (i = 0; i < n; i++) {
402 		if (disks[i].consumer == NULL)
403 			continue;
404 		g_mirror_fill_metadata(sc, NULL, &md);
405 		md.md_priority = *priority;
406 		if (*inactive)
407 			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
408 		pp = disks[i].provider;
409 		if (*hardcode) {
410 			strlcpy(md.md_provider, pp->name,
411 			    sizeof(md.md_provider));
412 		} else {
413 			bzero(md.md_provider, sizeof(md.md_provider));
414 		}
415 		sector = g_malloc(pp->sectorsize, M_WAITOK);
416 		mirror_metadata_encode(&md, sector);
417 		error = g_write_data(disks[i].consumer,
418 		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
419 		g_free(sector);
420 		if (error != 0) {
421 			gctl_error(req, "Cannot store metadata on %s.",
422 			    pp->name);
423 			g_access(disks[i].consumer, 0, -1, -1);
424 			g_detach(disks[i].consumer);
425 			g_destroy_consumer(disks[i].consumer);
426 			disks[i].consumer = NULL;
427 			disks[i].provider = NULL;
428 			sc->sc_ndisks--;
429 			goto again;
430 		}
431 	}
432 	if (i == 0) {
433 		/* All writes failed. */
434 		g_free(disks);
435 		return;
436 	}
437 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
438 		g_mirror_update_metadata(disk);
439 	}
440 	/*
441 	 * Release provider and wait for retaste.
442 	 */
443 	for (i = 0; i < n; i++) {
444 		if (disks[i].consumer == NULL)
445 			continue;
446 		g_access(disks[i].consumer, 0, -1, -1);
447 		g_detach(disks[i].consumer);
448 		g_destroy_consumer(disks[i].consumer);
449 	}
450 	g_free(disks);
451 }
452 
453 static void
454 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
455 {
456 	struct g_mirror_softc *sc;
457 	struct g_mirror_disk *disk;
458 	const char *name;
459 	char param[16];
460 	int *nargs;
461 	u_int i;
462 
463 	g_topology_assert();
464 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
465 	if (nargs == NULL) {
466 		gctl_error(req, "No '%s' argument.", "nargs");
467 		return;
468 	}
469 	if (*nargs < 2) {
470 		gctl_error(req, "Too few arguments.");
471 		return;
472 	}
473 	name = gctl_get_asciiparam(req, "arg0");
474 	if (name == NULL) {
475 		gctl_error(req, "No 'arg%u' argument.", 0);
476 		return;
477 	}
478 	sc = g_mirror_find_device(mp, name);
479 	if (sc == NULL) {
480 		gctl_error(req, "No such device: %s.", name);
481 		return;
482 	}
483 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
484 		gctl_error(req, "Not all disks connected.");
485 		return;
486 	}
487 
488 	for (i = 1; i < (u_int)*nargs; i++) {
489 		snprintf(param, sizeof(param), "arg%u", i);
490 		name = gctl_get_asciiparam(req, param);
491 		if (name == NULL) {
492 			gctl_error(req, "No 'arg%u' argument.", i);
493 			continue;
494 		}
495 		disk = g_mirror_find_disk(sc, name);
496 		if (disk == NULL) {
497 			gctl_error(req, "No such provider: %s.", name);
498 			continue;
499 		}
500 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
501 		    G_MIRROR_EVENT_WAIT);
502 	}
503 }
504 
505 static void
506 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
507 {
508 	struct g_mirror_softc *sc;
509 	struct g_mirror_disk *disk;
510 	const char *name;
511 	char param[16];
512 	int *nargs;
513 	u_int i;
514 
515 	g_topology_assert();
516 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
517 	if (nargs == NULL) {
518 		gctl_error(req, "No '%s' argument.", "nargs");
519 		return;
520 	}
521 	if (*nargs < 2) {
522 		gctl_error(req, "Too few arguments.");
523 		return;
524 	}
525 	name = gctl_get_asciiparam(req, "arg0");
526 	if (name == NULL) {
527 		gctl_error(req, "No 'arg%u' argument.", 0);
528 		return;
529 	}
530 	sc = g_mirror_find_device(mp, name);
531 	if (sc == NULL) {
532 		gctl_error(req, "No such device: %s.", name);
533 		return;
534 	}
535 
536 	for (i = 1; i < (u_int)*nargs; i++) {
537 		snprintf(param, sizeof(param), "arg%u", i);
538 		name = gctl_get_asciiparam(req, param);
539 		if (name == NULL) {
540 			gctl_error(req, "No 'arg%u' argument.", i);
541 			continue;
542 		}
543 		disk = g_mirror_find_disk(sc, name);
544 		if (disk == NULL) {
545 			gctl_error(req, "No such provider: %s.", name);
546 			continue;
547 		}
548 		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
549 		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
550 		g_mirror_update_metadata(disk);
551 		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
552 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
553 		    G_MIRROR_EVENT_WAIT);
554 	}
555 }
556 
557 static void
558 g_mirror_ctl_forget(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;
566 
567 	g_topology_assert();
568 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
569 	if (nargs == NULL) {
570 		gctl_error(req, "No '%s' argument.", "nargs");
571 		return;
572 	}
573 	if (*nargs < 1) {
574 		gctl_error(req, "Missing device(s).");
575 		return;
576 	}
577 
578 	for (i = 0; i < (u_int)*nargs; i++) {
579 		snprintf(param, sizeof(param), "arg%u", i);
580 		name = gctl_get_asciiparam(req, param);
581 		if (name == NULL) {
582 			gctl_error(req, "No 'arg%u' argument.", i);
583 			return;
584 		}
585 		sc = g_mirror_find_device(mp, name);
586 		if (sc == NULL) {
587 			gctl_error(req, "No such device: %s.", name);
588 			return;
589 		}
590 		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
591 			G_MIRROR_DEBUG(1,
592 			    "All disks connected in %s, skipping.",
593 			    sc->sc_name);
594 			continue;
595 		}
596 		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
597 		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
598 			g_mirror_update_metadata(disk);
599 		}
600 	}
601 }
602 
603 static void
604 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp)
605 {
606 	struct g_mirror_softc *sc;
607 	int *force, *nargs, error;
608 	const char *name;
609 	char param[16];
610 	u_int i;
611 
612 	g_topology_assert();
613 
614 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
615 	if (nargs == NULL) {
616 		gctl_error(req, "No '%s' argument.", "nargs");
617 		return;
618 	}
619 	if (*nargs < 1) {
620 		gctl_error(req, "Missing device(s).");
621 		return;
622 	}
623 	force = gctl_get_paraml(req, "force", sizeof(*force));
624 	if (force == NULL) {
625 		gctl_error(req, "No '%s' argument.", "force");
626 		return;
627 	}
628 
629 	for (i = 0; i < (u_int)*nargs; i++) {
630 		snprintf(param, sizeof(param), "arg%u", i);
631 		name = gctl_get_asciiparam(req, param);
632 		if (name == NULL) {
633 			gctl_error(req, "No 'arg%u' argument.", i);
634 			return;
635 		}
636 		sc = g_mirror_find_device(mp, name);
637 		if (sc == NULL) {
638 			gctl_error(req, "No such device: %s.", name);
639 			return;
640 		}
641 		error = g_mirror_destroy(sc, *force);
642 		if (error != 0) {
643 			gctl_error(req, "Cannot destroy device %s (error=%d).",
644 			    sc->sc_geom->name, error);
645 			return;
646 		}
647 	}
648 }
649 
650 void
651 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
652 {
653 	uint32_t *version;
654 
655 	g_topology_assert();
656 
657 	version = gctl_get_paraml(req, "version", sizeof(*version));
658 	if (version == NULL) {
659 		gctl_error(req, "No '%s' argument.", "version");
660 		return;
661 	}
662 	if (*version != G_MIRROR_VERSION) {
663 		gctl_error(req, "Userland and kernel parts are out of sync.");
664 		return;
665 	}
666 
667 	if (strcmp(verb, "configure") == 0)
668 		g_mirror_ctl_configure(req, mp);
669 	else if (strcmp(verb, "rebuild") == 0)
670 		g_mirror_ctl_rebuild(req, mp);
671 	else if (strcmp(verb, "insert") == 0)
672 		g_mirror_ctl_insert(req, mp);
673 	else if (strcmp(verb, "remove") == 0)
674 		g_mirror_ctl_remove(req, mp);
675 	else if (strcmp(verb, "deactivate") == 0)
676 		g_mirror_ctl_deactivate(req, mp);
677 	else if (strcmp(verb, "forget") == 0)
678 		g_mirror_ctl_forget(req, mp);
679 	else if (strcmp(verb, "stop") == 0)
680 		g_mirror_ctl_stop(req, mp);
681 	else
682 		gctl_error(req, "Unknown verb.");
683 }
684