The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/geom/mirror/g_mirror_ctl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 | M_ZERO);
  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 }

Cache object: 971b9a6cbefb9486c8279d03ce69eb98


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.