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/linux_lvm/g_linux_lvm.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2008 Andrew Thompson <thompsa@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/11.0/sys/geom/linux_lvm/g_linux_lvm.c 267992 2014-06-28 03:56:17Z hselasky $");
   29 
   30 #include <sys/ctype.h>
   31 #include <sys/param.h>
   32 #include <sys/bio.h>
   33 #include <sys/kernel.h>
   34 #include <sys/limits.h>
   35 #include <sys/malloc.h>
   36 #include <sys/queue.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/systm.h>
   39 
   40 #include <geom/geom.h>
   41 #include <sys/endian.h>
   42 
   43 #include <geom/linux_lvm/g_linux_lvm.h>
   44 
   45 FEATURE(geom_linux_lvm, "GEOM Linux LVM partitioning support");
   46 
   47 /* Declare malloc(9) label */
   48 static MALLOC_DEFINE(M_GLLVM, "gllvm", "GEOM_LINUX_LVM Data");
   49 
   50 /* GEOM class methods */
   51 static g_access_t g_llvm_access;
   52 static g_init_t g_llvm_init;
   53 static g_orphan_t g_llvm_orphan;
   54 static g_orphan_t g_llvm_taste_orphan;
   55 static g_start_t g_llvm_start;
   56 static g_taste_t g_llvm_taste;
   57 static g_ctl_destroy_geom_t g_llvm_destroy_geom;
   58 
   59 static void     g_llvm_done(struct bio *);
   60 static void     g_llvm_remove_disk(struct g_llvm_vg *, struct g_consumer *);
   61 static int      g_llvm_activate_lv(struct g_llvm_vg *, struct g_llvm_lv *);
   62 static int      g_llvm_add_disk(struct g_llvm_vg *, struct g_provider *, char *);
   63 static void     g_llvm_free_vg(struct g_llvm_vg *);
   64 static int      g_llvm_destroy(struct g_llvm_vg *, int);
   65 static int      g_llvm_read_label(struct g_consumer *, struct g_llvm_label *);
   66 static int      g_llvm_read_md(struct g_consumer *, struct g_llvm_metadata *,
   67                     struct g_llvm_label *);
   68 
   69 static int      llvm_label_decode(const u_char *, struct g_llvm_label *, int);
   70 static int      llvm_md_decode(const u_char *, struct g_llvm_metadata *,
   71                     struct g_llvm_label *);
   72 static int      llvm_textconf_decode(u_char *, int,
   73                     struct g_llvm_metadata *);
   74 static int      llvm_textconf_decode_pv(char **, char *, struct g_llvm_vg *);
   75 static int      llvm_textconf_decode_lv(char **, char *, struct g_llvm_vg *);
   76 static int      llvm_textconf_decode_sg(char **, char *, struct g_llvm_lv *);
   77 
   78 SYSCTL_DECL(_kern_geom);
   79 SYSCTL_NODE(_kern_geom, OID_AUTO, linux_lvm, CTLFLAG_RW, 0,
   80     "GEOM_LINUX_LVM stuff");
   81 static u_int g_llvm_debug = 0;
   82 SYSCTL_UINT(_kern_geom_linux_lvm, OID_AUTO, debug, CTLFLAG_RWTUN, &g_llvm_debug, 0,
   83     "Debug level");
   84 
   85 LIST_HEAD(, g_llvm_vg) vg_list;
   86 
   87 /*
   88  * Called to notify geom when it's been opened, and for what intent
   89  */
   90 static int
   91 g_llvm_access(struct g_provider *pp, int dr, int dw, int de)
   92 {
   93         struct g_consumer *c;
   94         struct g_llvm_vg *vg;
   95         struct g_geom *gp;
   96         int error;
   97 
   98         KASSERT(pp != NULL, ("%s: NULL provider", __func__));
   99         gp = pp->geom;
  100         KASSERT(gp != NULL, ("%s: NULL geom", __func__));
  101         vg = gp->softc;
  102 
  103         if (vg == NULL) {
  104                 /* It seems that .access can be called with negative dr,dw,dx
  105                  * in this case but I want to check for myself */
  106                 G_LLVM_DEBUG(0, "access(%d, %d, %d) for %s",
  107                     dr, dw, de, pp->name);
  108 
  109                 /* This should only happen when geom is withered so
  110                  * allow only negative requests */
  111                 KASSERT(dr <= 0 && dw <= 0 && de <= 0,
  112                     ("%s: Positive access for %s", __func__, pp->name));
  113                 if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0)
  114                         G_LLVM_DEBUG(0,
  115                             "Device %s definitely destroyed", pp->name);
  116                 return (0);
  117         }
  118 
  119         /* Grab an exclusive bit to propagate on our consumers on first open */
  120         if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
  121                 de++;
  122         /* ... drop it on close */
  123         if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0)
  124                 de--;
  125 
  126         error = ENXIO;
  127         LIST_FOREACH(c, &gp->consumer, consumer) {
  128                 KASSERT(c != NULL, ("%s: consumer is NULL", __func__));
  129                 error = g_access(c, dr, dw, de);
  130                 if (error != 0) {
  131                         struct g_consumer *c2;
  132 
  133                         /* Backout earlier changes */
  134                         LIST_FOREACH(c2, &gp->consumer, consumer) {
  135                                 if (c2 == c) /* all eariler components fixed */
  136                                         return (error);
  137                                 g_access(c2, -dr, -dw, -de);
  138                         }
  139                 }
  140         }
  141 
  142         return (error);
  143 }
  144 
  145 /*
  146  * Dismantle bio_queue and destroy its components
  147  */
  148 static void
  149 bioq_dismantle(struct bio_queue_head *bq)
  150 {
  151         struct bio *b;
  152 
  153         for (b = bioq_first(bq); b != NULL; b = bioq_first(bq)) {
  154                 bioq_remove(bq, b);
  155                 g_destroy_bio(b);
  156         }
  157 }
  158 
  159 /*
  160  * GEOM .done handler
  161  * Can't use standard handler because one requested IO may
  162  * fork into additional data IOs
  163  */
  164 static void
  165 g_llvm_done(struct bio *b)
  166 {
  167         struct bio *parent_b;
  168 
  169         parent_b = b->bio_parent;
  170 
  171         if (b->bio_error != 0) {
  172                 G_LLVM_DEBUG(0, "Error %d for offset=%ju, length=%ju on %s",
  173                     b->bio_error, b->bio_offset, b->bio_length,
  174                     b->bio_to->name);
  175                 if (parent_b->bio_error == 0)
  176                         parent_b->bio_error = b->bio_error;
  177         }
  178 
  179         parent_b->bio_inbed++;
  180         parent_b->bio_completed += b->bio_completed;
  181 
  182         if (parent_b->bio_children == parent_b->bio_inbed) {
  183                 parent_b->bio_completed = parent_b->bio_length;
  184                 g_io_deliver(parent_b, parent_b->bio_error);
  185         }
  186         g_destroy_bio(b);
  187 }
  188 
  189 static void
  190 g_llvm_start(struct bio *bp)
  191 {
  192         struct g_provider *pp;
  193         struct g_llvm_vg *vg;
  194         struct g_llvm_pv *pv;
  195         struct g_llvm_lv *lv;
  196         struct g_llvm_segment *sg;
  197         struct bio *cb;
  198         struct bio_queue_head bq;
  199         size_t chunk_size;
  200         off_t offset, length;
  201         char *addr;
  202         u_int count;
  203 
  204         pp = bp->bio_to;
  205         lv = pp->private;
  206         vg = pp->geom->softc;
  207 
  208         switch (bp->bio_cmd) {
  209         case BIO_READ:
  210         case BIO_WRITE:
  211         case BIO_DELETE:
  212         /* XXX BIO_GETATTR allowed? */
  213                 break;
  214         default:
  215                 g_io_deliver(bp, EOPNOTSUPP);
  216                 return;
  217         }
  218 
  219         bioq_init(&bq);
  220 
  221         chunk_size = vg->vg_extentsize;
  222         addr = bp->bio_data;
  223         offset = bp->bio_offset;        /* virtual offset and length */
  224         length = bp->bio_length;
  225 
  226         while (length > 0) {
  227                 size_t chunk_index, in_chunk_offset, in_chunk_length;
  228 
  229                 pv = NULL;
  230                 cb = g_clone_bio(bp);
  231                 if (cb == NULL) {
  232                         bioq_dismantle(&bq);
  233                         if (bp->bio_error == 0)
  234                                 bp->bio_error = ENOMEM;
  235                         g_io_deliver(bp, bp->bio_error);
  236                         return;
  237                 }
  238 
  239                 /* get the segment and the pv */
  240                 if (lv->lv_sgcount == 1) {
  241                         /* skip much of the calculations for a single sg */
  242                         chunk_index = 0;
  243                         in_chunk_offset = 0;
  244                         in_chunk_length = length;
  245                         sg = lv->lv_firstsg;
  246                         pv = sg->sg_pv;
  247                         cb->bio_offset = offset + sg->sg_pvoffset;
  248                 } else {
  249                         chunk_index = offset / chunk_size; /* round downwards */
  250                         in_chunk_offset = offset % chunk_size;
  251                         in_chunk_length =
  252                             min(length, chunk_size - in_chunk_offset);
  253 
  254                         /* XXX could be faster */
  255                         LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
  256                                 if (chunk_index >= sg->sg_start &&
  257                                     chunk_index <= sg->sg_end) {
  258                                         /* adjust chunk index for sg start */
  259                                         chunk_index -= sg->sg_start;
  260                                         pv = sg->sg_pv;
  261                                         break;
  262                                 }
  263                         }
  264                         cb->bio_offset =
  265                             (off_t)chunk_index * (off_t)chunk_size
  266                             + in_chunk_offset + sg->sg_pvoffset;
  267                 }
  268 
  269                 KASSERT(pv != NULL, ("Can't find PV for chunk %zu",
  270                     chunk_index));
  271 
  272                 cb->bio_to = pv->pv_gprov;
  273                 cb->bio_done = g_llvm_done;
  274                 cb->bio_length = in_chunk_length;
  275                 cb->bio_data = addr;
  276                 cb->bio_caller1 = pv;
  277                 bioq_disksort(&bq, cb);
  278 
  279                 G_LLVM_DEBUG(5,
  280                     "Mapped %s(%ju, %ju) on %s to %zu(%zu,%zu) @ %s:%ju",
  281                     bp->bio_cmd == BIO_READ ? "R" : "W",
  282                     offset, length, lv->lv_name,
  283                     chunk_index, in_chunk_offset, in_chunk_length,
  284                     pv->pv_name, cb->bio_offset);
  285 
  286                 addr += in_chunk_length;
  287                 length -= in_chunk_length;
  288                 offset += in_chunk_length;
  289         }
  290 
  291         /* Fire off bio's here */
  292         count = 0;
  293         for (cb = bioq_first(&bq); cb != NULL; cb = bioq_first(&bq)) {
  294                 bioq_remove(&bq, cb);
  295                 pv = cb->bio_caller1;
  296                 cb->bio_caller1 = NULL;
  297                 G_LLVM_DEBUG(6, "firing bio to %s, offset=%ju, length=%ju",
  298                     cb->bio_to->name, cb->bio_offset, cb->bio_length);
  299                 g_io_request(cb, pv->pv_gcons);
  300                 count++;
  301         }
  302         if (count == 0) { /* We handled everything locally */
  303                 bp->bio_completed = bp->bio_length;
  304                 g_io_deliver(bp, 0);
  305         }
  306 }
  307 
  308 static void
  309 g_llvm_remove_disk(struct g_llvm_vg *vg, struct g_consumer *cp)
  310 {
  311         struct g_llvm_pv *pv;
  312         struct g_llvm_lv *lv;
  313         struct g_llvm_segment *sg;
  314         int found;
  315 
  316         KASSERT(cp != NULL, ("Non-valid disk in %s.", __func__));
  317         pv = (struct g_llvm_pv *)cp->private;
  318 
  319         G_LLVM_DEBUG(0, "Disk %s removed from %s.", cp->provider->name,
  320             pv->pv_name);
  321 
  322         LIST_FOREACH(lv, &vg->vg_lvs, lv_next) {
  323                 /* Find segments that map to this disk */
  324                 found = 0;
  325                 LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
  326                         if (sg->sg_pv == pv) {
  327                                 sg->sg_pv = NULL;
  328                                 lv->lv_sgactive--;
  329                                 found = 1;
  330                                 break;
  331                         }
  332                 }
  333                 if (found) {
  334                         G_LLVM_DEBUG(0, "Device %s removed.",
  335                             lv->lv_gprov->name);
  336                         g_orphan_provider(lv->lv_gprov, ENXIO);
  337                         lv->lv_gprov = NULL;
  338                 }
  339         }
  340 
  341         if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
  342                 g_access(cp, -cp->acr, -cp->acw, -cp->ace);
  343         g_detach(cp);
  344         g_destroy_consumer(cp);
  345 }
  346 
  347 static void
  348 g_llvm_orphan(struct g_consumer *cp)
  349 {
  350         struct g_llvm_vg *vg;
  351         struct g_geom *gp;
  352 
  353         g_topology_assert();
  354         gp = cp->geom;
  355         vg = gp->softc;
  356         if (vg == NULL)
  357                 return;
  358 
  359         g_llvm_remove_disk(vg, cp);
  360         g_llvm_destroy(vg, 1);
  361 }
  362 
  363 static int
  364 g_llvm_activate_lv(struct g_llvm_vg *vg, struct g_llvm_lv *lv)
  365 {
  366         struct g_geom *gp;
  367         struct g_provider *pp;
  368 
  369         g_topology_assert();
  370 
  371         KASSERT(lv->lv_sgactive == lv->lv_sgcount, ("segment missing"));
  372 
  373         gp = vg->vg_geom;
  374         pp = g_new_providerf(gp, "linux_lvm/%s-%s", vg->vg_name, lv->lv_name);
  375         pp->mediasize = vg->vg_extentsize * (off_t)lv->lv_extentcount;
  376         pp->sectorsize = vg->vg_sectorsize;
  377         g_error_provider(pp, 0);
  378         lv->lv_gprov = pp;
  379         pp->private = lv;
  380 
  381         G_LLVM_DEBUG(1, "Created %s, %juM", pp->name,
  382             pp->mediasize / (1024*1024));
  383 
  384         return (0);
  385 }
  386 
  387 static int
  388 g_llvm_add_disk(struct g_llvm_vg *vg, struct g_provider *pp, char *uuid)
  389 {
  390         struct g_geom *gp;
  391         struct g_consumer *cp, *fcp;
  392         struct g_llvm_pv *pv;
  393         struct g_llvm_lv *lv;
  394         struct g_llvm_segment *sg;
  395         int error;
  396 
  397         g_topology_assert();
  398 
  399         LIST_FOREACH(pv, &vg->vg_pvs, pv_next) {
  400                 if (strcmp(pv->pv_uuid, uuid) == 0)
  401                         break;  /* found it */
  402         }
  403         if (pv == NULL) {
  404                 G_LLVM_DEBUG(3, "uuid %s not found in pv list", uuid);
  405                 return (ENOENT);
  406         }
  407         if (pv->pv_gprov != NULL) {
  408                 G_LLVM_DEBUG(0, "disk %s already initialised in %s",
  409                     pv->pv_name, vg->vg_name);
  410                 return (EEXIST);
  411         }
  412 
  413         pv->pv_start *= vg->vg_sectorsize;
  414         gp = vg->vg_geom;
  415         fcp = LIST_FIRST(&gp->consumer);
  416 
  417         cp = g_new_consumer(gp);
  418         error = g_attach(cp, pp);
  419         G_LLVM_DEBUG(1, "Attached %s to %s at offset %ju",
  420             pp->name, pv->pv_name, pv->pv_start);
  421 
  422         if (error != 0) {
  423                 G_LLVM_DEBUG(0, "cannot attach %s to %s",
  424                     pp->name, vg->vg_name);
  425                 g_destroy_consumer(cp);
  426                 return (error);
  427         }
  428 
  429         if (fcp != NULL) {
  430                 if (fcp->provider->sectorsize != pp->sectorsize) {
  431                         G_LLVM_DEBUG(0, "Provider %s of %s has invalid "
  432                             "sector size (%d)", pp->name, vg->vg_name,
  433                             pp->sectorsize);
  434                         return (EINVAL);
  435                 }
  436                 if (fcp->acr > 0 || fcp->acw || fcp->ace > 0) {
  437                         /* Replicate access permissions from first "live"
  438                          * consumer to the new one */
  439                         error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
  440                         if (error != 0) {
  441                                 g_detach(cp);
  442                                 g_destroy_consumer(cp);
  443                                 return (error);
  444                         }
  445                 }
  446         }
  447 
  448         cp->private = pv;
  449         pv->pv_gcons = cp;
  450         pv->pv_gprov = pp;
  451 
  452         LIST_FOREACH(lv, &vg->vg_lvs, lv_next) {
  453                 /* Find segments that map to this disk */
  454                 LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
  455                         if (strcmp(sg->sg_pvname, pv->pv_name) == 0) {
  456                                 /* avtivate the segment */
  457                                 KASSERT(sg->sg_pv == NULL,
  458                                     ("segment already mapped"));
  459                                 sg->sg_pvoffset =
  460                                     (off_t)sg->sg_pvstart * vg->vg_extentsize
  461                                     + pv->pv_start;
  462                                 sg->sg_pv = pv;
  463                                 lv->lv_sgactive++;
  464 
  465                                 G_LLVM_DEBUG(2, "%s: %d to %d @ %s:%d"
  466                                     " offset %ju sector %ju",
  467                                     lv->lv_name, sg->sg_start, sg->sg_end,
  468                                     sg->sg_pvname, sg->sg_pvstart,
  469                                     sg->sg_pvoffset,
  470                                     sg->sg_pvoffset / vg->vg_sectorsize);
  471                         }
  472                 }
  473                 /* Activate any lvs waiting on this disk */
  474                 if (lv->lv_gprov == NULL && lv->lv_sgactive == lv->lv_sgcount) {
  475                         error = g_llvm_activate_lv(vg, lv);
  476                         if (error)
  477                                 break;
  478                 }
  479         }
  480         return (error);
  481 }
  482 
  483 static void
  484 g_llvm_init(struct g_class *mp)
  485 {
  486         LIST_INIT(&vg_list);
  487 }
  488 
  489 static void
  490 g_llvm_free_vg(struct g_llvm_vg *vg)
  491 {
  492         struct g_llvm_pv *pv;
  493         struct g_llvm_lv *lv;
  494         struct g_llvm_segment *sg;
  495 
  496         /* Free all the structures */
  497         while ((pv = LIST_FIRST(&vg->vg_pvs)) != NULL) {
  498                 LIST_REMOVE(pv, pv_next);
  499                 free(pv, M_GLLVM);
  500         }
  501         while ((lv = LIST_FIRST(&vg->vg_lvs)) != NULL) {
  502                 while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) {
  503                         LIST_REMOVE(sg, sg_next);
  504                         free(sg, M_GLLVM);
  505                 }
  506                 LIST_REMOVE(lv, lv_next);
  507                 free(lv, M_GLLVM);
  508         }
  509         LIST_REMOVE(vg, vg_next);
  510         free(vg, M_GLLVM);
  511 }
  512 
  513 static void
  514 g_llvm_taste_orphan(struct g_consumer *cp)
  515 {
  516 
  517         KASSERT(1 == 0, ("%s called while tasting %s.", __func__,
  518             cp->provider->name));
  519 }
  520 
  521 static struct g_geom *
  522 g_llvm_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
  523 {
  524         struct g_consumer *cp;
  525         struct g_geom *gp;
  526         struct g_llvm_label ll;
  527         struct g_llvm_metadata md;
  528         struct g_llvm_vg *vg;
  529         int error;
  530 
  531         bzero(&md, sizeof(md));
  532 
  533         g_topology_assert();
  534         g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
  535         gp = g_new_geomf(mp, "linux_lvm:taste");
  536         /* This orphan function should be never called. */
  537         gp->orphan = g_llvm_taste_orphan;
  538         cp = g_new_consumer(gp);
  539         g_attach(cp, pp);
  540         error = g_llvm_read_label(cp, &ll);
  541         if (!error)
  542                 error = g_llvm_read_md(cp, &md, &ll);
  543         g_detach(cp);
  544         g_destroy_consumer(cp);
  545         g_destroy_geom(gp);
  546         if (error != 0)
  547                 return (NULL);
  548 
  549         vg = md.md_vg;
  550         if (vg->vg_geom == NULL) {
  551                 /* new volume group */
  552                 gp = g_new_geomf(mp, "%s", vg->vg_name);
  553                 gp->start = g_llvm_start;
  554                 gp->spoiled = g_llvm_orphan;
  555                 gp->orphan = g_llvm_orphan;
  556                 gp->access = g_llvm_access;
  557                 vg->vg_sectorsize = pp->sectorsize;
  558                 vg->vg_extentsize *= vg->vg_sectorsize;
  559                 vg->vg_geom = gp;
  560                 gp->softc = vg;
  561                 G_LLVM_DEBUG(1, "Created volume %s, extent size %zuK",
  562                     vg->vg_name, vg->vg_extentsize / 1024);
  563         }
  564 
  565         /* initialise this disk in the volume group */
  566         g_llvm_add_disk(vg, pp, ll.ll_uuid);
  567         return (vg->vg_geom);
  568 }
  569 
  570 static int
  571 g_llvm_destroy(struct g_llvm_vg *vg, int force)
  572 {
  573         struct g_provider *pp;
  574         struct g_geom *gp;
  575 
  576         g_topology_assert();
  577         if (vg == NULL)
  578                 return (ENXIO);
  579         gp = vg->vg_geom;
  580 
  581         LIST_FOREACH(pp, &gp->provider, provider) {
  582                 if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) {
  583                         G_LLVM_DEBUG(1, "Device %s is still open (r%dw%de%d)",
  584                             pp->name, pp->acr, pp->acw, pp->ace);
  585                         if (!force)
  586                                 return (EBUSY);
  587                 }
  588         }
  589 
  590         g_llvm_free_vg(gp->softc);
  591         gp->softc = NULL;
  592         g_wither_geom(gp, ENXIO);
  593         return (0);
  594 }
  595 
  596 static int
  597 g_llvm_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused,
  598     struct g_geom *gp)
  599 {
  600         struct g_llvm_vg *vg;
  601 
  602         vg = gp->softc;
  603         return (g_llvm_destroy(vg, 0));
  604 }
  605 
  606 int
  607 g_llvm_read_label(struct g_consumer *cp, struct g_llvm_label *ll)
  608 {
  609         struct g_provider *pp;
  610         u_char *buf;
  611         int i, error = 0;
  612 
  613         g_topology_assert();
  614 
  615         /* The LVM label is stored on the first four sectors */
  616         error = g_access(cp, 1, 0, 0);
  617         if (error != 0)
  618                 return (error);
  619         pp = cp->provider;
  620         g_topology_unlock();
  621         buf = g_read_data(cp, 0, pp->sectorsize * 4, &error);
  622         g_topology_lock();
  623         g_access(cp, -1, 0, 0);
  624         if (buf == NULL) {
  625                 G_LLVM_DEBUG(1, "Cannot read metadata from %s (error=%d)",
  626                     pp->name, error);
  627                 return (error);
  628         }
  629 
  630         /* Search the four sectors for the LVM label. */
  631         for (i = 0; i < 4; i++) {
  632                 error = llvm_label_decode(&buf[i * pp->sectorsize], ll, i);
  633                 if (error == 0)
  634                         break;  /* found it */
  635         }
  636         g_free(buf);
  637         return (error);
  638 }
  639 
  640 int
  641 g_llvm_read_md(struct g_consumer *cp, struct g_llvm_metadata *md,
  642     struct g_llvm_label *ll)
  643 {
  644         struct g_provider *pp;
  645         u_char *buf;
  646         int error;
  647         int size;
  648 
  649         g_topology_assert();
  650 
  651         error = g_access(cp, 1, 0, 0);
  652         if (error != 0)
  653                 return (error);
  654         pp = cp->provider;
  655         g_topology_unlock();
  656         buf = g_read_data(cp, ll->ll_md_offset, pp->sectorsize, &error);
  657         g_topology_lock();
  658         g_access(cp, -1, 0, 0);
  659         if (buf == NULL) {
  660                 G_LLVM_DEBUG(0, "Cannot read metadata from %s (error=%d)",
  661                     cp->provider->name, error);
  662                 return (error);
  663         }
  664 
  665         error = llvm_md_decode(buf, md, ll);
  666         g_free(buf);
  667         if (error != 0) {
  668                 return (error);
  669         }
  670 
  671         G_LLVM_DEBUG(1, "reading LVM2 config @ %s:%ju", pp->name,
  672                     ll->ll_md_offset + md->md_reloffset);
  673         error = g_access(cp, 1, 0, 0);
  674         if (error != 0)
  675                 return (error);
  676         pp = cp->provider;
  677         g_topology_unlock();
  678         /* round up to the nearest sector */
  679         size = md->md_relsize +
  680             (pp->sectorsize - md->md_relsize % pp->sectorsize);
  681         buf = g_read_data(cp, ll->ll_md_offset + md->md_reloffset, size, &error);
  682         g_topology_lock();
  683         g_access(cp, -1, 0, 0);
  684         if (buf == NULL) {
  685                 G_LLVM_DEBUG(0, "Cannot read LVM2 config from %s (error=%d)",
  686                     pp->name, error);
  687                 return (error);
  688         }
  689         buf[md->md_relsize] = '\0';
  690         G_LLVM_DEBUG(10, "LVM config:\n%s\n", buf);
  691         error = llvm_textconf_decode(buf, md->md_relsize, md);
  692         g_free(buf);
  693 
  694         return (error);
  695 }
  696 
  697 static int
  698 llvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector)
  699 {
  700         uint64_t off;
  701         char *uuid;
  702 
  703         /* Magic string */
  704         if (bcmp("LABELONE", data , 8) != 0)
  705                 return (EINVAL);
  706 
  707         /* We only support LVM2 text format */
  708         if (bcmp("LVM2 001", data + 24, 8) != 0) {
  709                 G_LLVM_DEBUG(0, "Unsupported LVM format");
  710                 return (EINVAL);
  711         }
  712 
  713         ll->ll_sector = le64dec(data + 8);
  714         ll->ll_crc = le32dec(data + 16);
  715         ll->ll_offset = le32dec(data + 20);
  716 
  717         if (ll->ll_sector != sector) {
  718                 G_LLVM_DEBUG(0, "Expected sector %ju, found at %d",
  719                     ll->ll_sector, sector);
  720                 return (EINVAL);
  721         }
  722 
  723         off = ll->ll_offset;
  724         /*
  725          * convert the binary uuid to string format, the format is
  726          * xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx (6-4-4-4-4-4-6)
  727          */
  728         uuid = ll->ll_uuid;
  729         bcopy(data + off, uuid, 6);
  730         off += 6;
  731         uuid += 6;
  732         *uuid++ = '-';
  733         for (int i = 0; i < 5; i++) {
  734                 bcopy(data + off, uuid, 4);
  735                 off += 4;
  736                 uuid += 4;
  737                 *uuid++ = '-';
  738         }
  739         bcopy(data + off, uuid, 6);
  740         off += 6;
  741         uuid += 6;
  742         *uuid++ = '\0';
  743 
  744         ll->ll_size = le64dec(data + off);
  745         off += 8;
  746         ll->ll_pestart = le64dec(data + off);
  747         off += 16;
  748 
  749         /* Only one data section is supported */
  750         if (le64dec(data + off) != 0) {
  751                 G_LLVM_DEBUG(0, "Only one data section supported");
  752                 return (EINVAL);
  753         }
  754 
  755         off += 16;
  756         ll->ll_md_offset = le64dec(data + off);
  757         off += 8;
  758         ll->ll_md_size = le64dec(data + off);
  759         off += 8;
  760 
  761         G_LLVM_DEBUG(1, "LVM metadata: offset=%ju, size=%ju", ll->ll_md_offset,
  762             ll->ll_md_size);
  763 
  764         /* Only one data section is supported */
  765         if (le64dec(data + off) != 0) {
  766                 G_LLVM_DEBUG(0, "Only one metadata section supported");
  767                 return (EINVAL);
  768         }
  769 
  770         G_LLVM_DEBUG(2, "label uuid=%s", ll->ll_uuid);
  771         G_LLVM_DEBUG(2, "sector=%ju, crc=%u, offset=%u, size=%ju, pestart=%ju",
  772             ll->ll_sector, ll->ll_crc, ll->ll_offset, ll->ll_size,
  773             ll->ll_pestart);
  774 
  775         return (0);
  776 }
  777 
  778 static int
  779 llvm_md_decode(const u_char *data, struct g_llvm_metadata *md,
  780     struct g_llvm_label *ll)
  781 {
  782         uint64_t off;
  783         char magic[16];
  784 
  785         off = 0;
  786         md->md_csum = le32dec(data + off);
  787         off += 4;
  788         bcopy(data + off, magic, 16);
  789         off += 16;
  790         md->md_version = le32dec(data + off);
  791         off += 4;
  792         md->md_start = le64dec(data + off);
  793         off += 8;
  794         md->md_size = le64dec(data + off);
  795         off += 8;
  796 
  797         if (bcmp(G_LLVM_MAGIC, magic, 16) != 0) {
  798                 G_LLVM_DEBUG(0, "Incorrect md magic number");
  799                 return (EINVAL);
  800         }
  801         if (md->md_version != 1) {
  802                 G_LLVM_DEBUG(0, "Incorrect md version number (%u)",
  803                     md->md_version);
  804                 return (EINVAL);
  805         }
  806         if (md->md_start != ll->ll_md_offset) {
  807                 G_LLVM_DEBUG(0, "Incorrect md offset (%ju)", md->md_start);
  808                 return (EINVAL);
  809         }
  810 
  811         /* Aparently only one is ever returned */
  812         md->md_reloffset = le64dec(data + off);
  813         off += 8;
  814         md->md_relsize = le64dec(data + off);
  815         off += 16;      /* XXX skipped checksum */
  816 
  817         if (le64dec(data + off) != 0) {
  818                 G_LLVM_DEBUG(0, "Only one reloc supported");
  819                 return (EINVAL);
  820         }
  821 
  822         G_LLVM_DEBUG(3, "reloc: offset=%ju, size=%ju",
  823             md->md_reloffset, md->md_relsize);
  824         G_LLVM_DEBUG(3, "md: version=%u, start=%ju, size=%ju",
  825             md->md_version, md->md_start, md->md_size);
  826 
  827         return (0);
  828 }
  829 
  830 #define GRAB_INT(key, tok1, tok2, v)                                    \
  831         if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) {     \
  832                 v = strtol(tok2, &tok1, 10);                            \
  833                 if (tok1 == tok2)                                       \
  834                         /* strtol did not eat any of the buffer */      \
  835                         goto bad;                                       \
  836                 continue;                                               \
  837         }
  838 
  839 #define GRAB_STR(key, tok1, tok2, v, len)                               \
  840         if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) {     \
  841                 strsep(&tok2, "\"");                                    \
  842                 if (tok2 == NULL)                                       \
  843                         continue;                                       \
  844                 tok1 = strsep(&tok2, "\"");                             \
  845                 if (tok2 == NULL)                                       \
  846                         continue;                                       \
  847                 strncpy(v, tok1, len);                                  \
  848                 continue;                                               \
  849         }
  850 
  851 #define SPLIT(key, value, str)                                          \
  852         key = strsep(&value, str);                                      \
  853         /* strip trailing whitespace on the key */                      \
  854         for (char *t = key; *t != '\0'; t++)                            \
  855                 if (isspace(*t)) {                                      \
  856                         *t = '\0';                                      \
  857                         break;                                          \
  858                 }
  859 
  860 static size_t 
  861 llvm_grab_name(char *name, const char *tok)
  862 {
  863         size_t len;
  864 
  865         len = 0;
  866         if (tok == NULL)
  867                 return (0);
  868         if (tok[0] == '-')
  869                 return (0);
  870         if (strcmp(tok, ".") == 0 || strcmp(tok, "..") == 0)
  871                 return (0);
  872         while (tok[len] && (isalpha(tok[len]) || isdigit(tok[len]) ||
  873             tok[len] == '.' || tok[len] == '_' || tok[len] == '-' ||
  874             tok[len] == '+') && len < G_LLVM_NAMELEN - 1)
  875                 len++;
  876         bcopy(tok, name, len);
  877         name[len] = '\0';
  878         return (len);
  879 }
  880 
  881 static int
  882 llvm_textconf_decode(u_char *data, int buflen, struct g_llvm_metadata *md)
  883 {
  884         struct g_llvm_vg        *vg;
  885         char *buf = data;
  886         char *tok, *v;
  887         char name[G_LLVM_NAMELEN];
  888         char uuid[G_LLVM_UUIDLEN];
  889         size_t len;
  890 
  891         if (buf == NULL || *buf == '\0')
  892                 return (EINVAL);
  893 
  894         tok = strsep(&buf, "\n");
  895         if (tok == NULL)
  896                 return (EINVAL);
  897         len = llvm_grab_name(name, tok);
  898         if (len == 0)
  899                 return (EINVAL);
  900 
  901         /* check too see if the vg has already been loaded off another disk */
  902         LIST_FOREACH(vg, &vg_list, vg_next) {
  903                 if (strcmp(vg->vg_name, name) == 0) {
  904                         uuid[0] = '\0';
  905                         /* grab the volume group uuid */
  906                         while ((tok = strsep(&buf, "\n")) != NULL) {
  907                                 if (strstr(tok, "{"))
  908                                         break;
  909                                 if (strstr(tok, "=")) {
  910                                         SPLIT(v, tok, "=");
  911                                         GRAB_STR("id", v, tok, uuid,
  912                                             sizeof(uuid));
  913                                 }
  914                         }
  915                         if (strcmp(vg->vg_uuid, uuid) == 0) {
  916                                 /* existing vg */
  917                                 md->md_vg = vg;
  918                                 return (0);
  919                         }
  920                         /* XXX different volume group with name clash! */
  921                         G_LLVM_DEBUG(0,
  922                             "%s already exists, volume group not loaded", name);
  923                         return (EINVAL);
  924                 }
  925         }
  926 
  927         vg = malloc(sizeof(*vg), M_GLLVM, M_NOWAIT|M_ZERO);
  928         if (vg == NULL)
  929                 return (ENOMEM);
  930 
  931         strncpy(vg->vg_name, name, sizeof(vg->vg_name));
  932         LIST_INIT(&vg->vg_pvs);
  933         LIST_INIT(&vg->vg_lvs);
  934 
  935 #define VOL_FOREACH(func, tok, buf, p)                                  \
  936         while ((tok = strsep(buf, "\n")) != NULL) {                     \
  937                 if (strstr(tok, "{")) {                                 \
  938                         func(buf, tok, p);                              \
  939                         continue;                                       \
  940                 }                                                       \
  941                 if (strstr(tok, "}"))                                   \
  942                         break;                                          \
  943         }
  944 
  945         while ((tok = strsep(&buf, "\n")) != NULL) {
  946                 if (strcmp(tok, "physical_volumes {") == 0) {
  947                         VOL_FOREACH(llvm_textconf_decode_pv, tok, &buf, vg);
  948                         continue;
  949                 }
  950                 if (strcmp(tok, "logical_volumes {") == 0) {
  951                         VOL_FOREACH(llvm_textconf_decode_lv, tok, &buf, vg);
  952                         continue;
  953                 }
  954                 if (strstr(tok, "{")) {
  955                         G_LLVM_DEBUG(2, "unknown section %s", tok);
  956                         continue;
  957                 }
  958 
  959                 /* parse 'key = value' lines */
  960                 if (strstr(tok, "=")) {
  961                         SPLIT(v, tok, "=");
  962                         GRAB_STR("id", v, tok, vg->vg_uuid, sizeof(vg->vg_uuid));
  963                         GRAB_INT("extent_size", v, tok, vg->vg_extentsize);
  964                         continue;
  965                 }
  966         }
  967         /* basic checking */
  968         if (vg->vg_extentsize == 0)
  969                 goto bad;
  970 
  971         md->md_vg = vg;
  972         LIST_INSERT_HEAD(&vg_list, vg, vg_next);
  973         G_LLVM_DEBUG(3, "vg: name=%s uuid=%s", vg->vg_name, vg->vg_uuid);
  974         return(0);
  975 
  976 bad:
  977         g_llvm_free_vg(vg);
  978         return (-1);
  979 }
  980 #undef  VOL_FOREACH
  981 
  982 static int
  983 llvm_textconf_decode_pv(char **buf, char *tok, struct g_llvm_vg *vg)
  984 {
  985         struct g_llvm_pv        *pv;
  986         char *v;
  987         size_t len;
  988 
  989         if (*buf == NULL || **buf == '\0')
  990                 return (EINVAL);
  991 
  992         pv = malloc(sizeof(*pv), M_GLLVM, M_NOWAIT|M_ZERO);
  993         if (pv == NULL)
  994                 return (ENOMEM);
  995 
  996         pv->pv_vg = vg;
  997         len = 0;
  998         if (tok == NULL)
  999                 goto bad;
 1000         len = llvm_grab_name(pv->pv_name, tok);
 1001         if (len == 0)
 1002                 goto bad;
 1003 
 1004         while ((tok = strsep(buf, "\n")) != NULL) {
 1005                 if (strstr(tok, "{"))
 1006                         goto bad;
 1007 
 1008                 if (strstr(tok, "}"))
 1009                         break;
 1010 
 1011                 /* parse 'key = value' lines */
 1012                 if (strstr(tok, "=")) {
 1013                         SPLIT(v, tok, "=");
 1014                         GRAB_STR("id", v, tok, pv->pv_uuid, sizeof(pv->pv_uuid));
 1015                         GRAB_INT("pe_start", v, tok, pv->pv_start);
 1016                         GRAB_INT("pe_count", v, tok, pv->pv_count);
 1017                         continue;
 1018                 }
 1019         }
 1020         if (tok == NULL)
 1021                 goto bad;
 1022         /* basic checking */
 1023         if (pv->pv_count == 0)
 1024                 goto bad;
 1025 
 1026         LIST_INSERT_HEAD(&vg->vg_pvs, pv, pv_next);
 1027         G_LLVM_DEBUG(3, "pv: name=%s uuid=%s", pv->pv_name, pv->pv_uuid);
 1028 
 1029         return (0);
 1030 bad:
 1031         free(pv, M_GLLVM);
 1032         return (-1);
 1033 }
 1034 
 1035 static int
 1036 llvm_textconf_decode_lv(char **buf, char *tok, struct g_llvm_vg *vg)
 1037 {
 1038         struct g_llvm_lv        *lv;
 1039         struct g_llvm_segment *sg;
 1040         char *v;
 1041         size_t len;
 1042 
 1043         if (*buf == NULL || **buf == '\0')
 1044                 return (EINVAL);
 1045 
 1046         lv = malloc(sizeof(*lv), M_GLLVM, M_NOWAIT|M_ZERO);
 1047         if (lv == NULL)
 1048                 return (ENOMEM);
 1049 
 1050         lv->lv_vg = vg;
 1051         LIST_INIT(&lv->lv_segs);
 1052 
 1053         if (tok == NULL)
 1054                 goto bad;
 1055         len = llvm_grab_name(lv->lv_name, tok);
 1056         if (len == 0)
 1057                 goto bad;
 1058 
 1059         while ((tok = strsep(buf, "\n")) != NULL) {
 1060                 if (strstr(tok, "{")) {
 1061                         if (strstr(tok, "segment")) {
 1062                                 llvm_textconf_decode_sg(buf, tok, lv);
 1063                                 continue;
 1064                         } else
 1065                                 /* unexpected section */
 1066                                 goto bad;
 1067                 }
 1068 
 1069                 if (strstr(tok, "}"))
 1070                         break;
 1071 
 1072                 /* parse 'key = value' lines */
 1073                 if (strstr(tok, "=")) {
 1074                         SPLIT(v, tok, "=");
 1075                         GRAB_STR("id", v, tok, lv->lv_uuid, sizeof(lv->lv_uuid));
 1076                         GRAB_INT("segment_count", v, tok, lv->lv_sgcount);
 1077                         continue;
 1078                 }
 1079         }
 1080         if (tok == NULL)
 1081                 goto bad;
 1082         if (lv->lv_sgcount == 0 || lv->lv_sgcount != lv->lv_numsegs)
 1083                 /* zero or incomplete segment list */
 1084                 goto bad;
 1085 
 1086         /* Optimize for only one segment on the pv */
 1087         lv->lv_firstsg = LIST_FIRST(&lv->lv_segs);
 1088         LIST_INSERT_HEAD(&vg->vg_lvs, lv, lv_next);
 1089         G_LLVM_DEBUG(3, "lv: name=%s uuid=%s", lv->lv_name, lv->lv_uuid);
 1090 
 1091         return (0);
 1092 bad:
 1093         while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) {
 1094                 LIST_REMOVE(sg, sg_next);
 1095                 free(sg, M_GLLVM);
 1096         }
 1097         free(lv, M_GLLVM);
 1098         return (-1);
 1099 }
 1100 
 1101 static int
 1102 llvm_textconf_decode_sg(char **buf, char *tok, struct g_llvm_lv *lv)
 1103 {
 1104         struct g_llvm_segment *sg;
 1105         char *v;
 1106         int count = 0;
 1107 
 1108         if (*buf == NULL || **buf == '\0')
 1109                 return (EINVAL);
 1110 
 1111         sg = malloc(sizeof(*sg), M_GLLVM, M_NOWAIT|M_ZERO);
 1112         if (sg == NULL)
 1113                 return (ENOMEM);
 1114 
 1115         while ((tok = strsep(buf, "\n")) != NULL) {
 1116                 /* only a single linear stripe is supported */
 1117                 if (strstr(tok, "stripe_count")) {
 1118                         SPLIT(v, tok, "=");
 1119                         GRAB_INT("stripe_count", v, tok, count);
 1120                         if (count != 1)
 1121                                 goto bad;
 1122                 }
 1123 
 1124                 if (strstr(tok, "{"))
 1125                         goto bad;
 1126 
 1127                 if (strstr(tok, "}"))
 1128                         break;
 1129 
 1130                 if (strcmp(tok, "stripes = [") == 0) {
 1131                         tok = strsep(buf, "\n");
 1132                         if (tok == NULL)
 1133                                 goto bad;
 1134 
 1135                         strsep(&tok, "\"");
 1136                         if (tok == NULL)
 1137                                 goto bad;       /* missing open quotes */
 1138                         v = strsep(&tok, "\"");
 1139                         if (tok == NULL)
 1140                                 goto bad;       /* missing close quotes */
 1141                         strncpy(sg->sg_pvname, v, sizeof(sg->sg_pvname));
 1142                         if (*tok != ',')
 1143                                 goto bad;       /* missing comma for stripe */
 1144                         tok++;
 1145 
 1146                         sg->sg_pvstart = strtol(tok, &v, 10);
 1147                         if (v == tok)
 1148                                 /* strtol did not eat any of the buffer */
 1149                                 goto bad;
 1150 
 1151                         continue;
 1152                 }
 1153 
 1154                 /* parse 'key = value' lines */
 1155                 if (strstr(tok, "=")) {
 1156                         SPLIT(v, tok, "=");
 1157                         GRAB_INT("start_extent", v, tok, sg->sg_start);
 1158                         GRAB_INT("extent_count", v, tok, sg->sg_count);
 1159                         continue;
 1160                 }
 1161         }
 1162         if (tok == NULL)
 1163                 goto bad;
 1164         /* basic checking */
 1165         if (count != 1 || sg->sg_count == 0)
 1166                 goto bad;
 1167 
 1168         sg->sg_end = sg->sg_start + sg->sg_count - 1;
 1169         lv->lv_numsegs++;
 1170         lv->lv_extentcount += sg->sg_count;
 1171         LIST_INSERT_HEAD(&lv->lv_segs, sg, sg_next);
 1172 
 1173         return (0);
 1174 bad:
 1175         free(sg, M_GLLVM);
 1176         return (-1);
 1177 }
 1178 #undef  GRAB_INT
 1179 #undef  GRAB_STR
 1180 #undef  SPLIT
 1181 
 1182 static struct g_class g_llvm_class = {
 1183         .name = G_LLVM_CLASS_NAME,
 1184         .version = G_VERSION,
 1185         .init = g_llvm_init,
 1186         .taste = g_llvm_taste,
 1187         .destroy_geom = g_llvm_destroy_geom
 1188 };
 1189 
 1190 DECLARE_GEOM_CLASS(g_llvm_class, g_linux_lvm);

Cache object: e5122041d3c52ca40027d5365d8fdc76


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