]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/geom/mirror/g_mirror_ctl.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 (strcmp(balancep, "none") == 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             strcmp(balancep, "none") != 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;
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         for (i = 1; i < (u_int)*nargs; i++) {
591                 snprintf(param, sizeof(param), "arg%u", i);
592                 name = gctl_get_asciiparam(req, param);
593                 if (name == NULL) {
594                         gctl_error(req, "No 'arg%u' argument.", i);
595                         continue;
596                 }
597                 disk = g_mirror_find_disk(sc, name);
598                 if (disk == NULL) {
599                         gctl_error(req, "No such provider: %s.", name);
600                         continue;
601                 }
602                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
603                     G_MIRROR_EVENT_DONTWAIT);
604         }
605         sx_xunlock(&sc->sc_lock);
606 }
607
608 static void
609 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
610 {
611         struct g_mirror_softc *sc;
612         struct g_mirror_disk *disk;
613         const char *name;
614         char param[16];
615         int *nargs;
616         u_int i;
617
618         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
619         if (nargs == NULL) {
620                 gctl_error(req, "No '%s' argument.", "nargs");
621                 return;
622         }
623         if (*nargs < 2) {
624                 gctl_error(req, "Too few arguments.");
625                 return;
626         }
627         name = gctl_get_asciiparam(req, "arg0");
628         if (name == NULL) {
629                 gctl_error(req, "No 'arg%u' argument.", 0);
630                 return;
631         }
632         sc = g_mirror_find_device(mp, name);
633         if (sc == NULL) {
634                 gctl_error(req, "No such device: %s.", name);
635                 return;
636         }
637         for (i = 1; i < (u_int)*nargs; i++) {
638                 snprintf(param, sizeof(param), "arg%u", i);
639                 name = gctl_get_asciiparam(req, param);
640                 if (name == NULL) {
641                         gctl_error(req, "No 'arg%u' argument.", i);
642                         continue;
643                 }
644                 disk = g_mirror_find_disk(sc, name);
645                 if (disk == NULL) {
646                         gctl_error(req, "No such provider: %s.", name);
647                         continue;
648                 }
649                 disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
650                 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
651                 g_mirror_update_metadata(disk);
652                 sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
653                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
654                     G_MIRROR_EVENT_DONTWAIT);
655         }
656         sx_xunlock(&sc->sc_lock);
657 }
658
659 static void
660 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
661 {
662         struct g_mirror_softc *sc;
663         struct g_mirror_disk *disk;
664         const char *name;
665         char param[16];
666         int *nargs;
667         u_int i;
668
669         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
670         if (nargs == NULL) {
671                 gctl_error(req, "No '%s' argument.", "nargs");
672                 return;
673         }
674         if (*nargs < 1) {
675                 gctl_error(req, "Missing device(s).");
676                 return;
677         }
678
679         for (i = 0; i < (u_int)*nargs; i++) {
680                 snprintf(param, sizeof(param), "arg%u", i);
681                 name = gctl_get_asciiparam(req, param);
682                 if (name == NULL) {
683                         gctl_error(req, "No 'arg%u' argument.", i);
684                         return;
685                 }
686                 sc = g_mirror_find_device(mp, name);
687                 if (sc == NULL) {
688                         gctl_error(req, "No such device: %s.", name);
689                         return;
690                 }
691                 if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
692                         sx_xunlock(&sc->sc_lock);
693                         G_MIRROR_DEBUG(1,
694                             "All disks connected in %s, skipping.",
695                             sc->sc_name);
696                         continue;
697                 }
698                 sc->sc_ndisks = g_mirror_ndisks(sc, -1);
699                 LIST_FOREACH(disk, &sc->sc_disks, d_next) {
700                         g_mirror_update_metadata(disk);
701                 }
702                 sx_xunlock(&sc->sc_lock);
703         }
704 }
705
706 static void
707 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp)
708 {
709         struct g_mirror_softc *sc;
710         int *force, *nargs, error;
711         const char *name;
712         char param[16];
713         u_int i;
714         int how;
715
716         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
717         if (nargs == NULL) {
718                 gctl_error(req, "No '%s' argument.", "nargs");
719                 return;
720         }
721         if (*nargs < 1) {
722                 gctl_error(req, "Missing device(s).");
723                 return;
724         }
725         force = gctl_get_paraml(req, "force", sizeof(*force));
726         if (force == NULL) {
727                 gctl_error(req, "No '%s' argument.", "force");
728                 return;
729         }
730         if (*force)
731                 how = G_MIRROR_DESTROY_HARD;
732         else
733                 how = G_MIRROR_DESTROY_SOFT;
734
735         for (i = 0; i < (u_int)*nargs; i++) {
736                 snprintf(param, sizeof(param), "arg%u", i);
737                 name = gctl_get_asciiparam(req, param);
738                 if (name == NULL) {
739                         gctl_error(req, "No 'arg%u' argument.", i);
740                         return;
741                 }
742                 sc = g_mirror_find_device(mp, name);
743                 if (sc == NULL) {
744                         gctl_error(req, "No such device: %s.", name);
745                         return;
746                 }
747                 g_cancel_event(sc);
748                 error = g_mirror_destroy(sc, how);
749                 if (error != 0) {
750                         gctl_error(req, "Cannot destroy device %s (error=%d).",
751                             sc->sc_geom->name, error);
752                         sx_xunlock(&sc->sc_lock);
753                         return;
754                 }
755                 /* No need to unlock, because lock is already dead. */
756         }
757 }
758
759 void
760 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
761 {
762         uint32_t *version;
763
764         g_topology_assert();
765
766         version = gctl_get_paraml(req, "version", sizeof(*version));
767         if (version == NULL) {
768                 gctl_error(req, "No '%s' argument.", "version");
769                 return;
770         }
771         if (*version != G_MIRROR_VERSION) {
772                 gctl_error(req, "Userland and kernel parts are out of sync.");
773                 return;
774         }
775
776         g_topology_unlock();
777         if (strcmp(verb, "configure") == 0)
778                 g_mirror_ctl_configure(req, mp);
779         else if (strcmp(verb, "rebuild") == 0)
780                 g_mirror_ctl_rebuild(req, mp);
781         else if (strcmp(verb, "insert") == 0)
782                 g_mirror_ctl_insert(req, mp);
783         else if (strcmp(verb, "remove") == 0)
784                 g_mirror_ctl_remove(req, mp);
785         else if (strcmp(verb, "deactivate") == 0)
786                 g_mirror_ctl_deactivate(req, mp);
787         else if (strcmp(verb, "forget") == 0)
788                 g_mirror_ctl_forget(req, mp);
789         else if (strcmp(verb, "stop") == 0)
790                 g_mirror_ctl_stop(req, mp);
791         else
792                 gctl_error(req, "Unknown verb.");
793         g_topology_lock();
794 }