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