]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/geom/mirror/g_mirror_ctl.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / geom / mirror / g_mirror_ctl.c
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/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/bio.h>
37 #include <sys/sysctl.h>
38 #include <sys/malloc.h>
39 #include <sys/bitstring.h>
40 #include <vm/uma.h>
41 #include <machine/atomic.h>
42 #include <geom/geom.h>
43 #include <sys/proc.h>
44 #include <sys/kthread.h>
45 #include <geom/mirror/g_mirror.h>
46
47
48 static struct g_mirror_softc *
49 g_mirror_find_device(struct g_class *mp, const char *name)
50 {
51         struct g_mirror_softc *sc;
52         struct g_geom *gp;
53
54         g_topology_lock();
55         LIST_FOREACH(gp, &mp->geom, geom) {
56                 sc = gp->softc;
57                 if (sc == NULL)
58                         continue;
59                 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0)
60                         continue;
61                 if (strcmp(gp->name, name) == 0 ||
62                     strcmp(sc->sc_name, name) == 0) {
63                         g_topology_unlock();
64                         sx_xlock(&sc->sc_lock);
65                         return (sc);
66                 }
67         }
68         g_topology_unlock();
69         return (NULL);
70 }
71
72 static struct g_mirror_disk *
73 g_mirror_find_disk(struct g_mirror_softc *sc, const char *name)
74 {
75         struct g_mirror_disk *disk;
76
77         sx_assert(&sc->sc_lock, SX_XLOCKED);
78         if (strncmp(name, "/dev/", 5) == 0)
79                 name += 5;
80         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
81                 if (disk->d_consumer == NULL)
82                         continue;
83                 if (disk->d_consumer->provider == NULL)
84                         continue;
85                 if (strcmp(disk->d_consumer->provider->name, name) == 0)
86                         return (disk);
87         }
88         return (NULL);
89 }
90
91 static void
92 g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp)
93 {
94         struct g_mirror_softc *sc;
95         struct g_mirror_disk *disk;
96         const char *name, *balancep, *prov;
97         intmax_t *slicep, *priority;
98         uint32_t slice;
99         uint8_t balance;
100         int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic;
101         int *nargs, do_sync = 0, dirty = 1, do_priority = 0;
102
103         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
104         if (nargs == NULL) {
105                 gctl_error(req, "No '%s' argument.", "nargs");
106                 return;
107         }
108         if (*nargs != 1 && *nargs != 2) {
109                 gctl_error(req, "Invalid number of arguments.");
110                 return;
111         }
112         name = gctl_get_asciiparam(req, "arg0");
113         if (name == NULL) {
114                 gctl_error(req, "No 'arg%u' argument.", 0);
115                 return;
116         }
117         balancep = gctl_get_asciiparam(req, "balance");
118         if (balancep == NULL) {
119                 gctl_error(req, "No '%s' argument.", "balance");
120                 return;
121         }
122         autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync));
123         if (autosync == NULL) {
124                 gctl_error(req, "No '%s' argument.", "autosync");
125                 return;
126         }
127         noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
128         if (noautosync == NULL) {
129                 gctl_error(req, "No '%s' argument.", "noautosync");
130                 return;
131         }
132         failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync));
133         if (failsync == NULL) {
134                 gctl_error(req, "No '%s' argument.", "failsync");
135                 return;
136         }
137         nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync));
138         if (nofailsync == NULL) {
139                 gctl_error(req, "No '%s' argument.", "nofailsync");
140                 return;
141         }
142         hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
143         if (hardcode == NULL) {
144                 gctl_error(req, "No '%s' argument.", "hardcode");
145                 return;
146         }
147         dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic));
148         if (dynamic == NULL) {
149                 gctl_error(req, "No '%s' argument.", "dynamic");
150                 return;
151         }
152         priority = gctl_get_paraml(req, "priority", sizeof(*priority));
153         if (priority == NULL) {
154                 gctl_error(req, "No '%s' argument.", "priority");
155                 return;
156         }
157         if (*priority < -1 || *priority > 255) {
158                 gctl_error(req, "Priority range is 0 to 255, %jd given",
159                     *priority);
160                 return;
161         }
162         /* 
163          * Since we have a priority, we also need a provider now.
164          * Note: be WARNS safe, by always assigning prov and only throw an
165          * error if *priority != -1.
166          */
167         prov = gctl_get_asciiparam(req, "arg1");
168         if (*priority > -1) {
169                 if (prov == NULL) {
170                         gctl_error(req, "Priority needs a disk name");
171                         return;
172                 }
173                 do_priority = 1;
174         }
175         if (*autosync && *noautosync) {
176                 gctl_error(req, "'%s' and '%s' specified.", "autosync",
177                     "noautosync");
178                 return;
179         }
180         if (*failsync && *nofailsync) {
181                 gctl_error(req, "'%s' and '%s' specified.", "failsync",
182                     "nofailsync");
183                 return;
184         }
185         if (*hardcode && *dynamic) {
186                 gctl_error(req, "'%s' and '%s' specified.", "hardcode",
187                     "dynamic");
188                 return;
189         }
190         sc = g_mirror_find_device(mp, name);
191         if (sc == NULL) {
192                 gctl_error(req, "No such device: %s.", name);
193                 return;
194         }
195         if (*balancep == '\0')
196                 balance = sc->sc_balance;
197         else {
198                 if (balance_id(balancep) == -1) {
199                         gctl_error(req, "Invalid balance algorithm.");
200                         sx_xunlock(&sc->sc_lock);
201                         return;
202                 }
203                 balance = balance_id(balancep);
204         }
205         slicep = gctl_get_paraml(req, "slice", sizeof(*slicep));
206         if (slicep == NULL) {
207                 gctl_error(req, "No '%s' argument.", "slice");
208                 sx_xunlock(&sc->sc_lock);
209                 return;
210         }
211         if (*slicep == -1)
212                 slice = sc->sc_slice;
213         else
214                 slice = *slicep;
215         /* Enforce usage() of -p not allowing any other options. */
216         if (do_priority && (*autosync || *noautosync || *failsync ||
217             *nofailsync || *hardcode || *dynamic || *slicep != -1 ||
218             *balancep != '\0')) {
219                 sx_xunlock(&sc->sc_lock);
220                 gctl_error(req, "only -p accepted when setting priority");
221                 return;
222         }
223         if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync &&
224             !*noautosync && !*failsync && !*nofailsync && !*hardcode &&
225             !*dynamic && !do_priority) {
226                 sx_xunlock(&sc->sc_lock);
227                 gctl_error(req, "Nothing has changed.");
228                 return;
229         }
230         if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) {
231                 sx_xunlock(&sc->sc_lock);
232                 gctl_error(req, "Invalid number of arguments.");
233                 return;
234         }
235         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
236                 sx_xunlock(&sc->sc_lock);
237                 gctl_error(req, "Not all disks connected. Try 'forget' command "
238                     "first.");
239                 return;
240         }
241         sc->sc_balance = balance;
242         sc->sc_slice = slice;
243         if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) {
244                 if (*autosync) {
245                         sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
246                         do_sync = 1;
247                 }
248         } else {
249                 if (*noautosync)
250                         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
251         }
252         if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) {
253                 if (*failsync)
254                         sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
255         } else {
256                 if (*nofailsync) {
257                         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
258                         dirty = 0;
259                 }
260         }
261         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
262                 /*
263                  * Handle priority first, since we only need one disk, do one
264                  * operation on it and then we're done. No need to check other
265                  * flags, as usage doesn't allow it.
266                  */
267                 if (do_priority) {
268                         if (strcmp(disk->d_name, prov) == 0) {
269                                 if (disk->d_priority == *priority)
270                                         gctl_error(req, "Nothing has changed.");
271                                 else {
272                                         disk->d_priority = *priority;
273                                         g_mirror_update_metadata(disk);
274                                 }
275                                 break;
276                         }
277                         continue;
278                 }
279                 if (do_sync) {
280                         if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING)
281                                 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
282                 }
283                 if (*hardcode)
284                         disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED;
285                 else if (*dynamic)
286                         disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED;
287                 if (!dirty)
288                         disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY;
289                 g_mirror_update_metadata(disk);
290                 if (do_sync) {
291                         if (disk->d_state == G_MIRROR_DISK_STATE_STALE) {
292                                 g_mirror_event_send(disk,
293                                     G_MIRROR_DISK_STATE_DISCONNECTED,
294                                     G_MIRROR_EVENT_DONTWAIT);
295                         }
296                 }
297         }
298         sx_xunlock(&sc->sc_lock);
299 }
300
301 static void
302 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
303 {
304         struct g_mirror_metadata md;
305         struct g_mirror_softc *sc;
306         struct g_mirror_disk *disk;
307         struct g_provider *pp;
308         const char *name;
309         char param[16];
310         int error, *nargs;
311         u_int i;
312
313         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
314         if (nargs == NULL) {
315                 gctl_error(req, "No '%s' argument.", "nargs");
316                 return;
317         }
318         if (*nargs < 2) {
319                 gctl_error(req, "Too few arguments.");
320                 return;
321         }
322         name = gctl_get_asciiparam(req, "arg0");
323         if (name == NULL) {
324                 gctl_error(req, "No 'arg%u' argument.", 0);
325                 return;
326         }
327         sc = g_mirror_find_device(mp, name);
328         if (sc == NULL) {
329                 gctl_error(req, "No such device: %s.", name);
330                 return;
331         }
332         for (i = 1; i < (u_int)*nargs; i++) {
333                 snprintf(param, sizeof(param), "arg%u", i);
334                 name = gctl_get_asciiparam(req, param);
335                 if (name == NULL) {
336                         gctl_error(req, "No 'arg%u' argument.", i);
337                         continue;
338                 }
339                 disk = g_mirror_find_disk(sc, name);
340                 if (disk == NULL) {
341                         gctl_error(req, "No such provider: %s.", name);
342                         continue;
343                 }
344                 if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
345                     disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
346                         /*
347                          * This is the last active disk. There will be nothing
348                          * to rebuild it from, so deny this request.
349                          */
350                         gctl_error(req,
351                             "Provider %s is the last active provider in %s.",
352                             name, sc->sc_geom->name);
353                         break;
354                 }
355                 /*
356                  * Do rebuild by resetting syncid, disconnecting the disk and
357                  * connecting it again.
358                  */
359                 disk->d_sync.ds_syncid = 0;
360                 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
361                         disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
362                 g_mirror_update_metadata(disk);
363                 pp = disk->d_consumer->provider;
364                 g_topology_lock();
365                 error = g_mirror_read_metadata(disk->d_consumer, &md);
366                 g_topology_unlock();
367                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
368                     G_MIRROR_EVENT_WAIT);
369                 if (error != 0) {
370                         gctl_error(req, "Cannot read metadata from %s.",
371                             pp->name);
372                         continue;
373                 }
374                 error = g_mirror_add_disk(sc, pp, &md);
375                 if (error != 0) {
376                         gctl_error(req, "Cannot reconnect component %s.",
377                             pp->name);
378                         continue;
379                 }
380         }
381         sx_xunlock(&sc->sc_lock);
382 }
383
384 static void
385 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
386 {
387         struct g_mirror_softc *sc;
388         struct g_mirror_disk *disk;
389         struct g_mirror_metadata md;
390         struct g_provider *pp;
391         struct g_consumer *cp;
392         intmax_t *priority;
393         const char *name;
394         char param[16];
395         u_char *sector;
396         u_int i, n;
397         int error, *nargs, *hardcode, *inactive;
398         struct {
399                 struct g_provider       *provider;
400                 struct g_consumer       *consumer;
401         } *disks;
402
403         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
404         if (nargs == NULL) {
405                 gctl_error(req, "No '%s' argument.", "nargs");
406                 return;
407         }
408         if (*nargs < 2) {
409                 gctl_error(req, "Too few arguments.");
410                 return;
411         }
412         priority = gctl_get_paraml(req, "priority", sizeof(*priority));
413         if (priority == NULL) {
414                 gctl_error(req, "No '%s' argument.", "priority");
415                 return;
416         }
417         inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
418         if (inactive == NULL) {
419                 gctl_error(req, "No '%s' argument.", "inactive");
420                 return;
421         }
422         hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
423         if (hardcode == NULL) {
424                 gctl_error(req, "No '%s' argument.", "hardcode");
425                 return;
426         }
427         name = gctl_get_asciiparam(req, "arg0");
428         if (name == NULL) {
429                 gctl_error(req, "No 'arg%u' argument.", 0);
430                 return;
431         }
432         sc = g_mirror_find_device(mp, name);
433         if (sc == NULL) {
434                 gctl_error(req, "No such device: %s.", name);
435                 return;
436         }
437         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
438                 gctl_error(req, "Not all disks connected.");
439                 sx_xunlock(&sc->sc_lock);
440                 return;
441         }
442
443         disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
444         g_topology_lock();
445         for (i = 1, n = 0; i < (u_int)*nargs; i++) {
446                 snprintf(param, sizeof(param), "arg%u", i);
447                 name = gctl_get_asciiparam(req, param);
448                 if (name == NULL) {
449                         gctl_error(req, "No 'arg%u' argument.", i);
450                         continue;
451                 }
452                 if (g_mirror_find_disk(sc, name) != NULL) {
453                         gctl_error(req, "Provider %s already inserted.", name);
454                         continue;
455                 }
456                 if (strncmp(name, "/dev/", 5) == 0)
457                         name += 5;
458                 pp = g_provider_by_name(name);
459                 if (pp == NULL) {
460                         gctl_error(req, "Unknown provider %s.", name);
461                         continue;
462                 }
463                 if (sc->sc_provider->mediasize >
464                     pp->mediasize - pp->sectorsize) {
465                         gctl_error(req, "Provider %s too small.", name);
466                         continue;
467                 }
468                 if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
469                         gctl_error(req, "Invalid sectorsize of provider %s.",
470                             name);
471                         continue;
472                 }
473                 cp = g_new_consumer(sc->sc_geom);
474                 if (g_attach(cp, pp) != 0) {
475                         g_destroy_consumer(cp);
476                         gctl_error(req, "Cannot attach to provider %s.", name);
477                         continue;
478                 }
479                 if (g_access(cp, 0, 1, 1) != 0) {
480                         g_detach(cp);
481                         g_destroy_consumer(cp);
482                         gctl_error(req, "Cannot access provider %s.", name);
483                         continue;
484                 }
485                 disks[n].provider = pp;
486                 disks[n].consumer = cp;
487                 n++;
488         }
489         if (n == 0) {
490                 g_topology_unlock();
491                 sx_xunlock(&sc->sc_lock);
492                 g_free(disks);
493                 return;
494         }
495         sc->sc_ndisks += n;
496 again:
497         for (i = 0; i < n; i++) {
498                 if (disks[i].consumer == NULL)
499                         continue;
500                 g_mirror_fill_metadata(sc, NULL, &md);
501                 md.md_priority = *priority;
502                 if (*inactive)
503                         md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
504                 pp = disks[i].provider;
505                 if (*hardcode) {
506                         strlcpy(md.md_provider, pp->name,
507                             sizeof(md.md_provider));
508                 } else {
509                         bzero(md.md_provider, sizeof(md.md_provider));
510                 }
511                 md.md_provsize = pp->mediasize;
512                 sector = g_malloc(pp->sectorsize, M_WAITOK);
513                 mirror_metadata_encode(&md, sector);
514                 error = g_write_data(disks[i].consumer,
515                     pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
516                 g_free(sector);
517                 if (error != 0) {
518                         gctl_error(req, "Cannot store metadata on %s.",
519                             pp->name);
520                         g_access(disks[i].consumer, 0, -1, -1);
521                         g_detach(disks[i].consumer);
522                         g_destroy_consumer(disks[i].consumer);
523                         disks[i].consumer = NULL;
524                         disks[i].provider = NULL;
525                         sc->sc_ndisks--;
526                         goto again;
527                 }
528         }
529         g_topology_unlock();
530         if (i == 0) {
531                 /* All writes failed. */
532                 sx_xunlock(&sc->sc_lock);
533                 g_free(disks);
534                 return;
535         }
536         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
537                 g_mirror_update_metadata(disk);
538         }
539         /*
540          * Release provider and wait for retaste.
541          */
542         g_topology_lock();
543         for (i = 0; i < n; i++) {
544                 if (disks[i].consumer == NULL)
545                         continue;
546                 g_access(disks[i].consumer, 0, -1, -1);
547                 g_detach(disks[i].consumer);
548                 g_destroy_consumer(disks[i].consumer);
549         }
550         g_topology_unlock();
551         sx_xunlock(&sc->sc_lock);
552         g_free(disks);
553 }
554
555 static void
556 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
557 {
558         struct g_mirror_softc *sc;
559         struct g_mirror_disk *disk;
560         const char *name;
561         char param[16];
562         int *nargs;
563         u_int i, active;
564
565         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
566         if (nargs == NULL) {
567                 gctl_error(req, "No '%s' argument.", "nargs");
568                 return;
569         }
570         if (*nargs < 2) {
571                 gctl_error(req, "Too few arguments.");
572                 return;
573         }
574         name = gctl_get_asciiparam(req, "arg0");
575         if (name == NULL) {
576                 gctl_error(req, "No 'arg%u' argument.", 0);
577                 return;
578         }
579         sc = g_mirror_find_device(mp, name);
580         if (sc == NULL) {
581                 gctl_error(req, "No such device: %s.", name);
582                 return;
583         }
584         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
585                 sx_xunlock(&sc->sc_lock);
586                 gctl_error(req, "Not all disks connected. Try 'forget' command "
587                     "first.");
588                 return;
589         }
590         active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
591         for (i = 1; i < (u_int)*nargs; i++) {
592                 snprintf(param, sizeof(param), "arg%u", i);
593                 name = gctl_get_asciiparam(req, param);
594                 if (name == NULL) {
595                         gctl_error(req, "No 'arg%u' argument.", i);
596                         continue;
597                 }
598                 disk = g_mirror_find_disk(sc, name);
599                 if (disk == NULL) {
600                         gctl_error(req, "No such provider: %s.", name);
601                         continue;
602                 }
603                 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
604                         if (active > 1)
605                                 active--;
606                         else {
607                                 gctl_error(req, "%s: Can't remove the last "
608                                     "ACTIVE component %s.", sc->sc_geom->name,
609                                     name);
610                                 continue;
611                         }
612                 }
613                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
614                     G_MIRROR_EVENT_DONTWAIT);
615         }
616         sx_xunlock(&sc->sc_lock);
617 }
618
619 static void
620 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
621 {
622         struct g_mirror_softc *sc;
623         struct g_mirror_disk *disk;
624         const char *name;
625         char param[16];
626         int *nargs;
627         u_int i;
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         name = gctl_get_asciiparam(req, "arg0");
639         if (name == NULL) {
640                 gctl_error(req, "No 'arg%u' argument.", 0);
641                 return;
642         }
643         sc = g_mirror_find_device(mp, name);
644         if (sc == NULL) {
645                 gctl_error(req, "No such device: %s.", name);
646                 return;
647         }
648         for (i = 1; i < (u_int)*nargs; i++) {
649                 snprintf(param, sizeof(param), "arg%u", i);
650                 name = gctl_get_asciiparam(req, param);
651                 if (name == NULL) {
652                         gctl_error(req, "No 'arg%u' argument.", i);
653                         continue;
654                 }
655                 disk = g_mirror_find_disk(sc, name);
656                 if (disk == NULL) {
657                         gctl_error(req, "No such provider: %s.", name);
658                         continue;
659                 }
660                 disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
661                 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
662                 g_mirror_update_metadata(disk);
663                 sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
664                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
665                     G_MIRROR_EVENT_DONTWAIT);
666         }
667         sx_xunlock(&sc->sc_lock);
668 }
669
670 static void
671 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
672 {
673         struct g_mirror_softc *sc;
674         struct g_mirror_disk *disk;
675         const char *name;
676         char param[16];
677         int *nargs;
678         u_int i;
679
680         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
681         if (nargs == NULL) {
682                 gctl_error(req, "No '%s' argument.", "nargs");
683                 return;
684         }
685         if (*nargs < 1) {
686                 gctl_error(req, "Missing device(s).");
687                 return;
688         }
689
690         for (i = 0; i < (u_int)*nargs; i++) {
691                 snprintf(param, sizeof(param), "arg%u", i);
692                 name = gctl_get_asciiparam(req, param);
693                 if (name == NULL) {
694                         gctl_error(req, "No 'arg%u' argument.", i);
695                         return;
696                 }
697                 sc = g_mirror_find_device(mp, name);
698                 if (sc == NULL) {
699                         gctl_error(req, "No such device: %s.", name);
700                         return;
701                 }
702                 if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
703                         sx_xunlock(&sc->sc_lock);
704                         G_MIRROR_DEBUG(1,
705                             "All disks connected in %s, skipping.",
706                             sc->sc_name);
707                         continue;
708                 }
709                 sc->sc_ndisks = g_mirror_ndisks(sc, -1);
710                 LIST_FOREACH(disk, &sc->sc_disks, d_next) {
711                         g_mirror_update_metadata(disk);
712                 }
713                 sx_xunlock(&sc->sc_lock);
714         }
715 }
716
717 static void
718 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp)
719 {
720         struct g_mirror_softc *sc;
721         int *force, *nargs, error;
722         const char *name;
723         char param[16];
724         u_int i;
725         int how;
726
727         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
728         if (nargs == NULL) {
729                 gctl_error(req, "No '%s' argument.", "nargs");
730                 return;
731         }
732         if (*nargs < 1) {
733                 gctl_error(req, "Missing device(s).");
734                 return;
735         }
736         force = gctl_get_paraml(req, "force", sizeof(*force));
737         if (force == NULL) {
738                 gctl_error(req, "No '%s' argument.", "force");
739                 return;
740         }
741         if (*force)
742                 how = G_MIRROR_DESTROY_HARD;
743         else
744                 how = G_MIRROR_DESTROY_SOFT;
745
746         for (i = 0; i < (u_int)*nargs; i++) {
747                 snprintf(param, sizeof(param), "arg%u", i);
748                 name = gctl_get_asciiparam(req, param);
749                 if (name == NULL) {
750                         gctl_error(req, "No 'arg%u' argument.", i);
751                         return;
752                 }
753                 sc = g_mirror_find_device(mp, name);
754                 if (sc == NULL) {
755                         gctl_error(req, "No such device: %s.", name);
756                         return;
757                 }
758                 g_cancel_event(sc);
759                 error = g_mirror_destroy(sc, how);
760                 if (error != 0) {
761                         gctl_error(req, "Cannot destroy device %s (error=%d).",
762                             sc->sc_geom->name, error);
763                         sx_xunlock(&sc->sc_lock);
764                         return;
765                 }
766                 /* No need to unlock, because lock is already dead. */
767         }
768 }
769
770 void
771 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
772 {
773         uint32_t *version;
774
775         g_topology_assert();
776
777         version = gctl_get_paraml(req, "version", sizeof(*version));
778         if (version == NULL) {
779                 gctl_error(req, "No '%s' argument.", "version");
780                 return;
781         }
782         if (*version != G_MIRROR_VERSION) {
783                 gctl_error(req, "Userland and kernel parts are out of sync.");
784                 return;
785         }
786
787         g_topology_unlock();
788         if (strcmp(verb, "configure") == 0)
789                 g_mirror_ctl_configure(req, mp);
790         else if (strcmp(verb, "rebuild") == 0)
791                 g_mirror_ctl_rebuild(req, mp);
792         else if (strcmp(verb, "insert") == 0)
793                 g_mirror_ctl_insert(req, mp);
794         else if (strcmp(verb, "remove") == 0)
795                 g_mirror_ctl_remove(req, mp);
796         else if (strcmp(verb, "deactivate") == 0)
797                 g_mirror_ctl_deactivate(req, mp);
798         else if (strcmp(verb, "forget") == 0)
799                 g_mirror_ctl_forget(req, mp);
800         else if (strcmp(verb, "stop") == 0)
801                 g_mirror_ctl_stop(req, mp);
802         else
803                 gctl_error(req, "Unknown verb.");
804         g_topology_lock();
805 }