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