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