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