]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/mirror/g_mirror_ctl.c
Import lib9p 7ddb1164407da19b9b1afb83df83ae65a71a9a66.
[FreeBSD/FreeBSD.git] / sys / geom / mirror / g_mirror_ctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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, "/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                 name = gctl_get_asciiparam(req, param);
445                 if (name == NULL) {
446                         gctl_error(req, "No 'arg%u' argument.", no);
447 err:
448                         g_destroy_consumer(cp);
449                         g_destroy_geom(gp);
450                         g_topology_unlock();
451                         return;
452                 }
453                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
454                         name += strlen("/dev/");
455                 pp = g_provider_by_name(name);
456                 if (pp == NULL) {
457                         G_MIRROR_DEBUG(1, "Disk %s is invalid.", name);
458                         gctl_error(req, "Disk %s is invalid.", name);
459                         goto err;
460                 }
461                 g_attach(cp, pp);
462                 if (g_access(cp, 1, 0, 0) != 0) {
463                         G_MIRROR_DEBUG(1, "Can't open disk %s.", name);
464                         gctl_error(req, "Can't open disk %s.", name);
465 err2:
466                         g_detach(cp);
467                         goto err;
468                 }
469                 if (pp->mediasize == 0 || pp->sectorsize == 0) {
470                         G_MIRROR_DEBUG(1, "Disk %s has no media.", name);
471                         gctl_error(req, "Disk %s has no media.", name);
472                         g_access(cp, -1, 0, 0);
473                         goto err2;
474                 }
475                 if (pp->mediasize < mediasize)
476                         mediasize = pp->mediasize;
477                 if (pp->sectorsize > sectorsize)
478                         sectorsize = pp->sectorsize;
479                 g_access(cp, -1, 0, 0);
480                 g_detach(cp);
481         }
482         g_destroy_consumer(cp);
483         g_destroy_geom(gp);
484         md.md_mediasize = mediasize;
485         md.md_sectorsize = sectorsize;
486         md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
487
488         gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_MANUAL);
489         if (gp == NULL) {
490                 gctl_error(req, "Can't create %s.", md.md_name);
491                 g_topology_unlock();
492                 return;
493         }
494
495         sc = gp->softc;
496         g_topology_unlock();
497         sx_xlock(&sc->sc_lock);
498         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING;
499         sb = sbuf_new_auto();
500         sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
501         for (attached = 0, no = 1; no < *nargs; no++) {
502                 snprintf(param, sizeof(param), "arg%u", no);
503                 name = gctl_get_asciiparam(req, param);
504                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
505                         name += strlen("/dev/");
506                 pp = g_provider_by_name(name);
507                 if (pp == NULL) {
508                         G_MIRROR_DEBUG(1, "Provider %s disappear?!", name);
509                         sbuf_printf(sb, " %s", name);
510                         continue;
511                 }
512                 md.md_did = arc4random();
513                 md.md_priority = no - 1;
514                 if (g_mirror_add_disk(sc, pp, &md) != 0) {
515                         G_MIRROR_DEBUG(1, "Disk %u (%s) not attached to %s.",
516                             no, pp->name, gp->name);
517                         sbuf_printf(sb, " %s", pp->name);
518                         continue;
519                 }
520                 attached++;
521         }
522         sbuf_finish(sb);
523         sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING;
524         if (md.md_all != attached ||
525             (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
526                 g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_HARD);
527                 gctl_error(req, "%s", sbuf_data(sb));
528         } else
529                 sx_xunlock(&sc->sc_lock);
530         sbuf_delete(sb);
531 }
532
533 static void
534 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
535 {
536         struct g_mirror_metadata md;
537         struct g_mirror_softc *sc;
538         struct g_mirror_disk *disk;
539         struct g_provider *pp;
540         const char *name;
541         char param[16];
542         int error, *nargs;
543         u_int i;
544
545         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
546         if (nargs == NULL) {
547                 gctl_error(req, "No '%s' argument.", "nargs");
548                 return;
549         }
550         if (*nargs < 2) {
551                 gctl_error(req, "Too few arguments.");
552                 return;
553         }
554         name = gctl_get_asciiparam(req, "arg0");
555         if (name == NULL) {
556                 gctl_error(req, "No 'arg%u' argument.", 0);
557                 return;
558         }
559         sc = g_mirror_find_device(mp, name);
560         if (sc == NULL) {
561                 gctl_error(req, "No such device: %s.", name);
562                 return;
563         }
564         for (i = 1; i < (u_int)*nargs; i++) {
565                 snprintf(param, sizeof(param), "arg%u", i);
566                 name = gctl_get_asciiparam(req, param);
567                 if (name == NULL) {
568                         gctl_error(req, "No 'arg%u' argument.", i);
569                         continue;
570                 }
571                 disk = g_mirror_find_disk(sc, name);
572                 if (disk == NULL) {
573                         gctl_error(req, "No such provider: %s.", name);
574                         continue;
575                 }
576                 if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
577                     disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
578                         /*
579                          * This is the last active disk. There will be nothing
580                          * to rebuild it from, so deny this request.
581                          */
582                         gctl_error(req,
583                             "Provider %s is the last active provider in %s.",
584                             name, sc->sc_geom->name);
585                         break;
586                 }
587                 /*
588                  * Do rebuild by resetting syncid, disconnecting the disk and
589                  * connecting it again.
590                  */
591                 disk->d_sync.ds_syncid = 0;
592                 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
593                         disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
594                 g_mirror_update_metadata(disk);
595                 pp = disk->d_consumer->provider;
596                 g_topology_lock();
597                 error = g_mirror_read_metadata(disk->d_consumer, &md);
598                 g_topology_unlock();
599                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
600                     G_MIRROR_EVENT_WAIT);
601                 if (error != 0) {
602                         gctl_error(req, "Cannot read metadata from %s.",
603                             pp->name);
604                         continue;
605                 }
606                 error = g_mirror_add_disk(sc, pp, &md);
607                 if (error != 0) {
608                         gctl_error(req, "Cannot reconnect component %s.",
609                             pp->name);
610                         continue;
611                 }
612         }
613         sx_xunlock(&sc->sc_lock);
614 }
615
616 static void
617 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
618 {
619         struct g_mirror_softc *sc;
620         struct g_mirror_disk *disk;
621         struct g_mirror_metadata md;
622         struct g_provider *pp;
623         struct g_consumer *cp;
624         intmax_t *priority;
625         const char *name;
626         char param[16];
627         u_char *sector;
628         u_int i, n;
629         int error, *nargs, *hardcode, *inactive;
630         struct {
631                 struct g_provider       *provider;
632                 struct g_consumer       *consumer;
633         } *disks;
634         off_t mdsize;
635
636         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
637         if (nargs == NULL) {
638                 gctl_error(req, "No '%s' argument.", "nargs");
639                 return;
640         }
641         if (*nargs < 2) {
642                 gctl_error(req, "Too few arguments.");
643                 return;
644         }
645         priority = gctl_get_paraml(req, "priority", sizeof(*priority));
646         if (priority == NULL) {
647                 gctl_error(req, "No '%s' argument.", "priority");
648                 return;
649         }
650         inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
651         if (inactive == NULL) {
652                 gctl_error(req, "No '%s' argument.", "inactive");
653                 return;
654         }
655         hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
656         if (hardcode == NULL) {
657                 gctl_error(req, "No '%s' argument.", "hardcode");
658                 return;
659         }
660         name = gctl_get_asciiparam(req, "arg0");
661         if (name == NULL) {
662                 gctl_error(req, "No 'arg%u' argument.", 0);
663                 return;
664         }
665         sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
666         if (sc == NULL) {
667                 gctl_error(req, "No such device: %s.", name);
668                 return;
669         }
670         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
671                 gctl_error(req, "Not all disks connected.");
672                 sx_xunlock(&sc->sc_lock);
673                 return;
674         }
675
676         disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
677         g_topology_lock();
678         for (i = 1, n = 0; i < (u_int)*nargs; i++) {
679                 snprintf(param, sizeof(param), "arg%u", i);
680                 name = gctl_get_asciiparam(req, param);
681                 if (name == NULL) {
682                         gctl_error(req, "No 'arg%u' argument.", i);
683                         continue;
684                 }
685                 if (g_mirror_find_disk(sc, name) != NULL) {
686                         gctl_error(req, "Provider %s already inserted.", name);
687                         continue;
688                 }
689                 if (strncmp(name, "/dev/", 5) == 0)
690                         name += 5;
691                 pp = g_provider_by_name(name);
692                 if (pp == NULL) {
693                         gctl_error(req, "Unknown provider %s.", name);
694                         continue;
695                 }
696                 cp = g_new_consumer(sc->sc_geom);
697                 if (g_attach(cp, pp) != 0) {
698                         g_destroy_consumer(cp);
699                         gctl_error(req, "Cannot attach to provider %s.", name);
700                         continue;
701                 }
702                 if (g_access(cp, 0, 1, 1) != 0) {
703                         gctl_error(req, "Cannot access provider %s.", name);
704 err:
705                         g_detach(cp);
706                         g_destroy_consumer(cp);
707                         continue;
708                 }
709                 mdsize = (sc->sc_type == G_MIRROR_TYPE_AUTOMATIC) ?
710                     pp->sectorsize : 0;
711                 if (sc->sc_provider->mediasize > pp->mediasize - mdsize) {
712                         gctl_error(req, "Provider %s too small.", name);
713 err2:
714                         g_access(cp, 0, -1, -1);
715                         goto err;
716                 }
717                 if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
718                         gctl_error(req, "Invalid sectorsize of provider %s.",
719                             name);
720                         goto err2;
721                 }
722                 if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) {
723                         g_access(cp, 0, -1, -1);
724                         g_detach(cp);
725                         g_destroy_consumer(cp);
726                         g_topology_unlock();
727                         sc->sc_ndisks++;
728                         g_mirror_fill_metadata(sc, NULL, &md);
729                         md.md_priority = *priority;
730                         if (*inactive)
731                                 md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
732                         if (g_mirror_add_disk(sc, pp, &md) != 0) {
733                                 sc->sc_ndisks--;
734                                 gctl_error(req, "Disk %s not inserted.", name);
735                         }
736                         g_topology_lock();
737                         continue;
738                 }
739                 disks[n].provider = pp;
740                 disks[n].consumer = cp;
741                 n++;
742         }
743         if (n == 0) {
744                 g_topology_unlock();
745                 sx_xunlock(&sc->sc_lock);
746                 g_free(disks);
747                 return;
748         }
749         sc->sc_ndisks += n;
750 again:
751         for (i = 0; i < n; i++) {
752                 if (disks[i].consumer == NULL)
753                         continue;
754                 g_mirror_fill_metadata(sc, NULL, &md);
755                 md.md_priority = *priority;
756                 if (*inactive)
757                         md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
758                 pp = disks[i].provider;
759                 if (*hardcode) {
760                         strlcpy(md.md_provider, pp->name,
761                             sizeof(md.md_provider));
762                 } else {
763                         bzero(md.md_provider, sizeof(md.md_provider));
764                 }
765                 md.md_provsize = pp->mediasize;
766                 sector = g_malloc(pp->sectorsize, M_WAITOK);
767                 mirror_metadata_encode(&md, sector);
768                 error = g_write_data(disks[i].consumer,
769                     pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
770                 g_free(sector);
771                 if (error != 0) {
772                         gctl_error(req, "Cannot store metadata on %s.",
773                             pp->name);
774                         g_access(disks[i].consumer, 0, -1, -1);
775                         g_detach(disks[i].consumer);
776                         g_destroy_consumer(disks[i].consumer);
777                         disks[i].consumer = NULL;
778                         disks[i].provider = NULL;
779                         sc->sc_ndisks--;
780                         goto again;
781                 }
782         }
783         g_topology_unlock();
784         if (i == 0) {
785                 /* All writes failed. */
786                 sx_xunlock(&sc->sc_lock);
787                 g_free(disks);
788                 return;
789         }
790         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
791                 g_mirror_update_metadata(disk);
792         }
793         /*
794          * Release provider and wait for retaste.
795          */
796         g_topology_lock();
797         for (i = 0; i < n; i++) {
798                 if (disks[i].consumer == NULL)
799                         continue;
800                 g_access(disks[i].consumer, 0, -1, -1);
801                 g_detach(disks[i].consumer);
802                 g_destroy_consumer(disks[i].consumer);
803         }
804         g_topology_unlock();
805         sx_xunlock(&sc->sc_lock);
806         g_free(disks);
807 }
808
809 static void
810 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
811 {
812         struct g_mirror_softc *sc;
813         struct g_mirror_disk *disk;
814         const char *name;
815         char param[16];
816         int *nargs;
817         u_int i, active;
818
819         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
820         if (nargs == NULL) {
821                 gctl_error(req, "No '%s' argument.", "nargs");
822                 return;
823         }
824         if (*nargs < 2) {
825                 gctl_error(req, "Too few arguments.");
826                 return;
827         }
828         name = gctl_get_asciiparam(req, "arg0");
829         if (name == NULL) {
830                 gctl_error(req, "No 'arg%u' argument.", 0);
831                 return;
832         }
833         sc = g_mirror_find_device(mp, name);
834         if (sc == NULL) {
835                 gctl_error(req, "No such device: %s.", name);
836                 return;
837         }
838         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
839                 sx_xunlock(&sc->sc_lock);
840                 gctl_error(req, "Not all disks connected. Try 'forget' command "
841                     "first.");
842                 return;
843         }
844         active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
845         for (i = 1; i < (u_int)*nargs; i++) {
846                 snprintf(param, sizeof(param), "arg%u", i);
847                 name = gctl_get_asciiparam(req, param);
848                 if (name == NULL) {
849                         gctl_error(req, "No 'arg%u' argument.", i);
850                         continue;
851                 }
852                 disk = g_mirror_find_disk(sc, name);
853                 if (disk == NULL) {
854                         gctl_error(req, "No such provider: %s.", name);
855                         continue;
856                 }
857                 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
858                         if (active > 1)
859                                 active--;
860                         else {
861                                 gctl_error(req, "%s: Can't remove the last "
862                                     "ACTIVE component %s.", sc->sc_geom->name,
863                                     name);
864                                 continue;
865                         }
866                 }
867                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
868                     G_MIRROR_EVENT_DONTWAIT);
869         }
870         sx_xunlock(&sc->sc_lock);
871 }
872
873 static void
874 g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
875 {
876         struct g_mirror_softc *sc;
877         struct g_mirror_disk *disk;
878         uint64_t mediasize;
879         const char *name, *s;
880         char *x;
881         int *nargs;
882
883         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
884         if (nargs == NULL) {
885                 gctl_error(req, "No '%s' argument.", "nargs");
886                 return;
887         }
888         if (*nargs != 1) {
889                 gctl_error(req, "Missing device.");
890                 return;
891         }
892         name = gctl_get_asciiparam(req, "arg0");
893         if (name == NULL) {
894                 gctl_error(req, "No 'arg%u' argument.", 0);
895                 return;
896         }
897         s = gctl_get_asciiparam(req, "size");
898         if (s == NULL) {
899                 gctl_error(req, "No '%s' argument.", "size");
900                 return;
901         }
902         mediasize = strtouq(s, &x, 0);
903         if (*x != '\0' || mediasize == 0) {
904                 gctl_error(req, "Invalid '%s' argument.", "size");
905                 return;
906         }
907         sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
908         if (sc == NULL) {
909                 gctl_error(req, "No such device: %s.", name);
910                 return;
911         }
912         /* Deny shrinking of an opened provider */
913         if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && sc->sc_provider_open > 0) {
914                 if (sc->sc_mediasize > mediasize) {
915                         gctl_error(req, "Device %s is busy.",
916                             sc->sc_provider->name);
917                         sx_xunlock(&sc->sc_lock);
918                         return;
919                 }
920         }
921         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
922                 if (mediasize > disk->d_consumer->provider->mediasize -
923                     disk->d_consumer->provider->sectorsize) {
924                         gctl_error(req, "Provider %s is too small.",
925                             disk->d_name);
926                         sx_xunlock(&sc->sc_lock);
927                         return;
928                 }
929         }
930         /* Update the size. */
931         sc->sc_mediasize = mediasize;
932         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
933                 g_mirror_update_metadata(disk);
934         }
935         g_topology_lock();
936         g_resize_provider(sc->sc_provider, mediasize);
937         g_topology_unlock();
938         sx_xunlock(&sc->sc_lock);
939 }
940
941 static void
942 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
943 {
944         struct g_mirror_softc *sc;
945         struct g_mirror_disk *disk;
946         const char *name;
947         char param[16];
948         int *nargs;
949         u_int i, active;
950
951         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
952         if (nargs == NULL) {
953                 gctl_error(req, "No '%s' argument.", "nargs");
954                 return;
955         }
956         if (*nargs < 2) {
957                 gctl_error(req, "Too few arguments.");
958                 return;
959         }
960         name = gctl_get_asciiparam(req, "arg0");
961         if (name == NULL) {
962                 gctl_error(req, "No 'arg%u' argument.", 0);
963                 return;
964         }
965         sc = g_mirror_find_device(mp, name);
966         if (sc == NULL) {
967                 gctl_error(req, "No such device: %s.", name);
968                 return;
969         }
970         active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
971         for (i = 1; i < (u_int)*nargs; i++) {
972                 snprintf(param, sizeof(param), "arg%u", i);
973                 name = gctl_get_asciiparam(req, param);
974                 if (name == NULL) {
975                         gctl_error(req, "No 'arg%u' argument.", i);
976                         continue;
977                 }
978                 disk = g_mirror_find_disk(sc, name);
979                 if (disk == NULL) {
980                         gctl_error(req, "No such provider: %s.", name);
981                         continue;
982                 }
983                 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
984                         if (active > 1)
985                                 active--;
986                         else {
987                                 gctl_error(req, "%s: Can't deactivate the "
988                                     "last ACTIVE component %s.",
989                                     sc->sc_geom->name, name);
990                                 continue;
991                         }
992                 }
993                 disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
994                 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
995                 g_mirror_update_metadata(disk);
996                 sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
997                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
998                     G_MIRROR_EVENT_DONTWAIT);
999         }
1000         sx_xunlock(&sc->sc_lock);
1001 }
1002
1003 static void
1004 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
1005 {
1006         struct g_mirror_softc *sc;
1007         struct g_mirror_disk *disk;
1008         const char *name;
1009         char param[16];
1010         int *nargs;
1011         u_int i;
1012
1013         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1014         if (nargs == NULL) {
1015                 gctl_error(req, "No '%s' argument.", "nargs");
1016                 return;
1017         }
1018         if (*nargs < 1) {
1019                 gctl_error(req, "Missing device(s).");
1020                 return;
1021         }
1022
1023         for (i = 0; i < (u_int)*nargs; i++) {
1024                 snprintf(param, sizeof(param), "arg%u", i);
1025                 name = gctl_get_asciiparam(req, param);
1026                 if (name == NULL) {
1027                         gctl_error(req, "No 'arg%u' argument.", i);
1028                         return;
1029                 }
1030                 sc = g_mirror_find_device(mp, name);
1031                 if (sc == NULL) {
1032                         gctl_error(req, "No such device: %s.", name);
1033                         return;
1034                 }
1035                 if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
1036                         sx_xunlock(&sc->sc_lock);
1037                         G_MIRROR_DEBUG(1,
1038                             "All disks connected in %s, skipping.",
1039                             sc->sc_name);
1040                         continue;
1041                 }
1042                 sc->sc_ndisks = g_mirror_ndisks(sc, -1);
1043                 LIST_FOREACH(disk, &sc->sc_disks, d_next) {
1044                         g_mirror_update_metadata(disk);
1045                 }
1046                 sx_xunlock(&sc->sc_lock);
1047         }
1048 }
1049
1050 static void
1051 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
1052 {
1053         struct g_mirror_softc *sc;
1054         int *force, *nargs, error;
1055         const char *name;
1056         char param[16];
1057         u_int i;
1058         int how;
1059
1060         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1061         if (nargs == NULL) {
1062                 gctl_error(req, "No '%s' argument.", "nargs");
1063                 return;
1064         }
1065         if (*nargs < 1) {
1066                 gctl_error(req, "Missing device(s).");
1067                 return;
1068         }
1069         force = gctl_get_paraml(req, "force", sizeof(*force));
1070         if (force == NULL) {
1071                 gctl_error(req, "No '%s' argument.", "force");
1072                 return;
1073         }
1074         if (*force)
1075                 how = G_MIRROR_DESTROY_HARD;
1076         else
1077                 how = G_MIRROR_DESTROY_SOFT;
1078
1079         for (i = 0; i < (u_int)*nargs; i++) {
1080                 snprintf(param, sizeof(param), "arg%u", i);
1081                 name = gctl_get_asciiparam(req, param);
1082                 if (name == NULL) {
1083                         gctl_error(req, "No 'arg%u' argument.", i);
1084                         return;
1085                 }
1086                 sc = g_mirror_find_device(mp, name);
1087                 if (sc == NULL) {
1088                         gctl_error(req, "No such device: %s.", name);
1089                         return;
1090                 }
1091                 g_cancel_event(sc);
1092                 if (wipe)
1093                         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
1094                 error = g_mirror_destroy(sc, how);
1095                 if (error != 0) {
1096                         gctl_error(req, "Cannot destroy device %s (error=%d).",
1097                             sc->sc_geom->name, error);
1098                         if (wipe)
1099                                 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
1100                         sx_xunlock(&sc->sc_lock);
1101                         return;
1102                 }
1103                 /* No need to unlock, because lock is already dead. */
1104         }
1105 }
1106
1107 void
1108 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1109 {
1110         uint32_t *version;
1111
1112         g_topology_assert();
1113
1114         version = gctl_get_paraml(req, "version", sizeof(*version));
1115         if (version == NULL) {
1116                 gctl_error(req, "No '%s' argument.", "version");
1117                 return;
1118         }
1119         if (*version != G_MIRROR_VERSION) {
1120                 gctl_error(req, "Userland and kernel parts are out of sync.");
1121                 return;
1122         }
1123
1124         g_topology_unlock();
1125         if (strcmp(verb, "configure") == 0)
1126                 g_mirror_ctl_configure(req, mp);
1127         else if (strcmp(verb, "create") == 0)
1128                 g_mirror_ctl_create(req, mp);
1129         else if (strcmp(verb, "rebuild") == 0)
1130                 g_mirror_ctl_rebuild(req, mp);
1131         else if (strcmp(verb, "insert") == 0)
1132                 g_mirror_ctl_insert(req, mp);
1133         else if (strcmp(verb, "remove") == 0)
1134                 g_mirror_ctl_remove(req, mp);
1135         else if (strcmp(verb, "resize") == 0)
1136                 g_mirror_ctl_resize(req, mp);
1137         else if (strcmp(verb, "deactivate") == 0)
1138                 g_mirror_ctl_deactivate(req, mp);
1139         else if (strcmp(verb, "forget") == 0)
1140                 g_mirror_ctl_forget(req, mp);
1141         else if (strcmp(verb, "stop") == 0)
1142                 g_mirror_ctl_stop(req, mp, 0);
1143         else if (strcmp(verb, "destroy") == 0)
1144                 g_mirror_ctl_stop(req, mp, 1);
1145         else
1146                 gctl_error(req, "Unknown verb.");
1147         g_topology_lock();
1148 }