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/vinum/geom_vinum_subr.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-4-Clause
    3  *
    4  * Copyright (c) 2004, 2007 Lukas Ertl
    5  * Copyright (c) 2007, 2009 Ulf Lilleengen
    6  * Copyright (c) 1997, 1998, 1999
    7  *      Nan Yang Computer Services Limited.  All rights reserved.
    8  *
    9  *  Parts written by Greg Lehey
   10  *
   11  *  This software is distributed under the so-called ``Berkeley
   12  *  License'':
   13  *
   14  * Redistribution and use in source and binary forms, with or without
   15  * modification, are permitted provided that the following conditions
   16  * are met:
   17  * 1. Redistributions of source code must retain the above copyright
   18  *    notice, this list of conditions and the following disclaimer.
   19  * 2. Redistributions in binary form must reproduce the above copyright
   20  *    notice, this list of conditions and the following disclaimer in the
   21  *    documentation and/or other materials provided with the distribution.
   22  * 3. All advertising materials mentioning features or use of this software
   23  *    must display the following acknowledgement:
   24  *      This product includes software developed by Nan Yang Computer
   25  *      Services Limited.
   26  * 4. Neither the name of the Company nor the names of its contributors
   27  *    may be used to endorse or promote products derived from this software
   28  *    without specific prior written permission.
   29  *
   30  * This software is provided ``as is'', and any express or implied
   31  * warranties, including, but not limited to, the implied warranties of
   32  * merchantability and fitness for a particular purpose are disclaimed.
   33  * In no event shall the company or contributors be liable for any
   34  * direct, indirect, incidental, special, exemplary, or consequential
   35  * damages (including, but not limited to, procurement of substitute
   36  * goods or services; loss of use, data, or profits; or business
   37  * interruption) however caused and on any theory of liability, whether
   38  * in contract, strict liability, or tort (including negligence or
   39  * otherwise) arising in any way out of the use of this software, even if
   40  * advised of the possibility of such damage.
   41  *
   42  */
   43 
   44 #include <sys/cdefs.h>
   45 __FBSDID("$FreeBSD$");
   46 
   47 #include <sys/param.h>
   48 #include <sys/malloc.h>
   49 #include <sys/sbuf.h>
   50 #include <sys/systm.h>
   51 
   52 #include <geom/geom.h>
   53 #include <geom/geom_dbg.h>
   54 #include <geom/vinum/geom_vinum_var.h>
   55 #include <geom/vinum/geom_vinum.h>
   56 #include <geom/vinum/geom_vinum_share.h>
   57 
   58 int     gv_drive_is_newer(struct gv_softc *, struct gv_drive *);
   59 static off_t gv_plex_smallest_sd(struct gv_plex *);
   60 
   61 void
   62 gv_parse_config(struct gv_softc *sc, char *buf, struct gv_drive *d)
   63 {
   64         char *aptr, *bptr, *cptr;
   65         struct gv_volume *v, *v2;
   66         struct gv_plex *p, *p2;
   67         struct gv_sd *s, *s2;
   68         int error, is_newer, tokens;
   69         char *token[GV_MAXARGS];
   70 
   71         is_newer = gv_drive_is_newer(sc, d);
   72 
   73         /* Until the end of the string *buf. */
   74         for (aptr = buf; *aptr != '\0'; aptr = bptr) {
   75                 bptr = aptr;
   76                 cptr = aptr;
   77 
   78                 /* Separate input lines. */
   79                 while (*bptr != '\n')
   80                         bptr++;
   81                 *bptr = '\0';
   82                 bptr++;
   83 
   84                 tokens = gv_tokenize(cptr, token, GV_MAXARGS);
   85 
   86                 if (tokens <= 0)
   87                         continue;
   88 
   89                 if (!strcmp(token[0], "volume")) {
   90                         v = gv_new_volume(tokens, token);
   91                         if (v == NULL) {
   92                                 G_VINUM_DEBUG(0, "config parse failed volume");
   93                                 break;
   94                         }
   95 
   96                         v2 = gv_find_vol(sc, v->name);
   97                         if (v2 != NULL) {
   98                                 if (is_newer) {
   99                                         v2->state = v->state;
  100                                         G_VINUM_DEBUG(2, "newer volume found!");
  101                                 }
  102                                 g_free(v);
  103                                 continue;
  104                         }
  105 
  106                         gv_create_volume(sc, v);
  107 
  108                 } else if (!strcmp(token[0], "plex")) {
  109                         p = gv_new_plex(tokens, token);
  110                         if (p == NULL) {
  111                                 G_VINUM_DEBUG(0, "config parse failed plex");
  112                                 break;
  113                         }
  114 
  115                         p2 = gv_find_plex(sc, p->name);
  116                         if (p2 != NULL) {
  117                                 /* XXX */
  118                                 if (is_newer) {
  119                                         p2->state = p->state;
  120                                         G_VINUM_DEBUG(2, "newer plex found!");
  121                                 }
  122                                 g_free(p);
  123                                 continue;
  124                         }
  125 
  126                         error = gv_create_plex(sc, p);
  127                         if (error)
  128                                 continue;
  129                         /*
  130                          * These flags were set in gv_create_plex() and are not
  131                          * needed here (on-disk config parsing).
  132                          */
  133                         p->flags &= ~GV_PLEX_ADDED;
  134 
  135                 } else if (!strcmp(token[0], "sd")) {
  136                         s = gv_new_sd(tokens, token);
  137 
  138                         if (s == NULL) {
  139                                 G_VINUM_DEBUG(0, "config parse failed subdisk");
  140                                 break;
  141                         }
  142 
  143                         s2 = gv_find_sd(sc, s->name);
  144                         if (s2 != NULL) {
  145                                 /* XXX */
  146                                 if (is_newer) {
  147                                         s2->state = s->state;
  148                                         G_VINUM_DEBUG(2, "newer subdisk found!");
  149                                 }
  150                                 g_free(s);
  151                                 continue;
  152                         }
  153 
  154                         /*
  155                          * Signal that this subdisk was tasted, and could
  156                          * possibly reference a drive that isn't in our config
  157                          * yet.
  158                          */
  159                         s->flags |= GV_SD_TASTED;
  160 
  161                         if (s->state == GV_SD_UP)
  162                                 s->flags |= GV_SD_CANGOUP;
  163 
  164                         error = gv_create_sd(sc, s);
  165                         if (error)
  166                                 continue;
  167 
  168                         /*
  169                          * This flag was set in gv_create_sd() and is not
  170                          * needed here (on-disk config parsing).
  171                          */
  172                         s->flags &= ~GV_SD_NEWBORN;
  173                         s->flags &= ~GV_SD_GROW;
  174                 }
  175         }
  176 }
  177 
  178 /*
  179  * Format the vinum configuration properly.  If ondisk is non-zero then the
  180  * configuration is intended to be written to disk later.
  181  */
  182 void
  183 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
  184 {
  185         struct gv_drive *d;
  186         struct gv_sd *s;
  187         struct gv_plex *p;
  188         struct gv_volume *v;
  189 
  190         /*
  191          * We don't need the drive configuration if we're not writing the
  192          * config to disk.
  193          */
  194         if (!ondisk) {
  195                 LIST_FOREACH(d, &sc->drives, drive) {
  196                         sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
  197                             d->name, d->device);
  198                 }
  199         }
  200 
  201         LIST_FOREACH(v, &sc->volumes, volume) {
  202                 if (!ondisk)
  203                         sbuf_printf(sb, "%s", prefix);
  204                 sbuf_printf(sb, "volume %s", v->name);
  205                 if (ondisk)
  206                         sbuf_printf(sb, " state %s", gv_volstate(v->state));
  207                 sbuf_printf(sb, "\n");
  208         }
  209 
  210         LIST_FOREACH(p, &sc->plexes, plex) {
  211                 if (!ondisk)
  212                         sbuf_printf(sb, "%s", prefix);
  213                 sbuf_printf(sb, "plex name %s org %s ", p->name,
  214                     gv_plexorg(p->org));
  215                 if (gv_is_striped(p))
  216                         sbuf_printf(sb, "%ds ", p->stripesize / 512);
  217                 if (p->vol_sc != NULL)
  218                         sbuf_printf(sb, "vol %s", p->volume);
  219                 if (ondisk)
  220                         sbuf_printf(sb, " state %s", gv_plexstate(p->state));
  221                 sbuf_printf(sb, "\n");
  222         }
  223 
  224         LIST_FOREACH(s, &sc->subdisks, sd) {
  225                 if (!ondisk)
  226                         sbuf_printf(sb, "%s", prefix);
  227                 sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
  228                     "%jds", s->name, s->drive, s->size / 512,
  229                     s->drive_offset / 512);
  230                 if (s->plex_sc != NULL) {
  231                         sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
  232                             s->plex_offset / 512);
  233                 }
  234                 if (ondisk)
  235                         sbuf_printf(sb, " state %s", gv_sdstate(s->state));
  236                 sbuf_printf(sb, "\n");
  237         }
  238 }
  239 
  240 static off_t
  241 gv_plex_smallest_sd(struct gv_plex *p)
  242 {
  243         struct gv_sd *s;
  244         off_t smallest;
  245 
  246         KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p"));
  247 
  248         s = LIST_FIRST(&p->subdisks);
  249         if (s == NULL)
  250                 return (-1);
  251         smallest = s->size;
  252         LIST_FOREACH(s, &p->subdisks, in_plex) {
  253                 if (s->size < smallest)
  254                         smallest = s->size;
  255         }
  256         return (smallest);
  257 }
  258 
  259 /* Walk over plexes in a volume and count how many are down. */
  260 int
  261 gv_plexdown(struct gv_volume *v)
  262 {
  263         int plexdown;
  264         struct gv_plex *p;
  265 
  266         KASSERT(v != NULL, ("gv_plexdown: NULL v"));
  267 
  268         plexdown = 0;
  269 
  270         LIST_FOREACH(p, &v->plexes, plex) {
  271                 if (p->state == GV_PLEX_DOWN)
  272                         plexdown++;
  273         }
  274         return (plexdown);
  275 }
  276 
  277 int
  278 gv_sd_to_plex(struct gv_sd *s, struct gv_plex *p)
  279 {
  280         struct gv_sd *s2;
  281         off_t psizeorig, remainder, smallest;
  282 
  283         /* If this subdisk was already given to this plex, do nothing. */
  284         if (s->plex_sc == p)
  285                 return (0);
  286 
  287         /* Check correct size of this subdisk. */
  288         s2 = LIST_FIRST(&p->subdisks);
  289         /* Adjust the subdisk-size if necessary. */
  290         if (s2 != NULL && gv_is_striped(p)) {
  291                 /* First adjust to the stripesize. */
  292                 remainder = s->size % p->stripesize;
  293 
  294                 if (remainder) {
  295                         G_VINUM_DEBUG(1, "size of sd %s is not a "
  296                             "multiple of plex stripesize, taking off "
  297                             "%jd bytes", s->name,
  298                             (intmax_t)remainder);
  299                         gv_adjust_freespace(s, remainder);
  300                 }
  301 
  302                 smallest = gv_plex_smallest_sd(p);
  303                 /* Then take off extra if other subdisks are smaller. */
  304                 remainder = s->size - smallest;
  305 
  306                 /*
  307                  * Don't allow a remainder below zero for running plexes, it's too
  308                  * painful, and if someone were to accidentally do this, the
  309                  * resulting array might be smaller than the original... not god 
  310                  */
  311                 if (remainder < 0) {
  312                         if (!(p->flags & GV_PLEX_NEWBORN)) {
  313                                 G_VINUM_DEBUG(0, "sd %s too small for plex %s!",
  314                                     s->name, p->name);
  315                                 return (GV_ERR_BADSIZE);
  316                         }
  317                         /* Adjust other subdisks. */
  318                         LIST_FOREACH(s2, &p->subdisks, in_plex) {
  319                                 G_VINUM_DEBUG(1, "size of sd %s is to big, "
  320                                     "taking off %jd bytes", s->name,
  321                                     (intmax_t)remainder);
  322                                 gv_adjust_freespace(s2, (remainder * -1));
  323                         }
  324                 } else if (remainder > 0) {
  325                         G_VINUM_DEBUG(1, "size of sd %s is to big, "
  326                             "taking off %jd bytes", s->name,
  327                             (intmax_t)remainder);
  328                         gv_adjust_freespace(s, remainder);
  329                 }
  330         }
  331 
  332         /* Find the correct plex offset for this subdisk, if needed. */
  333         if (s->plex_offset == -1) {
  334                 /* 
  335                  * First set it to 0 to catch the case where we had a detached
  336                  * subdisk that didn't get any good offset.
  337                  */
  338                 s->plex_offset = 0;
  339                 if (p->sdcount) {
  340                         LIST_FOREACH(s2, &p->subdisks, in_plex) {
  341                                 if (gv_is_striped(p))
  342                                         s->plex_offset = p->sdcount *
  343                                             p->stripesize;
  344                                 else
  345                                         s->plex_offset = s2->plex_offset +
  346                                             s2->size;
  347                         }
  348                 }
  349         }
  350 
  351         /* There are no subdisks for this plex yet, just insert it. */
  352         if (LIST_EMPTY(&p->subdisks)) {
  353                 LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
  354 
  355         /* Insert in correct order, depending on plex_offset. */
  356         } else {
  357                 LIST_FOREACH(s2, &p->subdisks, in_plex) {
  358                         if (s->plex_offset < s2->plex_offset) {
  359                                 LIST_INSERT_BEFORE(s2, s, in_plex);
  360                                 break;
  361                         } else if (LIST_NEXT(s2, in_plex) == NULL) {
  362                                 LIST_INSERT_AFTER(s2, s, in_plex);
  363                                 break;
  364                         }
  365                 }
  366         }
  367 
  368         s->plex_sc = p;
  369         /* Adjust the size of our plex. We check if the plex misses a subdisk,
  370          * so we don't make the plex smaller than it actually should be.
  371          */
  372         psizeorig = p->size;
  373         p->size = gv_plex_size(p);
  374         /* Make sure the size is not changed. */
  375         if (p->sddetached > 0) {
  376                 if (p->size < psizeorig) {
  377                         p->size = psizeorig;
  378                         /* We make sure wee need another subdisk. */
  379                         if (p->sddetached == 1)
  380                                 p->sddetached++;
  381                 }
  382                 p->sddetached--;
  383         } else {
  384                 if ((p->org == GV_PLEX_RAID5 ||
  385                     p->org == GV_PLEX_STRIPED) &&
  386                     !(p->flags & GV_PLEX_NEWBORN) && 
  387                     p->state == GV_PLEX_UP) {
  388                         s->flags |= GV_SD_GROW;
  389                 }
  390                 p->sdcount++;
  391         }
  392 
  393         return (0);
  394 }
  395 
  396 void
  397 gv_update_vol_size(struct gv_volume *v, off_t size)
  398 {
  399         if (v == NULL)
  400                 return;
  401         if (v->provider != NULL) {
  402                 g_topology_lock();
  403                 v->provider->mediasize = size;
  404                 g_topology_unlock();
  405         }
  406         v->size = size;
  407 }
  408 
  409 /* Return how many subdisks that constitute the original plex. */
  410 int
  411 gv_sdcount(struct gv_plex *p, int growing)
  412 {
  413         struct gv_sd *s;
  414         int sdcount;
  415 
  416         sdcount = p->sdcount;
  417         if (growing) {
  418                 LIST_FOREACH(s, &p->subdisks, in_plex) {
  419                         if (s->flags & GV_SD_GROW)
  420                                 sdcount--;
  421                 }
  422         }
  423 
  424         return (sdcount);
  425 }
  426 
  427 /* Calculates the plex size. */
  428 off_t
  429 gv_plex_size(struct gv_plex *p)
  430 {
  431         struct gv_sd *s;
  432         off_t size;
  433         int sdcount;
  434 
  435         KASSERT(p != NULL, ("gv_plex_size: NULL p"));
  436 
  437         /* Adjust the size of our plex. */
  438         size = 0;
  439         sdcount = gv_sdcount(p, 1);
  440         switch (p->org) {
  441         case GV_PLEX_CONCAT:
  442                 LIST_FOREACH(s, &p->subdisks, in_plex)
  443                         size += s->size;
  444                 break;
  445         case GV_PLEX_STRIPED:
  446                 s = LIST_FIRST(&p->subdisks);
  447                 size = ((s != NULL) ? (sdcount * s->size) : 0);
  448                 break;
  449         case GV_PLEX_RAID5:
  450                 s = LIST_FIRST(&p->subdisks);
  451                 size = ((s != NULL) ? ((sdcount - 1) * s->size) : 0);
  452                 break;
  453         }
  454 
  455         return (size);
  456 }
  457 
  458 /* Returns the size of a volume. */
  459 off_t
  460 gv_vol_size(struct gv_volume *v)
  461 {
  462         struct gv_plex *p;
  463         off_t minplexsize;
  464 
  465         KASSERT(v != NULL, ("gv_vol_size: NULL v"));
  466 
  467         p = LIST_FIRST(&v->plexes);
  468         if (p == NULL)
  469                 return (0);
  470 
  471         minplexsize = p->size;
  472         LIST_FOREACH(p, &v->plexes, in_volume) {
  473                 if (p->size < minplexsize) {
  474                         minplexsize = p->size;
  475                 }
  476         }
  477         return (minplexsize);
  478 }
  479 
  480 void
  481 gv_update_plex_config(struct gv_plex *p)
  482 {
  483         struct gv_sd *s, *s2;
  484         off_t remainder;
  485         int required_sds, state;
  486 
  487         KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
  488 
  489         /* The plex was added to an already running volume. */
  490         if (p->flags & GV_PLEX_ADDED)
  491                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
  492 
  493         switch (p->org) {
  494         case GV_PLEX_STRIPED:
  495                 required_sds = 2;
  496                 break;
  497         case GV_PLEX_RAID5:
  498                 required_sds = 3;
  499                 break;
  500         case GV_PLEX_CONCAT:
  501         default:
  502                 required_sds = 0;
  503                 break;
  504         }
  505 
  506         if (required_sds) {
  507                 if (p->sdcount < required_sds) {
  508                         gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
  509                 }
  510 
  511                 /*
  512                  * The subdisks in striped plexes must all have the same size.
  513                  */
  514                 s = LIST_FIRST(&p->subdisks);
  515                 LIST_FOREACH(s2, &p->subdisks, in_plex) {
  516                         if (s->size != s2->size) {
  517                                 G_VINUM_DEBUG(0, "subdisk size mismatch %s"
  518                                     "(%jd) <> %s (%jd)", s->name, s->size,
  519                                     s2->name, s2->size);
  520                                 gv_set_plex_state(p, GV_PLEX_DOWN,
  521                                     GV_SETSTATE_FORCE);
  522                         }
  523                 }
  524 
  525                 LIST_FOREACH(s, &p->subdisks, in_plex) {
  526                         /* Trim subdisk sizes to match the stripe size. */
  527                         remainder = s->size % p->stripesize;
  528                         if (remainder) {
  529                                 G_VINUM_DEBUG(1, "size of sd %s is not a "
  530                                     "multiple of plex stripesize, taking off "
  531                                     "%jd bytes", s->name, (intmax_t)remainder);
  532                                 gv_adjust_freespace(s, remainder);
  533                         }
  534                 }
  535         }
  536 
  537         p->size = gv_plex_size(p);
  538         if (p->sdcount == 0)
  539                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
  540         else if (p->org == GV_PLEX_RAID5 && p->flags & GV_PLEX_NEWBORN) {
  541                 LIST_FOREACH(s, &p->subdisks, in_plex)
  542                         gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_FORCE);
  543                 /* If added to a volume, we want the plex to be down. */
  544                 state = (p->flags & GV_PLEX_ADDED) ? GV_PLEX_DOWN : GV_PLEX_UP;
  545                 gv_set_plex_state(p, state, GV_SETSTATE_FORCE);
  546                 p->flags &= ~GV_PLEX_ADDED;
  547         } else if (p->flags & GV_PLEX_ADDED) {
  548                 LIST_FOREACH(s, &p->subdisks, in_plex)
  549                         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
  550                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
  551                 p->flags &= ~GV_PLEX_ADDED;
  552         } else if (p->state == GV_PLEX_UP) {
  553                 LIST_FOREACH(s, &p->subdisks, in_plex) {
  554                         if (s->flags & GV_SD_GROW) {
  555                                 gv_set_plex_state(p, GV_PLEX_GROWABLE,
  556                                     GV_SETSTATE_FORCE);
  557                                 break;
  558                         }
  559                 }
  560         }
  561         /* Our plex is grown up now. */
  562         p->flags &= ~GV_PLEX_NEWBORN;
  563 }
  564 
  565 /*
  566  * Give a subdisk to a drive, check and adjust several parameters, adjust
  567  * freelist.
  568  */
  569 int
  570 gv_sd_to_drive(struct gv_sd *s, struct gv_drive *d)
  571 {
  572         struct gv_sd *s2;
  573         struct gv_freelist *fl, *fl2;
  574         off_t tmp;
  575         int i;
  576 
  577         fl2 = NULL;
  578 
  579         /* Shortcut for "referenced" drives. */
  580         if (d->flags & GV_DRIVE_REFERENCED) {
  581                 s->drive_sc = d;
  582                 return (0);
  583         }
  584 
  585         /* Check if this subdisk was already given to this drive. */
  586         if (s->drive_sc != NULL) {
  587                 if (s->drive_sc == d) {
  588                         if (!(s->flags & GV_SD_TASTED)) {
  589                                 return (0);
  590                         }
  591                 } else {
  592                         G_VINUM_DEBUG(0, "error giving subdisk '%s' to '%s' "
  593                             "(already on '%s')", s->name, d->name,
  594                             s->drive_sc->name);
  595                         return (GV_ERR_ISATTACHED);
  596                 }
  597         }
  598 
  599         /* Preliminary checks. */
  600         if ((s->size > d->avail) || (d->freelist_entries == 0)) {
  601                 G_VINUM_DEBUG(0, "not enough space on '%s' for '%s'", d->name,
  602                     s->name);
  603                 return (GV_ERR_NOSPACE);
  604         }
  605 
  606         /* If no size was given for this subdisk, try to auto-size it... */
  607         if (s->size == -1) {
  608                 /* Find the largest available slot. */
  609                 LIST_FOREACH(fl, &d->freelist, freelist) {
  610                         if (fl->size < s->size)
  611                                 continue;
  612                         s->size = fl->size;
  613                         s->drive_offset = fl->offset;
  614                         fl2 = fl;
  615                 }
  616 
  617                 /* No good slot found? */
  618                 if (s->size == -1) {
  619                         G_VINUM_DEBUG(0, "unable to autosize '%s' on '%s'",
  620                             s->name, d->name);
  621                         return (GV_ERR_BADSIZE);
  622                 }
  623 
  624         /*
  625          * ... or check if we have a free slot that's large enough for the
  626          * given size.
  627          */
  628         } else {
  629                 i = 0;
  630                 LIST_FOREACH(fl, &d->freelist, freelist) {
  631                         if (fl->size < s->size)
  632                                 continue;
  633                         /* Assign drive offset, if not given. */
  634                         if (s->drive_offset == -1)
  635                                 s->drive_offset = fl->offset;
  636                         fl2 = fl;
  637                         i++;
  638                         break;
  639                 }
  640 
  641                 /* Couldn't find a good free slot. */
  642                 if (i == 0) {
  643                         G_VINUM_DEBUG(0, "free slots to small for '%s' on '%s'",
  644                             s->name, d->name);
  645                         return (GV_ERR_NOSPACE);
  646                 }
  647         }
  648 
  649         /* No drive offset given, try to calculate it. */
  650         if (s->drive_offset == -1) {
  651                 /* Add offsets and sizes from other subdisks on this drive. */
  652                 LIST_FOREACH(s2, &d->subdisks, from_drive) {
  653                         s->drive_offset = s2->drive_offset + s2->size;
  654                 }
  655 
  656                 /*
  657                  * If there are no other subdisks yet, then set the default
  658                  * offset to GV_DATA_START.
  659                  */
  660                 if (s->drive_offset == -1)
  661                         s->drive_offset = GV_DATA_START;
  662 
  663         /* Check if we have a free slot at the given drive offset. */
  664         } else {
  665                 i = 0;
  666                 LIST_FOREACH(fl, &d->freelist, freelist) {
  667                         /* Yes, this subdisk fits. */
  668                         if ((fl->offset <= s->drive_offset) &&
  669                             (fl->offset + fl->size >=
  670                             s->drive_offset + s->size)) {
  671                                 i++;
  672                                 fl2 = fl;
  673                                 break;
  674                         }
  675                 }
  676 
  677                 /* Couldn't find a good free slot. */
  678                 if (i == 0) {
  679                         G_VINUM_DEBUG(0, "given drive_offset for '%s' won't fit "
  680                             "on '%s'", s->name, d->name);
  681                         return (GV_ERR_NOSPACE);
  682                 }
  683         }
  684 
  685         /*
  686          * Now that all parameters are checked and set up, we can give the
  687          * subdisk to the drive and adjust the freelist.
  688          */
  689 
  690         /* First, adjust the freelist. */
  691         LIST_FOREACH(fl, &d->freelist, freelist) {
  692                 /* Look for the free slot that we have found before. */
  693                 if (fl != fl2)
  694                         continue;
  695 
  696                 /* The subdisk starts at the beginning of the free slot. */
  697                 if (fl->offset == s->drive_offset) {
  698                         fl->offset += s->size;
  699                         fl->size -= s->size;
  700 
  701                         /* The subdisk uses the whole slot, so remove it. */
  702                         if (fl->size == 0) {
  703                                 d->freelist_entries--;
  704                                 LIST_REMOVE(fl, freelist);
  705                         }
  706                 /*
  707                  * The subdisk does not start at the beginning of the free
  708                  * slot.
  709                  */
  710                 } else {
  711                         tmp = fl->offset + fl->size;
  712                         fl->size = s->drive_offset - fl->offset;
  713 
  714                         /*
  715                          * The subdisk didn't use the complete rest of the free
  716                          * slot, so we need to split it.
  717                          */
  718                         if (s->drive_offset + s->size != tmp) {
  719                                 fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO);
  720                                 fl2->offset = s->drive_offset + s->size;
  721                                 fl2->size = tmp - fl2->offset;
  722                                 LIST_INSERT_AFTER(fl, fl2, freelist);
  723                                 d->freelist_entries++;
  724                         }
  725                 }
  726                 break;
  727         }
  728 
  729         /*
  730          * This is the first subdisk on this drive, just insert it into the
  731          * list.
  732          */
  733         if (LIST_EMPTY(&d->subdisks)) {
  734                 LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
  735 
  736         /* There are other subdisks, so insert this one in correct order. */
  737         } else {
  738                 LIST_FOREACH(s2, &d->subdisks, from_drive) {
  739                         if (s->drive_offset < s2->drive_offset) {
  740                                 LIST_INSERT_BEFORE(s2, s, from_drive);
  741                                 break;
  742                         } else if (LIST_NEXT(s2, from_drive) == NULL) {
  743                                 LIST_INSERT_AFTER(s2, s, from_drive);
  744                                 break;
  745                         }
  746                 }
  747         }
  748 
  749         d->sdcount++;
  750         d->avail -= s->size;
  751 
  752         s->flags &= ~GV_SD_TASTED;
  753 
  754         /* Link back from the subdisk to this drive. */
  755         s->drive_sc = d;
  756 
  757         return (0);
  758 }
  759 
  760 void
  761 gv_free_sd(struct gv_sd *s)
  762 {
  763         struct gv_drive *d;
  764         struct gv_freelist *fl, *fl2;
  765 
  766         KASSERT(s != NULL, ("gv_free_sd: NULL s"));
  767 
  768         d = s->drive_sc;
  769         if (d == NULL)
  770                 return;
  771 
  772         /*
  773          * First, find the free slot that's immediately before or after this
  774          * subdisk.
  775          */
  776         fl = NULL;
  777         LIST_FOREACH(fl, &d->freelist, freelist) {
  778                 if (fl->offset == s->drive_offset + s->size)
  779                         break;
  780                 if (fl->offset + fl->size == s->drive_offset)
  781                         break;
  782         }
  783 
  784         /* If there is no free slot behind this subdisk, so create one. */
  785         if (fl == NULL) {
  786                 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
  787                 fl->size = s->size;
  788                 fl->offset = s->drive_offset;
  789 
  790                 if (d->freelist_entries == 0) {
  791                         LIST_INSERT_HEAD(&d->freelist, fl, freelist);
  792                 } else {
  793                         LIST_FOREACH(fl2, &d->freelist, freelist) {
  794                                 if (fl->offset < fl2->offset) {
  795                                         LIST_INSERT_BEFORE(fl2, fl, freelist);
  796                                         break;
  797                                 } else if (LIST_NEXT(fl2, freelist) == NULL) {
  798                                         LIST_INSERT_AFTER(fl2, fl, freelist);
  799                                         break;
  800                                 }
  801                         }
  802                 }
  803 
  804                 d->freelist_entries++;
  805 
  806         /* Expand the free slot we just found. */
  807         } else {
  808                 fl->size += s->size;
  809                 if (fl->offset > s->drive_offset)
  810                         fl->offset = s->drive_offset;
  811         }
  812 
  813         d->avail += s->size;
  814         d->sdcount--;
  815 }
  816 
  817 void
  818 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
  819 {
  820         struct gv_drive *d;
  821         struct gv_freelist *fl, *fl2;
  822 
  823         KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
  824         d = s->drive_sc;
  825         KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
  826 
  827         /* First, find the free slot that's immediately after this subdisk. */
  828         fl = NULL;
  829         LIST_FOREACH(fl, &d->freelist, freelist) {
  830                 if (fl->offset == s->drive_offset + s->size)
  831                         break;
  832         }
  833 
  834         /* If there is no free slot behind this subdisk, so create one. */
  835         if (fl == NULL) {
  836                 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
  837                 fl->size = remainder;
  838                 fl->offset = s->drive_offset + s->size - remainder;
  839 
  840                 if (d->freelist_entries == 0) {
  841                         LIST_INSERT_HEAD(&d->freelist, fl, freelist);
  842                 } else {
  843                         LIST_FOREACH(fl2, &d->freelist, freelist) {
  844                                 if (fl->offset < fl2->offset) {
  845                                         LIST_INSERT_BEFORE(fl2, fl, freelist);
  846                                         break;
  847                                 } else if (LIST_NEXT(fl2, freelist) == NULL) {
  848                                         LIST_INSERT_AFTER(fl2, fl, freelist);
  849                                         break;
  850                                 }
  851                         }
  852                 }
  853 
  854                 d->freelist_entries++;
  855 
  856         /* Expand the free slot we just found. */
  857         } else {
  858                 fl->offset -= remainder;
  859                 fl->size += remainder;
  860         }
  861 
  862         s->size -= remainder;
  863         d->avail += remainder;
  864 }
  865 
  866 /* Check if the given plex is a striped one. */
  867 int
  868 gv_is_striped(struct gv_plex *p)
  869 {
  870         KASSERT(p != NULL, ("gv_is_striped: NULL p"));
  871         switch(p->org) {
  872         case GV_PLEX_STRIPED:
  873         case GV_PLEX_RAID5:
  874                 return (1);
  875         default:
  876                 return (0);
  877         }
  878 }
  879 
  880 /* Find a volume by name. */
  881 struct gv_volume *
  882 gv_find_vol(struct gv_softc *sc, char *name)
  883 {
  884         struct gv_volume *v;
  885 
  886         LIST_FOREACH(v, &sc->volumes, volume) {
  887                 if (!strncmp(v->name, name, GV_MAXVOLNAME))
  888                         return (v);
  889         }
  890 
  891         return (NULL);
  892 }
  893 
  894 /* Find a plex by name. */
  895 struct gv_plex *
  896 gv_find_plex(struct gv_softc *sc, char *name)
  897 {
  898         struct gv_plex *p;
  899 
  900         LIST_FOREACH(p, &sc->plexes, plex) {
  901                 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
  902                         return (p);
  903         }
  904 
  905         return (NULL);
  906 }
  907 
  908 /* Find a subdisk by name. */
  909 struct gv_sd *
  910 gv_find_sd(struct gv_softc *sc, char *name)
  911 {
  912         struct gv_sd *s;
  913 
  914         LIST_FOREACH(s, &sc->subdisks, sd) {
  915                 if (!strncmp(s->name, name, GV_MAXSDNAME))
  916                         return (s);
  917         }
  918 
  919         return (NULL);
  920 }
  921 
  922 /* Find a drive by name. */
  923 struct gv_drive *
  924 gv_find_drive(struct gv_softc *sc, char *name)
  925 {
  926         struct gv_drive *d;
  927 
  928         LIST_FOREACH(d, &sc->drives, drive) {
  929                 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
  930                         return (d);
  931         }
  932 
  933         return (NULL);
  934 }
  935 
  936 /* Find a drive given a device. */
  937 struct gv_drive *
  938 gv_find_drive_device(struct gv_softc *sc, char *device)
  939 {
  940         struct gv_drive *d;
  941 
  942         LIST_FOREACH(d, &sc->drives, drive) {
  943                 if(!strcmp(d->device, device))
  944                         return (d);
  945         }
  946 
  947         return (NULL);
  948 }
  949 
  950 /* Check if any consumer of the given geom is open. */
  951 int
  952 gv_consumer_is_open(struct g_consumer *cp)
  953 {
  954         if (cp == NULL)
  955                 return (0);
  956 
  957         if (cp->acr || cp->acw || cp->ace)
  958                 return (1);
  959 
  960         return (0);
  961 }
  962 
  963 int
  964 gv_provider_is_open(struct g_provider *pp)
  965 {
  966         if (pp == NULL)
  967                 return (0);
  968 
  969         if (pp->acr || pp->acw || pp->ace)
  970                 return (1);
  971 
  972         return (0);
  973 }
  974 
  975 /*
  976  * Compare the modification dates of the drives.
  977  * Return 1 if a > b, 0 otherwise.
  978  */
  979 int
  980 gv_drive_is_newer(struct gv_softc *sc, struct gv_drive *d)
  981 {
  982         struct gv_drive *d2;
  983         struct timeval *a, *b;
  984 
  985         KASSERT(!LIST_EMPTY(&sc->drives),
  986             ("gv_is_drive_newer: empty drive list"));
  987 
  988         a = &d->hdr->label.last_update;
  989         LIST_FOREACH(d2, &sc->drives, drive) {
  990                 if ((d == d2) || (d2->state != GV_DRIVE_UP) ||
  991                     (d2->hdr == NULL))
  992                         continue;
  993                 b = &d2->hdr->label.last_update;
  994                 if (timevalcmp(a, b, >))
  995                         return (1);
  996         }
  997 
  998         return (0);
  999 }
 1000 
 1001 /* Return the type of object identified by string 'name'. */
 1002 int
 1003 gv_object_type(struct gv_softc *sc, char *name)
 1004 {
 1005         struct gv_drive *d;
 1006         struct gv_plex *p;
 1007         struct gv_sd *s;
 1008         struct gv_volume *v;
 1009 
 1010         LIST_FOREACH(v, &sc->volumes, volume) {
 1011                 if (!strncmp(v->name, name, GV_MAXVOLNAME))
 1012                         return (GV_TYPE_VOL);
 1013         }
 1014 
 1015         LIST_FOREACH(p, &sc->plexes, plex) {
 1016                 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
 1017                         return (GV_TYPE_PLEX);
 1018         }
 1019 
 1020         LIST_FOREACH(s, &sc->subdisks, sd) {
 1021                 if (!strncmp(s->name, name, GV_MAXSDNAME))
 1022                         return (GV_TYPE_SD);
 1023         }
 1024 
 1025         LIST_FOREACH(d, &sc->drives, drive) {
 1026                 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
 1027                         return (GV_TYPE_DRIVE);
 1028         }
 1029 
 1030         return (GV_ERR_NOTFOUND);
 1031 }
 1032 
 1033 void
 1034 gv_setup_objects(struct gv_softc *sc)
 1035 {
 1036         struct g_provider *pp;
 1037         struct gv_volume *v;
 1038         struct gv_plex *p;
 1039         struct gv_sd *s;
 1040         struct gv_drive *d;
 1041 
 1042         LIST_FOREACH(s, &sc->subdisks, sd) {
 1043                 d = gv_find_drive(sc, s->drive);
 1044                 if (d != NULL)
 1045                         gv_sd_to_drive(s, d);
 1046                 p = gv_find_plex(sc, s->plex);
 1047                 if (p != NULL)
 1048                         gv_sd_to_plex(s, p);
 1049                 gv_update_sd_state(s);
 1050         }
 1051 
 1052         LIST_FOREACH(p, &sc->plexes, plex) {
 1053                 gv_update_plex_config(p);
 1054                 v = gv_find_vol(sc, p->volume);
 1055                 if (v != NULL && p->vol_sc != v) {
 1056                         p->vol_sc = v;
 1057                         v->plexcount++;
 1058                         LIST_INSERT_HEAD(&v->plexes, p, in_volume);
 1059                 }
 1060                 gv_update_plex_config(p);
 1061         }
 1062 
 1063         LIST_FOREACH(v, &sc->volumes, volume) {
 1064                 v->size = gv_vol_size(v);
 1065                 if (v->provider == NULL) {
 1066                         g_topology_lock();
 1067                         pp = g_new_providerf(sc->geom, "gvinum/%s", v->name);
 1068                         pp->mediasize = v->size;
 1069                         pp->sectorsize = 512;    /* XXX */
 1070                         g_error_provider(pp, 0);
 1071                         v->provider = pp;
 1072                         pp->private = v;
 1073                         g_topology_unlock();
 1074                 } else if (v->provider->mediasize != v->size) {
 1075                         g_topology_lock();
 1076                         v->provider->mediasize = v->size;
 1077                         g_topology_unlock();
 1078                 }
 1079                 v->flags &= ~GV_VOL_NEWBORN;
 1080                 gv_update_vol_state(v);
 1081         }
 1082 }
 1083 
 1084 void
 1085 gv_cleanup(struct gv_softc *sc)
 1086 {
 1087         struct gv_volume *v, *v2;
 1088         struct gv_plex *p, *p2;
 1089         struct gv_sd *s, *s2;
 1090         struct gv_drive *d, *d2;
 1091         struct gv_freelist *fl, *fl2;
 1092 
 1093         mtx_lock(&sc->config_mtx);
 1094         LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
 1095                 LIST_REMOVE(v, volume);
 1096                 g_free(v->wqueue);
 1097                 g_free(v);
 1098         }
 1099         LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
 1100                 LIST_REMOVE(p, plex);
 1101                 g_free(p->bqueue);
 1102                 g_free(p->rqueue);
 1103                 g_free(p->wqueue);
 1104                 g_free(p);
 1105         }
 1106         LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
 1107                 LIST_REMOVE(s, sd);
 1108                 g_free(s);
 1109         }
 1110         LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
 1111                 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
 1112                         LIST_REMOVE(fl, freelist);
 1113                         g_free(fl);
 1114                 }
 1115                 LIST_REMOVE(d, drive);
 1116                 g_free(d->hdr);
 1117                 g_free(d);
 1118         }
 1119         mtx_destroy(&sc->config_mtx);
 1120 }
 1121 
 1122 /* General 'attach' routine. */
 1123 int
 1124 gv_attach_plex(struct gv_plex *p, struct gv_volume *v, int rename)
 1125 {
 1126         struct gv_sd *s;
 1127         struct gv_softc *sc __diagused;
 1128 
 1129         g_topology_assert();
 1130 
 1131         sc = p->vinumconf;
 1132         KASSERT(sc != NULL, ("NULL sc"));
 1133 
 1134         if (p->vol_sc != NULL) {
 1135                 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
 1136                     p->name, p->volume);
 1137                 return (GV_ERR_ISATTACHED);
 1138         }
 1139 
 1140         /* Stale all subdisks of this plex. */
 1141         LIST_FOREACH(s, &p->subdisks, in_plex) {
 1142                 if (s->state != GV_SD_STALE)
 1143                         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
 1144         }
 1145         /* Attach to volume. Make sure volume is not up and running. */
 1146         if (gv_provider_is_open(v->provider)) {
 1147                 G_VINUM_DEBUG(1, "unable to attach %s: volume %s is busy",
 1148                     p->name, v->name);
 1149                 return (GV_ERR_ISBUSY);
 1150         }
 1151         p->vol_sc = v;
 1152         strlcpy(p->volume, v->name, sizeof(p->volume));
 1153         v->plexcount++;
 1154         if (rename) {
 1155                 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
 1156                     v->plexcount);
 1157         }
 1158         LIST_INSERT_HEAD(&v->plexes, p, in_volume);
 1159 
 1160         /* Get plex up again. */
 1161         gv_update_vol_size(v, gv_vol_size(v));
 1162         gv_set_plex_state(p, GV_PLEX_UP, 0);
 1163         gv_save_config(p->vinumconf);
 1164         return (0);
 1165 }
 1166 
 1167 int
 1168 gv_attach_sd(struct gv_sd *s, struct gv_plex *p, off_t offset, int rename)
 1169 {
 1170         struct gv_sd *s2;
 1171         int error;
 1172 
 1173         g_topology_assert();
 1174 
 1175         /* If subdisk is attached, don't do it. */
 1176         if (s->plex_sc != NULL) {
 1177                 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
 1178                     s->name, s->plex);
 1179                 return (GV_ERR_ISATTACHED);
 1180         }
 1181 
 1182         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
 1183         /* First check that this subdisk has a correct offset. If none other
 1184          * starts at the same, and it's correct module stripesize, it is */
 1185         if (offset != -1 && offset % p->stripesize != 0)
 1186                 return (GV_ERR_BADOFFSET);
 1187         LIST_FOREACH(s2, &p->subdisks, in_plex) {
 1188                 if (s2->plex_offset == offset)
 1189                         return (GV_ERR_BADOFFSET);
 1190         }
 1191 
 1192         /* Attach the subdisk to the plex at given offset. */
 1193         s->plex_offset = offset;
 1194         strlcpy(s->plex, p->name, sizeof(s->plex));
 1195 
 1196         error = gv_sd_to_plex(s, p);
 1197         if (error)
 1198                 return (error);
 1199         gv_update_plex_config(p);
 1200 
 1201         if (rename) {
 1202                 snprintf(s->name, sizeof(s->name), "%s.s%d", s->plex,
 1203                     p->sdcount);
 1204         }
 1205         if (p->vol_sc != NULL)
 1206                 gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc));
 1207         gv_save_config(p->vinumconf);
 1208         /* We don't update the subdisk state since the user might have to
 1209          * initiate a rebuild/sync first. */
 1210         return (0);
 1211 }
 1212 
 1213 /* Detach a plex from a volume. */
 1214 int
 1215 gv_detach_plex(struct gv_plex *p, int flags)
 1216 {
 1217         struct gv_volume *v;
 1218 
 1219         g_topology_assert();
 1220         v = p->vol_sc;
 1221 
 1222         if (v == NULL) {
 1223                 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
 1224                     p->name);
 1225                 return (0); /* Not an error. */
 1226         }
 1227 
 1228         /*
 1229          * Only proceed if forced or volume inactive.
 1230          */
 1231         if (!(flags & GV_FLAG_F) && (gv_provider_is_open(v->provider) ||
 1232             p->state == GV_PLEX_UP)) {
 1233                 G_VINUM_DEBUG(1, "unable to detach %s: volume %s is busy",
 1234                     p->name, p->volume);
 1235                 return (GV_ERR_ISBUSY);
 1236         }
 1237         v->plexcount--;
 1238         /* Make sure someone don't read us when gone. */
 1239         v->last_read_plex = NULL; 
 1240         LIST_REMOVE(p, in_volume);
 1241         p->vol_sc = NULL;
 1242         memset(p->volume, 0, GV_MAXVOLNAME);
 1243         gv_update_vol_size(v, gv_vol_size(v));
 1244         gv_save_config(p->vinumconf);
 1245         return (0);
 1246 }
 1247 
 1248 /* Detach a subdisk from a plex. */
 1249 int
 1250 gv_detach_sd(struct gv_sd *s, int flags)
 1251 {
 1252         struct gv_plex *p;
 1253 
 1254         g_topology_assert();
 1255         p = s->plex_sc;
 1256 
 1257         if (p == NULL) {
 1258                 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
 1259                     s->name);
 1260                 return (0); /* Not an error. */
 1261         }
 1262 
 1263         /*
 1264          * Don't proceed if we're not forcing, and the plex is up, or degraded
 1265          * with this subdisk up.
 1266          */
 1267         if (!(flags & GV_FLAG_F) && ((p->state > GV_PLEX_DEGRADED) ||
 1268             ((p->state == GV_PLEX_DEGRADED) && (s->state == GV_SD_UP)))) {
 1269                 G_VINUM_DEBUG(1, "unable to detach %s: plex %s is busy",
 1270                     s->name, s->plex);
 1271                 return (GV_ERR_ISBUSY);
 1272         }
 1273 
 1274         LIST_REMOVE(s, in_plex);
 1275         s->plex_sc = NULL;
 1276         memset(s->plex, 0, GV_MAXPLEXNAME);
 1277         p->sddetached++;
 1278         gv_save_config(s->vinumconf);
 1279         return (0);
 1280 }

Cache object: 0a904bafedfc24ad457147257974c972


[ 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.