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