xref: /freebsd/sys/geom/mirror/g_mirror_ctl.c (revision 445ed7b40948c160f2f7d363d2d0ae1ffac4aabd)
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$");
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 
50 static struct g_mirror_softc *
51 g_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 
74 static struct g_mirror_disk *
75 g_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 
93 static void
94 g_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 
303 static void
304 g_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 
386 static void
387 g_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;
498 again:
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 
557 static void
558 g_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 
621 static void
622 g_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 
690 static void
691 g_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 
752 static void
753 g_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 
799 static void
800 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
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 		if (wipe)
842 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
843 		error = g_mirror_destroy(sc, how);
844 		if (error != 0) {
845 			gctl_error(req, "Cannot destroy device %s (error=%d).",
846 			    sc->sc_geom->name, error);
847 			if (wipe)
848 				sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
849 			sx_xunlock(&sc->sc_lock);
850 			return;
851 		}
852 		/* No need to unlock, because lock is already dead. */
853 	}
854 }
855 
856 void
857 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
858 {
859 	uint32_t *version;
860 
861 	g_topology_assert();
862 
863 	version = gctl_get_paraml(req, "version", sizeof(*version));
864 	if (version == NULL) {
865 		gctl_error(req, "No '%s' argument.", "version");
866 		return;
867 	}
868 	if (*version != G_MIRROR_VERSION) {
869 		gctl_error(req, "Userland and kernel parts are out of sync.");
870 		return;
871 	}
872 
873 	g_topology_unlock();
874 	if (strcmp(verb, "configure") == 0)
875 		g_mirror_ctl_configure(req, mp);
876 	else if (strcmp(verb, "rebuild") == 0)
877 		g_mirror_ctl_rebuild(req, mp);
878 	else if (strcmp(verb, "insert") == 0)
879 		g_mirror_ctl_insert(req, mp);
880 	else if (strcmp(verb, "remove") == 0)
881 		g_mirror_ctl_remove(req, mp);
882 	else if (strcmp(verb, "resize") == 0)
883 		g_mirror_ctl_resize(req, mp);
884 	else if (strcmp(verb, "deactivate") == 0)
885 		g_mirror_ctl_deactivate(req, mp);
886 	else if (strcmp(verb, "forget") == 0)
887 		g_mirror_ctl_forget(req, mp);
888 	else if (strcmp(verb, "stop") == 0)
889 		g_mirror_ctl_stop(req, mp, 0);
890 	else if (strcmp(verb, "destroy") == 0)
891 		g_mirror_ctl_stop(req, mp, 1);
892 	else
893 		gctl_error(req, "Unknown verb.");
894 	g_topology_lock();
895 }
896