xref: /freebsd/sys/geom/mirror/g_mirror_ctl.c (revision 4e8d558c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bio.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/sbuf.h>
40 #include <sys/sx.h>
41 
42 #include <geom/geom.h>
43 #include <geom/geom_dbg.h>
44 #include <geom/geom_int.h>
45 #include <geom/mirror/g_mirror.h>
46 
47 /*
48  * Configure, Rebuild, Remove, Deactivate, Forget, and Stop operations do not
49  * seem to depend on any particular g_mirror initialization state.
50  */
51 static struct g_mirror_softc *
52 g_mirror_find_device(struct g_class *mp, const char *name)
53 {
54 	struct g_mirror_softc *sc;
55 	struct g_geom *gp;
56 
57 	g_topology_lock();
58 	LIST_FOREACH(gp, &mp->geom, geom) {
59 		sc = gp->softc;
60 		if (sc == NULL)
61 			continue;
62 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
63 			continue;
64 		if (strcmp(gp->name, name) == 0 ||
65 		    strcmp(sc->sc_name, name) == 0) {
66 			g_topology_unlock();
67 			sx_xlock(&sc->sc_lock);
68 			if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
69 				sx_xunlock(&sc->sc_lock);
70 				return (NULL);
71 			}
72 			return (sc);
73 		}
74 	}
75 	g_topology_unlock();
76 	return (NULL);
77 }
78 
79 /* Insert and Resize operations depend on a launched GEOM (sc_provider). */
80 #define	GMFL_VALID_FLAGS	(M_WAITOK | M_NOWAIT)
81 static struct g_mirror_softc *
82 g_mirror_find_launched_device(struct g_class *mp, const char *name, int flags)
83 {
84 	struct g_mirror_softc *sc;
85 	int error;
86 
87 	KASSERT((flags & ~GMFL_VALID_FLAGS) == 0 &&
88 	    flags != GMFL_VALID_FLAGS && flags != 0,
89 	    ("%s: Invalid flags %x\n", __func__, (unsigned)flags));
90 #undef	GMFL_VALID_FLAGS
91 
92 	while (true) {
93 		sc = g_mirror_find_device(mp, name);
94 		if (sc == NULL)
95 			return (NULL);
96 		if (sc->sc_provider != NULL)
97 			return (sc);
98 		if (flags & M_NOWAIT) {
99 			sx_xunlock(&sc->sc_lock);
100 			return (NULL);
101 		}
102 
103 		/*
104 		 * This is a dumb hack.  G_mirror does not expose any real
105 		 * wakeup API for observing state changes, and even if it did,
106 		 * its "RUNNING" state does not actually reflect all softc
107 		 * elements being initialized.
108 		 *
109 		 * Revamping g_mirror to have a 3rd, ACTUALLY_RUNNING state and
110 		 * updating all assertions and sc_state checks is a large work
111 		 * and would be easy to introduce regressions.
112 		 *
113 		 * Revamping g_mirror to have a wakeup for state changes would
114 		 * be difficult if one wanted to capture more than just
115 		 * sc_state and sc_provider.
116 		 *
117 		 * For now, just dummy sleep-poll until sc_provider shows up,
118 		 * the user cancels, or the g_mirror is destroyed.
119 		 */
120 		error = sx_sleep(&sc, &sc->sc_lock, PRIBIO | PCATCH | PDROP,
121 		    "GM:launched", 1);
122 		if (error != 0 && error != EWOULDBLOCK)
123 			return (NULL);
124 	}
125 	__unreachable();
126 }
127 
128 static struct g_mirror_disk *
129 g_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
130 {
131 	struct g_mirror_disk *disk;
132 
133 	sx_assert(&sc->sc_lock, SX_XLOCKED);
134 	if (strncmp(name, _PATH_DEV, 5) == 0)
135 		name += 5;
136 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
137 		if (disk->d_consumer == NULL)
138 			continue;
139 		if (disk->d_consumer->provider == NULL)
140 			continue;
141 		if (strcmp(disk->d_consumer->provider->name, name) == 0)
142 			return (disk);
143 	}
144 	return (NULL);
145 }
146 
147 static void
148 g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
149 {
150 	struct g_mirror_softc *sc;
151 	struct g_mirror_disk *disk;
152 	const char *name, *balancep, *prov;
153 	intmax_t *slicep, *priority;
154 	uint32_t slice;
155 	uint8_t balance;
156 	int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
157 	int *nargs, do_sync = 0, dirty = 1, do_priority = 0;
158 
159 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
160 	if (nargs == NULL) {
161 		gctl_error(req, "No '%s' argument.", "nargs");
162 		return;
163 	}
164 	if (*nargs != 1 && *nargs != 2) {
165 		gctl_error(req, "Invalid number of arguments.");
166 		return;
167 	}
168 	name = gctl_get_asciiparam(req, "arg0");
169 	if (name == NULL) {
170 		gctl_error(req, "No 'arg%u' argument.", 0);
171 		return;
172 	}
173 	balancep = gctl_get_asciiparam(req, "balance");
174 	if (balancep == NULL) {
175 		gctl_error(req, "No '%s' argument.", "balance");
176 		return;
177 	}
178 	autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
179 	if (autosync == NULL) {
180 		gctl_error(req, "No '%s' argument.", "autosync");
181 		return;
182 	}
183 	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
184 	if (noautosync == NULL) {
185 		gctl_error(req, "No '%s' argument.", "noautosync");
186 		return;
187 	}
188 	failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
189 	if (failsync == NULL) {
190 		gctl_error(req, "No '%s' argument.", "failsync");
191 		return;
192 	}
193 	nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
194 	if (nofailsync == NULL) {
195 		gctl_error(req, "No '%s' argument.", "nofailsync");
196 		return;
197 	}
198 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
199 	if (hardcode == NULL) {
200 		gctl_error(req, "No '%s' argument.", "hardcode");
201 		return;
202 	}
203 	dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
204 	if (dynamic == NULL) {
205 		gctl_error(req, "No '%s' argument.", "dynamic");
206 		return;
207 	}
208 	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
209 	if (priority == NULL) {
210 		gctl_error(req, "No '%s' argument.", "priority");
211 		return;
212 	}
213 	if (*priority < -1 || *priority > 255) {
214 		gctl_error(req, "Priority range is 0 to 255, %jd given",
215 		    *priority);
216 		return;
217 	}
218 	/*
219 	 * Since we have a priority, we also need a provider now.
220 	 * Note: be WARNS safe, by always assigning prov and only throw an
221 	 * error if *priority != -1.
222 	 */
223 	prov = gctl_get_asciiparam(req, "arg1");
224 	if (*priority > -1) {
225 		if (prov == NULL) {
226 			gctl_error(req, "Priority needs a disk name");
227 			return;
228 		}
229 		do_priority = 1;
230 	}
231 	if (*autosync && *noautosync) {
232 		gctl_error(req, "'%s' and '%s' specified.", "autosync",
233 		    "noautosync");
234 		return;
235 	}
236 	if (*failsync && *nofailsync) {
237 		gctl_error(req, "'%s' and '%s' specified.", "failsync",
238 		    "nofailsync");
239 		return;
240 	}
241 	if (*hardcode && *dynamic) {
242 		gctl_error(req, "'%s' and '%s' specified.", "hardcode",
243 		    "dynamic");
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 	if (*balancep == '\0')
252 		balance = sc->sc_balance;
253 	else {
254 		if (balance_id(balancep) == -1) {
255 			gctl_error(req, "Invalid balance algorithm.");
256 			sx_xunlock(&sc->sc_lock);
257 			return;
258 		}
259 		balance = balance_id(balancep);
260 	}
261 	slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
262 	if (slicep == NULL) {
263 		gctl_error(req, "No '%s' argument.", "slice");
264 		sx_xunlock(&sc->sc_lock);
265 		return;
266 	}
267 	if (*slicep == -1)
268 		slice = sc->sc_slice;
269 	else
270 		slice = *slicep;
271 	/* Enforce usage() of -p not allowing any other options. */
272 	if (do_priority && (*autosync || *noautosync || *failsync ||
273 	    *nofailsync || *hardcode || *dynamic || *slicep != -1 ||
274 	    *balancep != '\0')) {
275 		sx_xunlock(&sc->sc_lock);
276 		gctl_error(req, "only -p accepted when setting priority");
277 		return;
278 	}
279 	if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
280 	    !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
281 	    !*dynamic && !do_priority) {
282 		sx_xunlock(&sc->sc_lock);
283 		gctl_error(req, "Nothing has changed.");
284 		return;
285 	}
286 	if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) {
287 		sx_xunlock(&sc->sc_lock);
288 		gctl_error(req, "Invalid number of arguments.");
289 		return;
290 	}
291 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
292 		sx_xunlock(&sc->sc_lock);
293 		gctl_error(req, "Not all disks connected. Try 'forget' command "
294 		    "first.");
295 		return;
296 	}
297 	sc->sc_balance = balance;
298 	sc->sc_slice = slice;
299 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
300 		if (*autosync) {
301 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
302 			do_sync = 1;
303 		}
304 	} else {
305 		if (*noautosync)
306 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
307 	}
308 	if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
309 		if (*failsync)
310 			sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
311 	} else {
312 		if (*nofailsync) {
313 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
314 			dirty = 0;
315 		}
316 	}
317 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
318 		/*
319 		 * Handle priority first, since we only need one disk, do one
320 		 * operation on it and then we're done. No need to check other
321 		 * flags, as usage doesn't allow it.
322 		 */
323 		if (do_priority) {
324 			if (strcmp(disk->d_name, prov) == 0) {
325 				if (disk->d_priority == *priority)
326 					gctl_error(req, "Nothing has changed.");
327 				else {
328 					disk->d_priority = *priority;
329 					g_mirror_update_metadata(disk);
330 				}
331 				break;
332 			}
333 			continue;
334 		}
335 		if (do_sync) {
336 			if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
337 				disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
338 		}
339 		if (*hardcode)
340 			disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
341 		else if (*dynamic)
342 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
343 		if (!dirty)
344 			disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
345 		g_mirror_update_metadata(disk);
346 		if (do_sync) {
347 			if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
348 				g_mirror_event_send(disk,
349 				    G_MIRROR_DISK_STATE_DISCONNECTED,
350 				    G_MIRROR_EVENT_DONTWAIT);
351 			}
352 		}
353 	}
354 	sx_xunlock(&sc->sc_lock);
355 }
356 
357 static void
358 g_mirror_create_orphan(struct g_consumer *cp)
359 {
360 
361 	KASSERT(1 == 0, ("%s called while creating %s.", __func__,
362 	    cp->provider->name));
363 }
364 
365 static void
366 g_mirror_ctl_create(struct gctl_req *req, struct g_class *mp)
367 {
368 	struct g_mirror_metadata md;
369 	struct g_geom *gp;
370 	struct g_consumer *cp;
371 	struct g_provider *pp;
372 	struct g_mirror_softc *sc;
373 	struct sbuf *sb;
374 	const char *name;
375 	char param[16];
376 	int *nargs;
377 	intmax_t *val;
378 	int *ival;
379 	const char *sval;
380 	int bal;
381 	unsigned attached, no, sectorsize;
382 	off_t mediasize;
383 
384 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
385 	if (nargs == NULL) {
386 		gctl_error(req, "No '%s' argument.", "nargs");
387 		return;
388 	}
389 	if (*nargs <= 2) {
390 		gctl_error(req, "Too few arguments.");
391 		return;
392 	}
393 
394 	strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
395 	md.md_version = G_MIRROR_VERSION;
396 	name = gctl_get_asciiparam(req, "arg0");
397 	if (name == NULL) {
398 		gctl_error(req, "No 'arg%u' argument.", 0);
399 		return;
400 	}
401 	strlcpy(md.md_name, name, sizeof(md.md_name));
402 	md.md_mid = arc4random();
403 	md.md_all = *nargs - 1;
404 	md.md_genid = 0;
405 	md.md_syncid = 1;
406 	md.md_sync_offset = 0;
407 	val = gctl_get_paraml(req, "slice", sizeof(*val));
408 	if (val == NULL) {
409 		gctl_error(req, "No slice argument.");
410 		return;
411 	}
412 	md.md_slice = *val;
413 	sval = gctl_get_asciiparam(req, "balance");
414 	if (sval == NULL) {
415 		gctl_error(req, "No balance argument.");
416 		return;
417 	}
418 	bal = balance_id(sval);
419 	if (bal < 0) {
420 		gctl_error(req, "Invalid balance algorithm.");
421 		return;
422 	}
423 	md.md_balance = bal;
424 	md.md_mflags = 0;
425 	md.md_dflags = 0;
426 	ival = gctl_get_paraml(req, "noautosync", sizeof(*ival));
427 	if (ival != NULL && *ival)
428 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
429 	ival = gctl_get_paraml(req, "nofailsync", sizeof(*ival));
430 	if (ival != NULL && *ival)
431 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
432 	/* These fields not used in manual mode. */
433 	bzero(md.md_provider, sizeof(md.md_provider));
434 	md.md_provsize = 0;
435 
436 	g_topology_lock();
437 	mediasize = OFF_MAX;
438 	sectorsize = 0;
439 	gp = g_new_geomf(mp, "%s", md.md_name);
440 	gp->orphan = g_mirror_create_orphan;
441 	cp = g_new_consumer(gp);
442 	for (no = 1; no < *nargs; no++) {
443 		snprintf(param, sizeof(param), "arg%u", no);
444 		pp = gctl_get_provider(req, param);
445 		if (pp == NULL) {
446 err:
447 			g_destroy_consumer(cp);
448 			g_destroy_geom(gp);
449 			g_topology_unlock();
450 			return;
451 		}
452 		if (g_attach(cp, pp) != 0) {
453 			G_MIRROR_DEBUG(1, "Can't attach disk %s.", pp->name);
454 			gctl_error(req, "Can't attach disk %s.", pp->name);
455 			goto err;
456 		}
457 		if (g_access(cp, 1, 0, 0) != 0) {
458 			G_MIRROR_DEBUG(1, "Can't open disk %s.", pp->name);
459 			gctl_error(req, "Can't open disk %s.", pp->name);
460 err2:
461 			g_detach(cp);
462 			goto err;
463 		}
464 		if (pp->mediasize == 0 || pp->sectorsize == 0) {
465 			G_MIRROR_DEBUG(1, "Disk %s has no media.", pp->name);
466 			gctl_error(req, "Disk %s has no media.", pp->name);
467 			g_access(cp, -1, 0, 0);
468 			goto err2;
469 		}
470 		if (pp->mediasize < mediasize)
471 			mediasize = pp->mediasize;
472 		if (pp->sectorsize > sectorsize)
473 			sectorsize = pp->sectorsize;
474 		g_access(cp, -1, 0, 0);
475 		g_detach(cp);
476 	}
477 	g_destroy_consumer(cp);
478 	g_destroy_geom(gp);
479 	md.md_mediasize = mediasize;
480 	md.md_sectorsize = sectorsize;
481 	md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
482 
483 	gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_MANUAL);
484 	if (gp == NULL) {
485 		gctl_error(req, "Can't create %s.", md.md_name);
486 		g_topology_unlock();
487 		return;
488 	}
489 
490 	sc = gp->softc;
491 	g_topology_unlock();
492 	sx_xlock(&sc->sc_lock);
493 	sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING;
494 	sb = sbuf_new_auto();
495 	sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
496 	for (attached = 0, no = 1; no < *nargs; no++) {
497 		snprintf(param, sizeof(param), "arg%u", no);
498 		pp = gctl_get_provider(req, param);
499 		if (pp == NULL) {
500 			name = gctl_get_asciiparam(req, param);
501 			MPASS(name != NULL);
502 			sbuf_printf(sb, " %s", name);
503 			continue;
504 		}
505 		md.md_did = arc4random();
506 		md.md_priority = no - 1;
507 		if (g_mirror_add_disk(sc, pp, &md) != 0) {
508 			G_MIRROR_DEBUG(1, "Disk %u (%s) not attached to %s.",
509 			    no, pp->name, gp->name);
510 			sbuf_printf(sb, " %s", pp->name);
511 			continue;
512 		}
513 		attached++;
514 	}
515 	sbuf_finish(sb);
516 	sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING;
517 	if (md.md_all != attached ||
518 	    (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
519 		g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_HARD);
520 		gctl_error(req, "%s", sbuf_data(sb));
521 	} else
522 		sx_xunlock(&sc->sc_lock);
523 	sbuf_delete(sb);
524 }
525 
526 static void
527 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
528 {
529 	struct g_mirror_metadata md;
530 	struct g_mirror_softc *sc;
531 	struct g_mirror_disk *disk;
532 	struct g_provider *pp;
533 	const char *name;
534 	char param[16];
535 	int error, *nargs;
536 	u_int i;
537 
538 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
539 	if (nargs == NULL) {
540 		gctl_error(req, "No '%s' argument.", "nargs");
541 		return;
542 	}
543 	if (*nargs < 2) {
544 		gctl_error(req, "Too few arguments.");
545 		return;
546 	}
547 	name = gctl_get_asciiparam(req, "arg0");
548 	if (name == NULL) {
549 		gctl_error(req, "No 'arg%u' argument.", 0);
550 		return;
551 	}
552 	sc = g_mirror_find_device(mp, name);
553 	if (sc == NULL) {
554 		gctl_error(req, "No such device: %s.", name);
555 		return;
556 	}
557 	for (i = 1; i < (u_int)*nargs; i++) {
558 		snprintf(param, sizeof(param), "arg%u", i);
559 		name = gctl_get_asciiparam(req, param);
560 		if (name == NULL) {
561 			gctl_error(req, "No 'arg%u' argument.", i);
562 			continue;
563 		}
564 		disk = g_mirror_find_disk(sc, name);
565 		if (disk == NULL) {
566 			gctl_error(req, "No such provider: %s.", name);
567 			continue;
568 		}
569 		if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
570 		    disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
571 			/*
572 			 * This is the last active disk. There will be nothing
573 			 * to rebuild it from, so deny this request.
574 			 */
575 			gctl_error(req,
576 			    "Provider %s is the last active provider in %s.",
577 			    name, sc->sc_geom->name);
578 			break;
579 		}
580 		/*
581 		 * Do rebuild by resetting syncid, disconnecting the disk and
582 		 * connecting it again.
583 		 */
584 		disk->d_sync.ds_syncid = 0;
585 		if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
586 			disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
587 		g_mirror_update_metadata(disk);
588 		pp = disk->d_consumer->provider;
589 		g_topology_lock();
590 		error = g_mirror_read_metadata(disk->d_consumer, &md);
591 		g_topology_unlock();
592 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
593 		    G_MIRROR_EVENT_WAIT);
594 		if (error != 0) {
595 			gctl_error(req, "Cannot read metadata from %s.",
596 			    pp->name);
597 			continue;
598 		}
599 		error = g_mirror_add_disk(sc, pp, &md);
600 		if (error != 0) {
601 			gctl_error(req, "Cannot reconnect component %s.",
602 			    pp->name);
603 			continue;
604 		}
605 	}
606 	sx_xunlock(&sc->sc_lock);
607 }
608 
609 static void
610 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
611 {
612 	struct g_mirror_softc *sc;
613 	struct g_mirror_disk *disk;
614 	struct g_mirror_metadata md;
615 	struct g_provider *pp;
616 	struct g_consumer *cp;
617 	intmax_t *priority;
618 	const char *name;
619 	char param[16];
620 	u_char *sector;
621 	u_int i, n;
622 	int error, *nargs, *hardcode, *inactive;
623 	struct {
624 		struct g_provider	*provider;
625 		struct g_consumer	*consumer;
626 	} *disks;
627 	off_t mdsize;
628 
629 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
630 	if (nargs == NULL) {
631 		gctl_error(req, "No '%s' argument.", "nargs");
632 		return;
633 	}
634 	if (*nargs < 2) {
635 		gctl_error(req, "Too few arguments.");
636 		return;
637 	}
638 	priority = gctl_get_paraml(req, "priority", sizeof(*priority));
639 	if (priority == NULL) {
640 		gctl_error(req, "No '%s' argument.", "priority");
641 		return;
642 	}
643 	inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
644 	if (inactive == NULL) {
645 		gctl_error(req, "No '%s' argument.", "inactive");
646 		return;
647 	}
648 	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
649 	if (hardcode == NULL) {
650 		gctl_error(req, "No '%s' argument.", "hardcode");
651 		return;
652 	}
653 	name = gctl_get_asciiparam(req, "arg0");
654 	if (name == NULL) {
655 		gctl_error(req, "No 'arg%u' argument.", 0);
656 		return;
657 	}
658 	sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
659 	if (sc == NULL) {
660 		gctl_error(req, "No such device: %s.", name);
661 		return;
662 	}
663 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
664 		gctl_error(req, "Not all disks connected.");
665 		sx_xunlock(&sc->sc_lock);
666 		return;
667 	}
668 
669 	disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
670 	g_topology_lock();
671 	for (i = 1, n = 0; i < (u_int)*nargs; i++) {
672 		snprintf(param, sizeof(param), "arg%u", i);
673 		pp = gctl_get_provider(req, param);
674 		if (pp == NULL)
675 			continue;
676 		if (g_mirror_find_disk(sc, pp->name) != NULL) {
677 			gctl_error(req, "Provider %s already inserted.", pp->name);
678 			continue;
679 		}
680 		cp = g_new_consumer(sc->sc_geom);
681 		if (g_attach(cp, pp) != 0) {
682 			g_destroy_consumer(cp);
683 			gctl_error(req, "Cannot attach to provider %s.", pp->name);
684 			continue;
685 		}
686 		if (g_access(cp, 0, 1, 1) != 0) {
687 			gctl_error(req, "Cannot access provider %s.", pp->name);
688 err:
689 			g_detach(cp);
690 			g_destroy_consumer(cp);
691 			continue;
692 		}
693 		mdsize = (sc->sc_type == G_MIRROR_TYPE_AUTOMATIC) ?
694 		    pp->sectorsize : 0;
695 		if (sc->sc_provider->mediasize > pp->mediasize - mdsize) {
696 			gctl_error(req, "Provider %s too small.", pp->name);
697 err2:
698 			g_access(cp, 0, -1, -1);
699 			goto err;
700 		}
701 		if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
702 			gctl_error(req, "Invalid sectorsize of provider %s.",
703 			    pp->name);
704 			goto err2;
705 		}
706 		if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) {
707 			g_access(cp, 0, -1, -1);
708 			g_detach(cp);
709 			g_destroy_consumer(cp);
710 			g_topology_unlock();
711 			sc->sc_ndisks++;
712 			g_mirror_fill_metadata(sc, NULL, &md);
713 			md.md_priority = *priority;
714 			if (*inactive)
715 				md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
716 			if (g_mirror_add_disk(sc, pp, &md) != 0) {
717 				sc->sc_ndisks--;
718 				gctl_error(req, "Disk %s not inserted.", pp->name);
719 			}
720 			g_topology_lock();
721 			continue;
722 		}
723 		disks[n].provider = pp;
724 		disks[n].consumer = cp;
725 		n++;
726 	}
727 	if (n == 0) {
728 		g_topology_unlock();
729 		sx_xunlock(&sc->sc_lock);
730 		g_free(disks);
731 		return;
732 	}
733 	sc->sc_ndisks += n;
734 again:
735 	for (i = 0; i < n; i++) {
736 		if (disks[i].consumer == NULL)
737 			continue;
738 		g_mirror_fill_metadata(sc, NULL, &md);
739 		md.md_priority = *priority;
740 		if (*inactive)
741 			md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
742 		pp = disks[i].provider;
743 		if (*hardcode) {
744 			strlcpy(md.md_provider, pp->name,
745 			    sizeof(md.md_provider));
746 		} else {
747 			bzero(md.md_provider, sizeof(md.md_provider));
748 		}
749 		md.md_provsize = pp->mediasize;
750 		sector = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
751 		mirror_metadata_encode(&md, sector);
752 		error = g_write_data(disks[i].consumer,
753 		    pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
754 		g_free(sector);
755 		if (error != 0) {
756 			gctl_error(req, "Cannot store metadata on %s.",
757 			    pp->name);
758 			g_access(disks[i].consumer, 0, -1, -1);
759 			g_detach(disks[i].consumer);
760 			g_destroy_consumer(disks[i].consumer);
761 			disks[i].consumer = NULL;
762 			disks[i].provider = NULL;
763 			sc->sc_ndisks--;
764 			goto again;
765 		}
766 	}
767 	g_topology_unlock();
768 	if (i == 0) {
769 		/* All writes failed. */
770 		sx_xunlock(&sc->sc_lock);
771 		g_free(disks);
772 		return;
773 	}
774 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
775 		g_mirror_update_metadata(disk);
776 	}
777 	/*
778 	 * Release provider and wait for retaste.
779 	 */
780 	g_topology_lock();
781 	for (i = 0; i < n; i++) {
782 		if (disks[i].consumer == NULL)
783 			continue;
784 		g_access(disks[i].consumer, 0, -1, -1);
785 		g_detach(disks[i].consumer);
786 		g_destroy_consumer(disks[i].consumer);
787 	}
788 	g_topology_unlock();
789 	sx_xunlock(&sc->sc_lock);
790 	g_free(disks);
791 }
792 
793 static void
794 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
795 {
796 	struct g_mirror_softc *sc;
797 	struct g_mirror_disk *disk;
798 	const char *name;
799 	char param[16];
800 	int *nargs;
801 	u_int i, active;
802 
803 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
804 	if (nargs == NULL) {
805 		gctl_error(req, "No '%s' argument.", "nargs");
806 		return;
807 	}
808 	if (*nargs < 2) {
809 		gctl_error(req, "Too few arguments.");
810 		return;
811 	}
812 	name = gctl_get_asciiparam(req, "arg0");
813 	if (name == NULL) {
814 		gctl_error(req, "No 'arg%u' argument.", 0);
815 		return;
816 	}
817 	sc = g_mirror_find_device(mp, name);
818 	if (sc == NULL) {
819 		gctl_error(req, "No such device: %s.", name);
820 		return;
821 	}
822 	if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
823 		sx_xunlock(&sc->sc_lock);
824 		gctl_error(req, "Not all disks connected. Try 'forget' command "
825 		    "first.");
826 		return;
827 	}
828 	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
829 	for (i = 1; i < (u_int)*nargs; i++) {
830 		snprintf(param, sizeof(param), "arg%u", i);
831 		name = gctl_get_asciiparam(req, param);
832 		if (name == NULL) {
833 			gctl_error(req, "No 'arg%u' argument.", i);
834 			continue;
835 		}
836 		disk = g_mirror_find_disk(sc, name);
837 		if (disk == NULL) {
838 			gctl_error(req, "No such provider: %s.", name);
839 			continue;
840 		}
841 		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
842 			if (active > 1)
843 				active--;
844 			else {
845 				gctl_error(req, "%s: Can't remove the last "
846 				    "ACTIVE component %s.", sc->sc_geom->name,
847 				    name);
848 				continue;
849 			}
850 		}
851 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
852 		    G_MIRROR_EVENT_DONTWAIT);
853 	}
854 	sx_xunlock(&sc->sc_lock);
855 }
856 
857 static void
858 g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
859 {
860 	struct g_mirror_softc *sc;
861 	struct g_mirror_disk *disk;
862 	uint64_t mediasize;
863 	const char *name, *s;
864 	char *x;
865 	int *nargs;
866 
867 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
868 	if (nargs == NULL) {
869 		gctl_error(req, "No '%s' argument.", "nargs");
870 		return;
871 	}
872 	if (*nargs != 1) {
873 		gctl_error(req, "Missing device.");
874 		return;
875 	}
876 	name = gctl_get_asciiparam(req, "arg0");
877 	if (name == NULL) {
878 		gctl_error(req, "No 'arg%u' argument.", 0);
879 		return;
880 	}
881 	s = gctl_get_asciiparam(req, "size");
882 	if (s == NULL) {
883 		gctl_error(req, "No '%s' argument.", "size");
884 		return;
885 	}
886 	mediasize = strtouq(s, &x, 0);
887 	if (*x != '\0' || mediasize == 0) {
888 		gctl_error(req, "Invalid '%s' argument.", "size");
889 		return;
890 	}
891 	sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
892 	if (sc == NULL) {
893 		gctl_error(req, "No such device: %s.", name);
894 		return;
895 	}
896 	/* Deny shrinking of an opened provider */
897 	if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && sc->sc_provider_open > 0) {
898 		if (sc->sc_mediasize > mediasize) {
899 			gctl_error(req, "Device %s is busy.",
900 			    sc->sc_provider->name);
901 			sx_xunlock(&sc->sc_lock);
902 			return;
903 		}
904 	}
905 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
906 		if (mediasize > disk->d_consumer->provider->mediasize -
907 		    disk->d_consumer->provider->sectorsize) {
908 			gctl_error(req, "Provider %s is too small.",
909 			    disk->d_name);
910 			sx_xunlock(&sc->sc_lock);
911 			return;
912 		}
913 	}
914 	/* Update the size. */
915 	sc->sc_mediasize = mediasize;
916 	LIST_FOREACH(disk, &sc->sc_disks, d_next) {
917 		g_mirror_update_metadata(disk);
918 	}
919 	g_topology_lock();
920 	g_resize_provider(sc->sc_provider, mediasize);
921 	g_topology_unlock();
922 	sx_xunlock(&sc->sc_lock);
923 }
924 
925 static void
926 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
927 {
928 	struct g_mirror_softc *sc;
929 	struct g_mirror_disk *disk;
930 	const char *name;
931 	char param[16];
932 	int *nargs;
933 	u_int i, active;
934 
935 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
936 	if (nargs == NULL) {
937 		gctl_error(req, "No '%s' argument.", "nargs");
938 		return;
939 	}
940 	if (*nargs < 2) {
941 		gctl_error(req, "Too few arguments.");
942 		return;
943 	}
944 	name = gctl_get_asciiparam(req, "arg0");
945 	if (name == NULL) {
946 		gctl_error(req, "No 'arg%u' argument.", 0);
947 		return;
948 	}
949 	sc = g_mirror_find_device(mp, name);
950 	if (sc == NULL) {
951 		gctl_error(req, "No such device: %s.", name);
952 		return;
953 	}
954 	active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
955 	for (i = 1; i < (u_int)*nargs; i++) {
956 		snprintf(param, sizeof(param), "arg%u", i);
957 		name = gctl_get_asciiparam(req, param);
958 		if (name == NULL) {
959 			gctl_error(req, "No 'arg%u' argument.", i);
960 			continue;
961 		}
962 		disk = g_mirror_find_disk(sc, name);
963 		if (disk == NULL) {
964 			gctl_error(req, "No such provider: %s.", name);
965 			continue;
966 		}
967 		if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
968 			if (active > 1)
969 				active--;
970 			else {
971 				gctl_error(req, "%s: Can't deactivate the "
972 				    "last ACTIVE component %s.",
973 				    sc->sc_geom->name, name);
974 				continue;
975 			}
976 		}
977 		disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
978 		disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
979 		g_mirror_update_metadata(disk);
980 		sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
981 		g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
982 		    G_MIRROR_EVENT_DONTWAIT);
983 	}
984 	sx_xunlock(&sc->sc_lock);
985 }
986 
987 static void
988 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
989 {
990 	struct g_mirror_softc *sc;
991 	struct g_mirror_disk *disk;
992 	const char *name;
993 	char param[16];
994 	int *nargs;
995 	u_int i;
996 
997 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
998 	if (nargs == NULL) {
999 		gctl_error(req, "No '%s' argument.", "nargs");
1000 		return;
1001 	}
1002 	if (*nargs < 1) {
1003 		gctl_error(req, "Missing device(s).");
1004 		return;
1005 	}
1006 
1007 	for (i = 0; i < (u_int)*nargs; i++) {
1008 		snprintf(param, sizeof(param), "arg%u", i);
1009 		name = gctl_get_asciiparam(req, param);
1010 		if (name == NULL) {
1011 			gctl_error(req, "No 'arg%u' argument.", i);
1012 			return;
1013 		}
1014 		sc = g_mirror_find_device(mp, name);
1015 		if (sc == NULL) {
1016 			gctl_error(req, "No such device: %s.", name);
1017 			return;
1018 		}
1019 		if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
1020 			sx_xunlock(&sc->sc_lock);
1021 			G_MIRROR_DEBUG(1,
1022 			    "All disks connected in %s, skipping.",
1023 			    sc->sc_name);
1024 			continue;
1025 		}
1026 		sc->sc_ndisks = g_mirror_ndisks(sc, -1);
1027 		LIST_FOREACH(disk, &sc->sc_disks, d_next) {
1028 			g_mirror_update_metadata(disk);
1029 		}
1030 		sx_xunlock(&sc->sc_lock);
1031 	}
1032 }
1033 
1034 static void
1035 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
1036 {
1037 	struct g_mirror_softc *sc;
1038 	int *force, *nargs, error;
1039 	const char *name;
1040 	char param[16];
1041 	u_int i;
1042 	int how;
1043 
1044 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1045 	if (nargs == NULL) {
1046 		gctl_error(req, "No '%s' argument.", "nargs");
1047 		return;
1048 	}
1049 	if (*nargs < 1) {
1050 		gctl_error(req, "Missing device(s).");
1051 		return;
1052 	}
1053 	force = gctl_get_paraml(req, "force", sizeof(*force));
1054 	if (force == NULL) {
1055 		gctl_error(req, "No '%s' argument.", "force");
1056 		return;
1057 	}
1058 	if (*force)
1059 		how = G_MIRROR_DESTROY_HARD;
1060 	else
1061 		how = G_MIRROR_DESTROY_SOFT;
1062 
1063 	for (i = 0; i < (u_int)*nargs; i++) {
1064 		snprintf(param, sizeof(param), "arg%u", i);
1065 		name = gctl_get_asciiparam(req, param);
1066 		if (name == NULL) {
1067 			gctl_error(req, "No 'arg%u' argument.", i);
1068 			return;
1069 		}
1070 		sc = g_mirror_find_device(mp, name);
1071 		if (sc == NULL) {
1072 			gctl_error(req, "No such device: %s.", name);
1073 			return;
1074 		}
1075 		g_cancel_event(sc);
1076 		if (wipe)
1077 			sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
1078 		error = g_mirror_destroy(sc, how);
1079 		if (error != 0) {
1080 			gctl_error(req, "Cannot destroy device %s (error=%d).",
1081 			    sc->sc_geom->name, error);
1082 			if (wipe)
1083 				sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
1084 			sx_xunlock(&sc->sc_lock);
1085 			return;
1086 		}
1087 		/* No need to unlock, because lock is already dead. */
1088 	}
1089 }
1090 
1091 void
1092 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1093 {
1094 	uint32_t *version;
1095 
1096 	g_topology_assert();
1097 
1098 	version = gctl_get_paraml(req, "version", sizeof(*version));
1099 	if (version == NULL) {
1100 		gctl_error(req, "No '%s' argument.", "version");
1101 		return;
1102 	}
1103 	if (*version != G_MIRROR_VERSION) {
1104 		gctl_error(req, "Userland and kernel parts are out of sync.");
1105 		return;
1106 	}
1107 
1108 	g_topology_unlock();
1109 	if (strcmp(verb, "configure") == 0)
1110 		g_mirror_ctl_configure(req, mp);
1111 	else if (strcmp(verb, "create") == 0)
1112 		g_mirror_ctl_create(req, mp);
1113 	else if (strcmp(verb, "rebuild") == 0)
1114 		g_mirror_ctl_rebuild(req, mp);
1115 	else if (strcmp(verb, "insert") == 0)
1116 		g_mirror_ctl_insert(req, mp);
1117 	else if (strcmp(verb, "remove") == 0)
1118 		g_mirror_ctl_remove(req, mp);
1119 	else if (strcmp(verb, "resize") == 0)
1120 		g_mirror_ctl_resize(req, mp);
1121 	else if (strcmp(verb, "deactivate") == 0)
1122 		g_mirror_ctl_deactivate(req, mp);
1123 	else if (strcmp(verb, "forget") == 0)
1124 		g_mirror_ctl_forget(req, mp);
1125 	else if (strcmp(verb, "stop") == 0)
1126 		g_mirror_ctl_stop(req, mp, 0);
1127 	else if (strcmp(verb, "destroy") == 0)
1128 		g_mirror_ctl_stop(req, mp, 1);
1129 	else
1130 		gctl_error(req, "Unknown verb.");
1131 	g_topology_lock();
1132 }
1133