]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/mirror/g_mirror_ctl.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[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                 g_attach(cp, pp);
453                 if (g_access(cp, 1, 0, 0) != 0) {
454                         G_MIRROR_DEBUG(1, "Can't open disk %s.", pp->name);
455                         gctl_error(req, "Can't open disk %s.", pp->name);
456 err2:
457                         g_detach(cp);
458                         goto err;
459                 }
460                 if (pp->mediasize == 0 || pp->sectorsize == 0) {
461                         G_MIRROR_DEBUG(1, "Disk %s has no media.", pp->name);
462                         gctl_error(req, "Disk %s has no media.", pp->name);
463                         g_access(cp, -1, 0, 0);
464                         goto err2;
465                 }
466                 if (pp->mediasize < mediasize)
467                         mediasize = pp->mediasize;
468                 if (pp->sectorsize > sectorsize)
469                         sectorsize = pp->sectorsize;
470                 g_access(cp, -1, 0, 0);
471                 g_detach(cp);
472         }
473         g_destroy_consumer(cp);
474         g_destroy_geom(gp);
475         md.md_mediasize = mediasize;
476         md.md_sectorsize = sectorsize;
477         md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
478
479         gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_MANUAL);
480         if (gp == NULL) {
481                 gctl_error(req, "Can't create %s.", md.md_name);
482                 g_topology_unlock();
483                 return;
484         }
485
486         sc = gp->softc;
487         g_topology_unlock();
488         sx_xlock(&sc->sc_lock);
489         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING;
490         sb = sbuf_new_auto();
491         sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
492         for (attached = 0, no = 1; no < *nargs; no++) {
493                 snprintf(param, sizeof(param), "arg%u", no);
494                 pp = gctl_get_provider(req, param);
495                 if (pp == NULL) {
496                         name = gctl_get_asciiparam(req, param);
497                         MPASS(name != NULL);
498                         sbuf_printf(sb, " %s", name);
499                         continue;
500                 }
501                 md.md_did = arc4random();
502                 md.md_priority = no - 1;
503                 if (g_mirror_add_disk(sc, pp, &md) != 0) {
504                         G_MIRROR_DEBUG(1, "Disk %u (%s) not attached to %s.",
505                             no, pp->name, gp->name);
506                         sbuf_printf(sb, " %s", pp->name);
507                         continue;
508                 }
509                 attached++;
510         }
511         sbuf_finish(sb);
512         sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING;
513         if (md.md_all != attached ||
514             (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
515                 g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_HARD);
516                 gctl_error(req, "%s", sbuf_data(sb));
517         } else
518                 sx_xunlock(&sc->sc_lock);
519         sbuf_delete(sb);
520 }
521
522 static void
523 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp)
524 {
525         struct g_mirror_metadata md;
526         struct g_mirror_softc *sc;
527         struct g_mirror_disk *disk;
528         struct g_provider *pp;
529         const char *name;
530         char param[16];
531         int error, *nargs;
532         u_int i;
533
534         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
535         if (nargs == NULL) {
536                 gctl_error(req, "No '%s' argument.", "nargs");
537                 return;
538         }
539         if (*nargs < 2) {
540                 gctl_error(req, "Too few arguments.");
541                 return;
542         }
543         name = gctl_get_asciiparam(req, "arg0");
544         if (name == NULL) {
545                 gctl_error(req, "No 'arg%u' argument.", 0);
546                 return;
547         }
548         sc = g_mirror_find_device(mp, name);
549         if (sc == NULL) {
550                 gctl_error(req, "No such device: %s.", name);
551                 return;
552         }
553         for (i = 1; i < (u_int)*nargs; i++) {
554                 snprintf(param, sizeof(param), "arg%u", i);
555                 name = gctl_get_asciiparam(req, param);
556                 if (name == NULL) {
557                         gctl_error(req, "No 'arg%u' argument.", i);
558                         continue;
559                 }
560                 disk = g_mirror_find_disk(sc, name);
561                 if (disk == NULL) {
562                         gctl_error(req, "No such provider: %s.", name);
563                         continue;
564                 }
565                 if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 &&
566                     disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
567                         /*
568                          * This is the last active disk. There will be nothing
569                          * to rebuild it from, so deny this request.
570                          */
571                         gctl_error(req,
572                             "Provider %s is the last active provider in %s.",
573                             name, sc->sc_geom->name);
574                         break;
575                 }
576                 /*
577                  * Do rebuild by resetting syncid, disconnecting the disk and
578                  * connecting it again.
579                  */
580                 disk->d_sync.ds_syncid = 0;
581                 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0)
582                         disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC;
583                 g_mirror_update_metadata(disk);
584                 pp = disk->d_consumer->provider;
585                 g_topology_lock();
586                 error = g_mirror_read_metadata(disk->d_consumer, &md);
587                 g_topology_unlock();
588                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
589                     G_MIRROR_EVENT_WAIT);
590                 if (error != 0) {
591                         gctl_error(req, "Cannot read metadata from %s.",
592                             pp->name);
593                         continue;
594                 }
595                 error = g_mirror_add_disk(sc, pp, &md);
596                 if (error != 0) {
597                         gctl_error(req, "Cannot reconnect component %s.",
598                             pp->name);
599                         continue;
600                 }
601         }
602         sx_xunlock(&sc->sc_lock);
603 }
604
605 static void
606 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp)
607 {
608         struct g_mirror_softc *sc;
609         struct g_mirror_disk *disk;
610         struct g_mirror_metadata md;
611         struct g_provider *pp;
612         struct g_consumer *cp;
613         intmax_t *priority;
614         const char *name;
615         char param[16];
616         u_char *sector;
617         u_int i, n;
618         int error, *nargs, *hardcode, *inactive;
619         struct {
620                 struct g_provider       *provider;
621                 struct g_consumer       *consumer;
622         } *disks;
623         off_t mdsize;
624
625         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
626         if (nargs == NULL) {
627                 gctl_error(req, "No '%s' argument.", "nargs");
628                 return;
629         }
630         if (*nargs < 2) {
631                 gctl_error(req, "Too few arguments.");
632                 return;
633         }
634         priority = gctl_get_paraml(req, "priority", sizeof(*priority));
635         if (priority == NULL) {
636                 gctl_error(req, "No '%s' argument.", "priority");
637                 return;
638         }
639         inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive));
640         if (inactive == NULL) {
641                 gctl_error(req, "No '%s' argument.", "inactive");
642                 return;
643         }
644         hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
645         if (hardcode == NULL) {
646                 gctl_error(req, "No '%s' argument.", "hardcode");
647                 return;
648         }
649         name = gctl_get_asciiparam(req, "arg0");
650         if (name == NULL) {
651                 gctl_error(req, "No 'arg%u' argument.", 0);
652                 return;
653         }
654         sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
655         if (sc == NULL) {
656                 gctl_error(req, "No such device: %s.", name);
657                 return;
658         }
659         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
660                 gctl_error(req, "Not all disks connected.");
661                 sx_xunlock(&sc->sc_lock);
662                 return;
663         }
664
665         disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO);
666         g_topology_lock();
667         for (i = 1, n = 0; i < (u_int)*nargs; i++) {
668                 snprintf(param, sizeof(param), "arg%u", i);
669                 pp = gctl_get_provider(req, param);
670                 if (pp == NULL)
671                         continue;
672                 if (g_mirror_find_disk(sc, pp->name) != NULL) {
673                         gctl_error(req, "Provider %s already inserted.", pp->name);
674                         continue;
675                 }
676                 cp = g_new_consumer(sc->sc_geom);
677                 if (g_attach(cp, pp) != 0) {
678                         g_destroy_consumer(cp);
679                         gctl_error(req, "Cannot attach to provider %s.", pp->name);
680                         continue;
681                 }
682                 if (g_access(cp, 0, 1, 1) != 0) {
683                         gctl_error(req, "Cannot access provider %s.", pp->name);
684 err:
685                         g_detach(cp);
686                         g_destroy_consumer(cp);
687                         continue;
688                 }
689                 mdsize = (sc->sc_type == G_MIRROR_TYPE_AUTOMATIC) ?
690                     pp->sectorsize : 0;
691                 if (sc->sc_provider->mediasize > pp->mediasize - mdsize) {
692                         gctl_error(req, "Provider %s too small.", pp->name);
693 err2:
694                         g_access(cp, 0, -1, -1);
695                         goto err;
696                 }
697                 if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) {
698                         gctl_error(req, "Invalid sectorsize of provider %s.",
699                             pp->name);
700                         goto err2;
701                 }
702                 if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) {
703                         g_access(cp, 0, -1, -1);
704                         g_detach(cp);
705                         g_destroy_consumer(cp);
706                         g_topology_unlock();
707                         sc->sc_ndisks++;
708                         g_mirror_fill_metadata(sc, NULL, &md);
709                         md.md_priority = *priority;
710                         if (*inactive)
711                                 md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
712                         if (g_mirror_add_disk(sc, pp, &md) != 0) {
713                                 sc->sc_ndisks--;
714                                 gctl_error(req, "Disk %s not inserted.", pp->name);
715                         }
716                         g_topology_lock();
717                         continue;
718                 }
719                 disks[n].provider = pp;
720                 disks[n].consumer = cp;
721                 n++;
722         }
723         if (n == 0) {
724                 g_topology_unlock();
725                 sx_xunlock(&sc->sc_lock);
726                 g_free(disks);
727                 return;
728         }
729         sc->sc_ndisks += n;
730 again:
731         for (i = 0; i < n; i++) {
732                 if (disks[i].consumer == NULL)
733                         continue;
734                 g_mirror_fill_metadata(sc, NULL, &md);
735                 md.md_priority = *priority;
736                 if (*inactive)
737                         md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE;
738                 pp = disks[i].provider;
739                 if (*hardcode) {
740                         strlcpy(md.md_provider, pp->name,
741                             sizeof(md.md_provider));
742                 } else {
743                         bzero(md.md_provider, sizeof(md.md_provider));
744                 }
745                 md.md_provsize = pp->mediasize;
746                 sector = g_malloc(pp->sectorsize, M_WAITOK);
747                 mirror_metadata_encode(&md, sector);
748                 error = g_write_data(disks[i].consumer,
749                     pp->mediasize - pp->sectorsize, sector, pp->sectorsize);
750                 g_free(sector);
751                 if (error != 0) {
752                         gctl_error(req, "Cannot store metadata on %s.",
753                             pp->name);
754                         g_access(disks[i].consumer, 0, -1, -1);
755                         g_detach(disks[i].consumer);
756                         g_destroy_consumer(disks[i].consumer);
757                         disks[i].consumer = NULL;
758                         disks[i].provider = NULL;
759                         sc->sc_ndisks--;
760                         goto again;
761                 }
762         }
763         g_topology_unlock();
764         if (i == 0) {
765                 /* All writes failed. */
766                 sx_xunlock(&sc->sc_lock);
767                 g_free(disks);
768                 return;
769         }
770         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
771                 g_mirror_update_metadata(disk);
772         }
773         /*
774          * Release provider and wait for retaste.
775          */
776         g_topology_lock();
777         for (i = 0; i < n; i++) {
778                 if (disks[i].consumer == NULL)
779                         continue;
780                 g_access(disks[i].consumer, 0, -1, -1);
781                 g_detach(disks[i].consumer);
782                 g_destroy_consumer(disks[i].consumer);
783         }
784         g_topology_unlock();
785         sx_xunlock(&sc->sc_lock);
786         g_free(disks);
787 }
788
789 static void
790 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
791 {
792         struct g_mirror_softc *sc;
793         struct g_mirror_disk *disk;
794         const char *name;
795         char param[16];
796         int *nargs;
797         u_int i, active;
798
799         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
800         if (nargs == NULL) {
801                 gctl_error(req, "No '%s' argument.", "nargs");
802                 return;
803         }
804         if (*nargs < 2) {
805                 gctl_error(req, "Too few arguments.");
806                 return;
807         }
808         name = gctl_get_asciiparam(req, "arg0");
809         if (name == NULL) {
810                 gctl_error(req, "No 'arg%u' argument.", 0);
811                 return;
812         }
813         sc = g_mirror_find_device(mp, name);
814         if (sc == NULL) {
815                 gctl_error(req, "No such device: %s.", name);
816                 return;
817         }
818         if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) {
819                 sx_xunlock(&sc->sc_lock);
820                 gctl_error(req, "Not all disks connected. Try 'forget' command "
821                     "first.");
822                 return;
823         }
824         active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
825         for (i = 1; i < (u_int)*nargs; i++) {
826                 snprintf(param, sizeof(param), "arg%u", i);
827                 name = gctl_get_asciiparam(req, param);
828                 if (name == NULL) {
829                         gctl_error(req, "No 'arg%u' argument.", i);
830                         continue;
831                 }
832                 disk = g_mirror_find_disk(sc, name);
833                 if (disk == NULL) {
834                         gctl_error(req, "No such provider: %s.", name);
835                         continue;
836                 }
837                 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
838                         if (active > 1)
839                                 active--;
840                         else {
841                                 gctl_error(req, "%s: Can't remove the last "
842                                     "ACTIVE component %s.", sc->sc_geom->name,
843                                     name);
844                                 continue;
845                         }
846                 }
847                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY,
848                     G_MIRROR_EVENT_DONTWAIT);
849         }
850         sx_xunlock(&sc->sc_lock);
851 }
852
853 static void
854 g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
855 {
856         struct g_mirror_softc *sc;
857         struct g_mirror_disk *disk;
858         uint64_t mediasize;
859         const char *name, *s;
860         char *x;
861         int *nargs;
862
863         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
864         if (nargs == NULL) {
865                 gctl_error(req, "No '%s' argument.", "nargs");
866                 return;
867         }
868         if (*nargs != 1) {
869                 gctl_error(req, "Missing device.");
870                 return;
871         }
872         name = gctl_get_asciiparam(req, "arg0");
873         if (name == NULL) {
874                 gctl_error(req, "No 'arg%u' argument.", 0);
875                 return;
876         }
877         s = gctl_get_asciiparam(req, "size");
878         if (s == NULL) {
879                 gctl_error(req, "No '%s' argument.", "size");
880                 return;
881         }
882         mediasize = strtouq(s, &x, 0);
883         if (*x != '\0' || mediasize == 0) {
884                 gctl_error(req, "Invalid '%s' argument.", "size");
885                 return;
886         }
887         sc = g_mirror_find_launched_device(mp, name, M_WAITOK);
888         if (sc == NULL) {
889                 gctl_error(req, "No such device: %s.", name);
890                 return;
891         }
892         /* Deny shrinking of an opened provider */
893         if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && sc->sc_provider_open > 0) {
894                 if (sc->sc_mediasize > mediasize) {
895                         gctl_error(req, "Device %s is busy.",
896                             sc->sc_provider->name);
897                         sx_xunlock(&sc->sc_lock);
898                         return;
899                 }
900         }
901         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
902                 if (mediasize > disk->d_consumer->provider->mediasize -
903                     disk->d_consumer->provider->sectorsize) {
904                         gctl_error(req, "Provider %s is too small.",
905                             disk->d_name);
906                         sx_xunlock(&sc->sc_lock);
907                         return;
908                 }
909         }
910         /* Update the size. */
911         sc->sc_mediasize = mediasize;
912         LIST_FOREACH(disk, &sc->sc_disks, d_next) {
913                 g_mirror_update_metadata(disk);
914         }
915         g_topology_lock();
916         g_resize_provider(sc->sc_provider, mediasize);
917         g_topology_unlock();
918         sx_xunlock(&sc->sc_lock);
919 }
920
921 static void
922 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
923 {
924         struct g_mirror_softc *sc;
925         struct g_mirror_disk *disk;
926         const char *name;
927         char param[16];
928         int *nargs;
929         u_int i, active;
930
931         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
932         if (nargs == NULL) {
933                 gctl_error(req, "No '%s' argument.", "nargs");
934                 return;
935         }
936         if (*nargs < 2) {
937                 gctl_error(req, "Too few arguments.");
938                 return;
939         }
940         name = gctl_get_asciiparam(req, "arg0");
941         if (name == NULL) {
942                 gctl_error(req, "No 'arg%u' argument.", 0);
943                 return;
944         }
945         sc = g_mirror_find_device(mp, name);
946         if (sc == NULL) {
947                 gctl_error(req, "No such device: %s.", name);
948                 return;
949         }
950         active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE);
951         for (i = 1; i < (u_int)*nargs; i++) {
952                 snprintf(param, sizeof(param), "arg%u", i);
953                 name = gctl_get_asciiparam(req, param);
954                 if (name == NULL) {
955                         gctl_error(req, "No 'arg%u' argument.", i);
956                         continue;
957                 }
958                 disk = g_mirror_find_disk(sc, name);
959                 if (disk == NULL) {
960                         gctl_error(req, "No such provider: %s.", name);
961                         continue;
962                 }
963                 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) {
964                         if (active > 1)
965                                 active--;
966                         else {
967                                 gctl_error(req, "%s: Can't deactivate the "
968                                     "last ACTIVE component %s.",
969                                     sc->sc_geom->name, name);
970                                 continue;
971                         }
972                 }
973                 disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
974                 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
975                 g_mirror_update_metadata(disk);
976                 sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID;
977                 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
978                     G_MIRROR_EVENT_DONTWAIT);
979         }
980         sx_xunlock(&sc->sc_lock);
981 }
982
983 static void
984 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp)
985 {
986         struct g_mirror_softc *sc;
987         struct g_mirror_disk *disk;
988         const char *name;
989         char param[16];
990         int *nargs;
991         u_int i;
992
993         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
994         if (nargs == NULL) {
995                 gctl_error(req, "No '%s' argument.", "nargs");
996                 return;
997         }
998         if (*nargs < 1) {
999                 gctl_error(req, "Missing device(s).");
1000                 return;
1001         }
1002
1003         for (i = 0; i < (u_int)*nargs; i++) {
1004                 snprintf(param, sizeof(param), "arg%u", i);
1005                 name = gctl_get_asciiparam(req, param);
1006                 if (name == NULL) {
1007                         gctl_error(req, "No 'arg%u' argument.", i);
1008                         return;
1009                 }
1010                 sc = g_mirror_find_device(mp, name);
1011                 if (sc == NULL) {
1012                         gctl_error(req, "No such device: %s.", name);
1013                         return;
1014                 }
1015                 if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) {
1016                         sx_xunlock(&sc->sc_lock);
1017                         G_MIRROR_DEBUG(1,
1018                             "All disks connected in %s, skipping.",
1019                             sc->sc_name);
1020                         continue;
1021                 }
1022                 sc->sc_ndisks = g_mirror_ndisks(sc, -1);
1023                 LIST_FOREACH(disk, &sc->sc_disks, d_next) {
1024                         g_mirror_update_metadata(disk);
1025                 }
1026                 sx_xunlock(&sc->sc_lock);
1027         }
1028 }
1029
1030 static void
1031 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe)
1032 {
1033         struct g_mirror_softc *sc;
1034         int *force, *nargs, error;
1035         const char *name;
1036         char param[16];
1037         u_int i;
1038         int how;
1039
1040         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1041         if (nargs == NULL) {
1042                 gctl_error(req, "No '%s' argument.", "nargs");
1043                 return;
1044         }
1045         if (*nargs < 1) {
1046                 gctl_error(req, "Missing device(s).");
1047                 return;
1048         }
1049         force = gctl_get_paraml(req, "force", sizeof(*force));
1050         if (force == NULL) {
1051                 gctl_error(req, "No '%s' argument.", "force");
1052                 return;
1053         }
1054         if (*force)
1055                 how = G_MIRROR_DESTROY_HARD;
1056         else
1057                 how = G_MIRROR_DESTROY_SOFT;
1058
1059         for (i = 0; i < (u_int)*nargs; i++) {
1060                 snprintf(param, sizeof(param), "arg%u", i);
1061                 name = gctl_get_asciiparam(req, param);
1062                 if (name == NULL) {
1063                         gctl_error(req, "No 'arg%u' argument.", i);
1064                         return;
1065                 }
1066                 sc = g_mirror_find_device(mp, name);
1067                 if (sc == NULL) {
1068                         gctl_error(req, "No such device: %s.", name);
1069                         return;
1070                 }
1071                 g_cancel_event(sc);
1072                 if (wipe)
1073                         sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE;
1074                 error = g_mirror_destroy(sc, how);
1075                 if (error != 0) {
1076                         gctl_error(req, "Cannot destroy device %s (error=%d).",
1077                             sc->sc_geom->name, error);
1078                         if (wipe)
1079                                 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE;
1080                         sx_xunlock(&sc->sc_lock);
1081                         return;
1082                 }
1083                 /* No need to unlock, because lock is already dead. */
1084         }
1085 }
1086
1087 void
1088 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1089 {
1090         uint32_t *version;
1091
1092         g_topology_assert();
1093
1094         version = gctl_get_paraml(req, "version", sizeof(*version));
1095         if (version == NULL) {
1096                 gctl_error(req, "No '%s' argument.", "version");
1097                 return;
1098         }
1099         if (*version != G_MIRROR_VERSION) {
1100                 gctl_error(req, "Userland and kernel parts are out of sync.");
1101                 return;
1102         }
1103
1104         g_topology_unlock();
1105         if (strcmp(verb, "configure") == 0)
1106                 g_mirror_ctl_configure(req, mp);
1107         else if (strcmp(verb, "create") == 0)
1108                 g_mirror_ctl_create(req, mp);
1109         else if (strcmp(verb, "rebuild") == 0)
1110                 g_mirror_ctl_rebuild(req, mp);
1111         else if (strcmp(verb, "insert") == 0)
1112                 g_mirror_ctl_insert(req, mp);
1113         else if (strcmp(verb, "remove") == 0)
1114                 g_mirror_ctl_remove(req, mp);
1115         else if (strcmp(verb, "resize") == 0)
1116                 g_mirror_ctl_resize(req, mp);
1117         else if (strcmp(verb, "deactivate") == 0)
1118                 g_mirror_ctl_deactivate(req, mp);
1119         else if (strcmp(verb, "forget") == 0)
1120                 g_mirror_ctl_forget(req, mp);
1121         else if (strcmp(verb, "stop") == 0)
1122                 g_mirror_ctl_stop(req, mp, 0);
1123         else if (strcmp(verb, "destroy") == 0)
1124                 g_mirror_ctl_stop(req, mp, 1);
1125         else
1126                 gctl_error(req, "Unknown verb.");
1127         g_topology_lock();
1128 }