g_linux_lvm.c revision 176417
1176417Sthompsa/*- 2176417Sthompsa * Copyright (c) 2008 Andrew Thompson <thompsa@FreeBSD.org> 3176417Sthompsa * All rights reserved. 4176417Sthompsa * 5176417Sthompsa * Redistribution and use in source and binary forms, with or without 6176417Sthompsa * modification, are permitted provided that the following conditions 7176417Sthompsa * are met: 8176417Sthompsa * 1. Redistributions of source code must retain the above copyright 9176417Sthompsa * notice, this list of conditions and the following disclaimer. 10176417Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 11176417Sthompsa * notice, this list of conditions and the following disclaimer in the 12176417Sthompsa * documentation and/or other materials provided with the distribution. 13176417Sthompsa * 14176417Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15176417Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16176417Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17176417Sthompsa * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18176417Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19176417Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20176417Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21176417Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22176417Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23176417Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24176417Sthompsa * SUCH DAMAGE. 25176417Sthompsa */ 26176417Sthompsa 27176417Sthompsa#include <sys/cdefs.h> 28176417Sthompsa__FBSDID("$FreeBSD: head/sys/geom/linux_lvm/g_linux_lvm.c 176417 2008-02-20 07:45:36Z thompsa $"); 29176417Sthompsa 30176417Sthompsa#include <sys/ctype.h> 31176417Sthompsa#include <sys/param.h> 32176417Sthompsa#include <sys/bio.h> 33176417Sthompsa#include <sys/kernel.h> 34176417Sthompsa#include <sys/limits.h> 35176417Sthompsa#include <sys/malloc.h> 36176417Sthompsa#include <sys/queue.h> 37176417Sthompsa#include <sys/sysctl.h> 38176417Sthompsa#include <sys/systm.h> 39176417Sthompsa 40176417Sthompsa#include <geom/geom.h> 41176417Sthompsa#include <sys/endian.h> 42176417Sthompsa 43176417Sthompsa#include <geom/linux_lvm/g_linux_lvm.h> 44176417Sthompsa 45176417Sthompsa/* Declare malloc(9) label */ 46176417Sthompsastatic MALLOC_DEFINE(M_GLLVM, "gllvm", "GEOM_LINUX_LVM Data"); 47176417Sthompsa 48176417Sthompsa/* GEOM class methods */ 49176417Sthompsastatic g_access_t g_llvm_access; 50176417Sthompsastatic g_init_t g_llvm_init; 51176417Sthompsastatic g_orphan_t g_llvm_orphan; 52176417Sthompsastatic g_orphan_t g_llvm_taste_orphan; 53176417Sthompsastatic g_start_t g_llvm_start; 54176417Sthompsastatic g_taste_t g_llvm_taste; 55176417Sthompsastatic g_ctl_destroy_geom_t g_llvm_destroy_geom; 56176417Sthompsa 57176417Sthompsastatic void g_llvm_done(struct bio *); 58176417Sthompsastatic void g_llvm_remove_disk(struct g_llvm_vg *, struct g_consumer *); 59176417Sthompsastatic int g_llvm_activate_lv(struct g_llvm_vg *, struct g_llvm_lv *); 60176417Sthompsastatic int g_llvm_add_disk(struct g_llvm_vg *, struct g_provider *, char *); 61176417Sthompsastatic void g_llvm_free_vg(struct g_llvm_vg *); 62176417Sthompsastatic int g_llvm_destroy(struct g_llvm_vg *, int); 63176417Sthompsastatic int g_llvm_read_label(struct g_consumer *, struct g_llvm_label *); 64176417Sthompsastatic int g_llvm_read_md(struct g_consumer *, struct g_llvm_metadata *, 65176417Sthompsa struct g_llvm_label *); 66176417Sthompsa 67176417Sthompsastatic int llvm_label_decode(const u_char *, struct g_llvm_label *, int); 68176417Sthompsastatic int llvm_md_decode(const u_char *, struct g_llvm_metadata *, 69176417Sthompsa struct g_llvm_label *); 70176417Sthompsastatic int llvm_textconf_decode(u_char *, int, 71176417Sthompsa struct g_llvm_metadata *); 72176417Sthompsastatic int llvm_textconf_decode_pv(char **, char *, struct g_llvm_vg *); 73176417Sthompsastatic int llvm_textconf_decode_lv(char **, char *, struct g_llvm_vg *); 74176417Sthompsastatic int llvm_textconf_decode_sg(char **, char *, struct g_llvm_lv *); 75176417Sthompsa 76176417SthompsaSYSCTL_DECL(_kern_geom); 77176417SthompsaSYSCTL_NODE(_kern_geom, OID_AUTO, linux_lvm, CTLFLAG_RW, 0, 78176417Sthompsa "GEOM_LINUX_LVM stuff"); 79176417Sthompsastatic u_int g_llvm_debug = 0; 80176417SthompsaTUNABLE_INT("kern.geom.linux_lvm.debug", &g_llvm_debug); 81176417SthompsaSYSCTL_UINT(_kern_geom_linux_lvm, OID_AUTO, debug, CTLFLAG_RW, &g_llvm_debug, 0, 82176417Sthompsa "Debug level"); 83176417Sthompsa 84176417SthompsaLIST_HEAD(, g_llvm_vg) vg_list; 85176417Sthompsa 86176417Sthompsa/* 87176417Sthompsa * Called to notify geom when it's been opened, and for what intent 88176417Sthompsa */ 89176417Sthompsastatic int 90176417Sthompsag_llvm_access(struct g_provider *pp, int dr, int dw, int de) 91176417Sthompsa{ 92176417Sthompsa struct g_consumer *c; 93176417Sthompsa struct g_llvm_vg *vg; 94176417Sthompsa struct g_geom *gp; 95176417Sthompsa int error; 96176417Sthompsa 97176417Sthompsa KASSERT(pp != NULL, ("%s: NULL provider", __func__)); 98176417Sthompsa gp = pp->geom; 99176417Sthompsa KASSERT(gp != NULL, ("%s: NULL geom", __func__)); 100176417Sthompsa vg = gp->softc; 101176417Sthompsa 102176417Sthompsa if (vg == NULL) { 103176417Sthompsa /* It seems that .access can be called with negative dr,dw,dx 104176417Sthompsa * in this case but I want to check for myself */ 105176417Sthompsa G_LLVM_DEBUG(0, "access(%d, %d, %d) for %s", 106176417Sthompsa dr, dw, de, pp->name); 107176417Sthompsa 108176417Sthompsa /* This should only happen when geom is withered so 109176417Sthompsa * allow only negative requests */ 110176417Sthompsa KASSERT(dr <= 0 && dw <= 0 && de <= 0, 111176417Sthompsa ("%s: Positive access for %s", __func__, pp->name)); 112176417Sthompsa if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0) 113176417Sthompsa G_LLVM_DEBUG(0, 114176417Sthompsa "Device %s definitely destroyed", pp->name); 115176417Sthompsa return (0); 116176417Sthompsa } 117176417Sthompsa 118176417Sthompsa /* Grab an exclusive bit to propagate on our consumers on first open */ 119176417Sthompsa if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 120176417Sthompsa de++; 121176417Sthompsa /* ... drop it on close */ 122176417Sthompsa if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0) 123176417Sthompsa de--; 124176417Sthompsa 125176417Sthompsa error = ENXIO; 126176417Sthompsa LIST_FOREACH(c, &gp->consumer, consumer) { 127176417Sthompsa KASSERT(c != NULL, ("%s: consumer is NULL", __func__)); 128176417Sthompsa error = g_access(c, dr, dw, de); 129176417Sthompsa if (error != 0) { 130176417Sthompsa struct g_consumer *c2; 131176417Sthompsa 132176417Sthompsa /* Backout earlier changes */ 133176417Sthompsa LIST_FOREACH(c2, &gp->consumer, consumer) { 134176417Sthompsa if (c2 == c) /* all eariler components fixed */ 135176417Sthompsa return (error); 136176417Sthompsa g_access(c2, -dr, -dw, -de); 137176417Sthompsa } 138176417Sthompsa } 139176417Sthompsa } 140176417Sthompsa 141176417Sthompsa return (error); 142176417Sthompsa} 143176417Sthompsa 144176417Sthompsa/* 145176417Sthompsa * Dismantle bio_queue and destroy its components 146176417Sthompsa */ 147176417Sthompsastatic void 148176417Sthompsabioq_dismantle(struct bio_queue_head *bq) 149176417Sthompsa{ 150176417Sthompsa struct bio *b; 151176417Sthompsa 152176417Sthompsa for (b = bioq_first(bq); b != NULL; b = bioq_first(bq)) { 153176417Sthompsa bioq_remove(bq, b); 154176417Sthompsa g_destroy_bio(b); 155176417Sthompsa } 156176417Sthompsa} 157176417Sthompsa 158176417Sthompsa/* 159176417Sthompsa * GEOM .done handler 160176417Sthompsa * Can't use standard handler because one requested IO may 161176417Sthompsa * fork into additional data IOs 162176417Sthompsa */ 163176417Sthompsastatic void 164176417Sthompsag_llvm_done(struct bio *b) 165176417Sthompsa{ 166176417Sthompsa struct bio *parent_b; 167176417Sthompsa 168176417Sthompsa parent_b = b->bio_parent; 169176417Sthompsa 170176417Sthompsa if (b->bio_error != 0) { 171176417Sthompsa G_LLVM_DEBUG(0, "Error %d for offset=%ju, length=%ju on %s", 172176417Sthompsa b->bio_error, b->bio_offset, b->bio_length, 173176417Sthompsa b->bio_to->name); 174176417Sthompsa if (parent_b->bio_error == 0) 175176417Sthompsa parent_b->bio_error = b->bio_error; 176176417Sthompsa } 177176417Sthompsa 178176417Sthompsa parent_b->bio_inbed++; 179176417Sthompsa parent_b->bio_completed += b->bio_completed; 180176417Sthompsa 181176417Sthompsa if (parent_b->bio_children == parent_b->bio_inbed) { 182176417Sthompsa parent_b->bio_completed = parent_b->bio_length; 183176417Sthompsa g_io_deliver(parent_b, parent_b->bio_error); 184176417Sthompsa } 185176417Sthompsa g_destroy_bio(b); 186176417Sthompsa} 187176417Sthompsa 188176417Sthompsastatic void 189176417Sthompsag_llvm_start(struct bio *bp) 190176417Sthompsa{ 191176417Sthompsa struct g_provider *pp; 192176417Sthompsa struct g_llvm_vg *vg; 193176417Sthompsa struct g_llvm_pv *pv; 194176417Sthompsa struct g_llvm_lv *lv; 195176417Sthompsa struct g_llvm_segment *sg; 196176417Sthompsa struct bio *cb; 197176417Sthompsa struct bio_queue_head bq; 198176417Sthompsa size_t chunk_size; 199176417Sthompsa off_t offset, length; 200176417Sthompsa char *addr; 201176417Sthompsa u_int count; 202176417Sthompsa 203176417Sthompsa pp = bp->bio_to; 204176417Sthompsa lv = pp->private; 205176417Sthompsa vg = pp->geom->softc; 206176417Sthompsa 207176417Sthompsa switch (bp->bio_cmd) { 208176417Sthompsa case BIO_READ: 209176417Sthompsa case BIO_WRITE: 210176417Sthompsa case BIO_DELETE: 211176417Sthompsa /* XXX BIO_GETATTR allowed? */ 212176417Sthompsa break; 213176417Sthompsa default: 214176417Sthompsa g_io_deliver(bp, EOPNOTSUPP); 215176417Sthompsa return; 216176417Sthompsa } 217176417Sthompsa 218176417Sthompsa bioq_init(&bq); 219176417Sthompsa 220176417Sthompsa chunk_size = vg->vg_extentsize; 221176417Sthompsa addr = bp->bio_data; 222176417Sthompsa offset = bp->bio_offset; /* virtual offset and length */ 223176417Sthompsa length = bp->bio_length; 224176417Sthompsa 225176417Sthompsa while (length > 0) { 226176417Sthompsa size_t chunk_index, in_chunk_offset, in_chunk_length; 227176417Sthompsa 228176417Sthompsa pv = NULL; 229176417Sthompsa cb = g_clone_bio(bp); 230176417Sthompsa if (cb == NULL) { 231176417Sthompsa bioq_dismantle(&bq); 232176417Sthompsa if (bp->bio_error == 0) 233176417Sthompsa bp->bio_error = ENOMEM; 234176417Sthompsa g_io_deliver(bp, bp->bio_error); 235176417Sthompsa return; 236176417Sthompsa } 237176417Sthompsa 238176417Sthompsa /* get the segment and the pv */ 239176417Sthompsa if (lv->lv_sgcount == 1) { 240176417Sthompsa /* skip much of the calculations for a single sg */ 241176417Sthompsa chunk_index = 0; 242176417Sthompsa in_chunk_offset = 0; 243176417Sthompsa in_chunk_length = length; 244176417Sthompsa sg = lv->lv_firstsg; 245176417Sthompsa pv = sg->sg_pv; 246176417Sthompsa cb->bio_offset = offset + sg->sg_pvoffset; 247176417Sthompsa } else { 248176417Sthompsa chunk_index = offset / chunk_size; /* round downwards */ 249176417Sthompsa in_chunk_offset = offset % chunk_size; 250176417Sthompsa in_chunk_length = 251176417Sthompsa min(length, chunk_size - in_chunk_offset); 252176417Sthompsa 253176417Sthompsa /* XXX could be faster */ 254176417Sthompsa LIST_FOREACH(sg, &lv->lv_segs, sg_next) { 255176417Sthompsa if (chunk_index >= sg->sg_start && 256176417Sthompsa chunk_index <= sg->sg_end) { 257176417Sthompsa /* adjust chunk index for sg start */ 258176417Sthompsa chunk_index -= sg->sg_start; 259176417Sthompsa pv = sg->sg_pv; 260176417Sthompsa break; 261176417Sthompsa } 262176417Sthompsa } 263176417Sthompsa cb->bio_offset = 264176417Sthompsa (off_t)chunk_index * (off_t)chunk_size 265176417Sthompsa + in_chunk_offset + sg->sg_pvoffset; 266176417Sthompsa } 267176417Sthompsa 268176417Sthompsa KASSERT(pv != NULL, ("Can't find PV for chunk %zu", 269176417Sthompsa chunk_index)); 270176417Sthompsa 271176417Sthompsa cb->bio_to = pv->pv_gprov; 272176417Sthompsa cb->bio_done = g_llvm_done; 273176417Sthompsa cb->bio_length = in_chunk_length; 274176417Sthompsa cb->bio_data = addr; 275176417Sthompsa cb->bio_caller1 = pv; 276176417Sthompsa bioq_disksort(&bq, cb); 277176417Sthompsa 278176417Sthompsa G_LLVM_DEBUG(5, 279176417Sthompsa "Mapped %s(%ju, %ju) on %s to %zu(%zu,%zu) @ %s:%ju", 280176417Sthompsa bp->bio_cmd == BIO_READ ? "R" : "W", 281176417Sthompsa offset, length, lv->lv_name, 282176417Sthompsa chunk_index, in_chunk_offset, in_chunk_length, 283176417Sthompsa pv->pv_name, cb->bio_offset); 284176417Sthompsa 285176417Sthompsa addr += in_chunk_length; 286176417Sthompsa length -= in_chunk_length; 287176417Sthompsa offset += in_chunk_length; 288176417Sthompsa } 289176417Sthompsa 290176417Sthompsa /* Fire off bio's here */ 291176417Sthompsa count = 0; 292176417Sthompsa for (cb = bioq_first(&bq); cb != NULL; cb = bioq_first(&bq)) { 293176417Sthompsa bioq_remove(&bq, cb); 294176417Sthompsa pv = cb->bio_caller1; 295176417Sthompsa cb->bio_caller1 = NULL; 296176417Sthompsa G_LLVM_DEBUG(6, "firing bio to %s, offset=%ju, length=%ju", 297176417Sthompsa cb->bio_to->name, cb->bio_offset, cb->bio_length); 298176417Sthompsa g_io_request(cb, pv->pv_gcons); 299176417Sthompsa count++; 300176417Sthompsa } 301176417Sthompsa if (count == 0) { /* We handled everything locally */ 302176417Sthompsa bp->bio_completed = bp->bio_length; 303176417Sthompsa g_io_deliver(bp, 0); 304176417Sthompsa } 305176417Sthompsa} 306176417Sthompsa 307176417Sthompsastatic void 308176417Sthompsag_llvm_remove_disk(struct g_llvm_vg *vg, struct g_consumer *cp) 309176417Sthompsa{ 310176417Sthompsa struct g_llvm_pv *pv; 311176417Sthompsa struct g_llvm_lv *lv; 312176417Sthompsa struct g_llvm_segment *sg; 313176417Sthompsa int found; 314176417Sthompsa 315176417Sthompsa KASSERT(cp != NULL, ("Non-valid disk in %s.", __func__)); 316176417Sthompsa pv = (struct g_llvm_pv *)cp->private; 317176417Sthompsa 318176417Sthompsa G_LLVM_DEBUG(0, "Disk %s removed from %s.", cp->provider->name, 319176417Sthompsa pv->pv_name); 320176417Sthompsa 321176417Sthompsa LIST_FOREACH(lv, &vg->vg_lvs, lv_next) { 322176417Sthompsa /* Find segments that map to this disk */ 323176417Sthompsa found = 0; 324176417Sthompsa LIST_FOREACH(sg, &lv->lv_segs, sg_next) { 325176417Sthompsa if (sg->sg_pv == pv) { 326176417Sthompsa sg->sg_pv = NULL; 327176417Sthompsa lv->lv_sgactive--; 328176417Sthompsa found = 1; 329176417Sthompsa break; 330176417Sthompsa } 331176417Sthompsa } 332176417Sthompsa if (found) { 333176417Sthompsa G_LLVM_DEBUG(0, "Device %s removed.", 334176417Sthompsa lv->lv_gprov->name); 335176417Sthompsa g_orphan_provider(lv->lv_gprov, ENXIO); 336176417Sthompsa lv->lv_gprov = NULL; 337176417Sthompsa } 338176417Sthompsa } 339176417Sthompsa 340176417Sthompsa if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 341176417Sthompsa g_access(cp, -cp->acr, -cp->acw, -cp->ace); 342176417Sthompsa g_detach(cp); 343176417Sthompsa g_destroy_consumer(cp); 344176417Sthompsa} 345176417Sthompsa 346176417Sthompsastatic void 347176417Sthompsag_llvm_orphan(struct g_consumer *cp) 348176417Sthompsa{ 349176417Sthompsa struct g_llvm_vg *vg; 350176417Sthompsa struct g_geom *gp; 351176417Sthompsa 352176417Sthompsa g_topology_assert(); 353176417Sthompsa gp = cp->geom; 354176417Sthompsa vg = gp->softc; 355176417Sthompsa if (vg == NULL) 356176417Sthompsa return; 357176417Sthompsa 358176417Sthompsa g_llvm_remove_disk(vg, cp); 359176417Sthompsa g_llvm_destroy(vg, 1); 360176417Sthompsa} 361176417Sthompsa 362176417Sthompsastatic int 363176417Sthompsag_llvm_activate_lv(struct g_llvm_vg *vg, struct g_llvm_lv *lv) 364176417Sthompsa{ 365176417Sthompsa struct g_geom *gp; 366176417Sthompsa struct g_provider *pp; 367176417Sthompsa 368176417Sthompsa g_topology_assert(); 369176417Sthompsa 370176417Sthompsa KASSERT(lv->lv_sgactive == lv->lv_sgcount, ("segment missing")); 371176417Sthompsa 372176417Sthompsa gp = vg->vg_geom; 373176417Sthompsa pp = g_new_providerf(gp, "linux_lvm/%s-%s", vg->vg_name, lv->lv_name); 374176417Sthompsa pp->mediasize = vg->vg_extentsize * (off_t)lv->lv_extentcount; 375176417Sthompsa pp->sectorsize = vg->vg_sectorsize; 376176417Sthompsa g_error_provider(pp, 0); 377176417Sthompsa lv->lv_gprov = pp; 378176417Sthompsa pp->private = lv; 379176417Sthompsa 380176417Sthompsa G_LLVM_DEBUG(1, "Created %s, %juM", pp->name, 381176417Sthompsa pp->mediasize / (1024*1024)); 382176417Sthompsa 383176417Sthompsa return (0); 384176417Sthompsa} 385176417Sthompsa 386176417Sthompsastatic int 387176417Sthompsag_llvm_add_disk(struct g_llvm_vg *vg, struct g_provider *pp, char *uuid) 388176417Sthompsa{ 389176417Sthompsa struct g_geom *gp; 390176417Sthompsa struct g_consumer *cp, *fcp; 391176417Sthompsa struct g_llvm_pv *pv; 392176417Sthompsa struct g_llvm_lv *lv; 393176417Sthompsa struct g_llvm_segment *sg; 394176417Sthompsa int error; 395176417Sthompsa 396176417Sthompsa g_topology_assert(); 397176417Sthompsa 398176417Sthompsa LIST_FOREACH(pv, &vg->vg_pvs, pv_next) { 399176417Sthompsa if (strcmp(pv->pv_uuid, uuid) == 0) 400176417Sthompsa break; /* found it */ 401176417Sthompsa } 402176417Sthompsa if (pv == NULL) { 403176417Sthompsa G_LLVM_DEBUG(3, "uuid %s not found in pv list", uuid); 404176417Sthompsa return (ENOENT); 405176417Sthompsa } 406176417Sthompsa if (pv->pv_gprov != NULL) { 407176417Sthompsa G_LLVM_DEBUG(0, "disk %s already initialised in %s", 408176417Sthompsa pv->pv_name, vg->vg_name); 409176417Sthompsa return (EEXIST); 410176417Sthompsa } 411176417Sthompsa 412176417Sthompsa pv->pv_start *= vg->vg_sectorsize; 413176417Sthompsa gp = vg->vg_geom; 414176417Sthompsa fcp = LIST_FIRST(&gp->consumer); 415176417Sthompsa 416176417Sthompsa cp = g_new_consumer(gp); 417176417Sthompsa error = g_attach(cp, pp); 418176417Sthompsa G_LLVM_DEBUG(1, "Attached %s to %s at offset %ju", 419176417Sthompsa pp->name, pv->pv_name, pv->pv_start); 420176417Sthompsa 421176417Sthompsa if (error != 0) { 422176417Sthompsa G_LLVM_DEBUG(0, "cannot attach %s to %s", 423176417Sthompsa pp->name, vg->vg_name); 424176417Sthompsa g_destroy_consumer(cp); 425176417Sthompsa return (error); 426176417Sthompsa } 427176417Sthompsa 428176417Sthompsa if (fcp != NULL) { 429176417Sthompsa if (fcp->provider->sectorsize != pp->sectorsize) { 430176417Sthompsa G_LLVM_DEBUG(0, "Provider %s of %s has invalid " 431176417Sthompsa "sector size (%d)", pp->name, vg->vg_name, 432176417Sthompsa pp->sectorsize); 433176417Sthompsa return (EINVAL); 434176417Sthompsa } 435176417Sthompsa if (fcp->acr > 0 || fcp->acw || fcp->ace > 0) { 436176417Sthompsa /* Replicate access permissions from first "live" 437176417Sthompsa * consumer to the new one */ 438176417Sthompsa error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 439176417Sthompsa if (error != 0) { 440176417Sthompsa g_detach(cp); 441176417Sthompsa g_destroy_consumer(cp); 442176417Sthompsa return (error); 443176417Sthompsa } 444176417Sthompsa } 445176417Sthompsa } 446176417Sthompsa 447176417Sthompsa cp->private = pv; 448176417Sthompsa pv->pv_gcons = cp; 449176417Sthompsa pv->pv_gprov = pp; 450176417Sthompsa 451176417Sthompsa LIST_FOREACH(lv, &vg->vg_lvs, lv_next) { 452176417Sthompsa /* Find segments that map to this disk */ 453176417Sthompsa LIST_FOREACH(sg, &lv->lv_segs, sg_next) { 454176417Sthompsa if (strcmp(sg->sg_pvname, pv->pv_name) == 0) { 455176417Sthompsa /* avtivate the segment */ 456176417Sthompsa KASSERT(sg->sg_pv == NULL, 457176417Sthompsa ("segment already mapped")); 458176417Sthompsa sg->sg_pvoffset = 459176417Sthompsa (off_t)sg->sg_pvstart * vg->vg_extentsize 460176417Sthompsa + pv->pv_start; 461176417Sthompsa sg->sg_pv = pv; 462176417Sthompsa lv->lv_sgactive++; 463176417Sthompsa 464176417Sthompsa G_LLVM_DEBUG(2, "%s: %d to %d @ %s:%d" 465176417Sthompsa " offset %ju sector %ju", 466176417Sthompsa lv->lv_name, sg->sg_start, sg->sg_end, 467176417Sthompsa sg->sg_pvname, sg->sg_pvstart, 468176417Sthompsa sg->sg_pvoffset, 469176417Sthompsa sg->sg_pvoffset / vg->vg_sectorsize); 470176417Sthompsa } 471176417Sthompsa } 472176417Sthompsa /* Activate any lvs waiting on this disk */ 473176417Sthompsa if (lv->lv_gprov == NULL && lv->lv_sgactive == lv->lv_sgcount) { 474176417Sthompsa error = g_llvm_activate_lv(vg, lv); 475176417Sthompsa if (error) 476176417Sthompsa break; 477176417Sthompsa } 478176417Sthompsa } 479176417Sthompsa return (error); 480176417Sthompsa} 481176417Sthompsa 482176417Sthompsastatic void 483176417Sthompsag_llvm_init(struct g_class *mp) 484176417Sthompsa{ 485176417Sthompsa LIST_INIT(&vg_list); 486176417Sthompsa} 487176417Sthompsa 488176417Sthompsastatic void 489176417Sthompsag_llvm_free_vg(struct g_llvm_vg *vg) 490176417Sthompsa{ 491176417Sthompsa struct g_llvm_pv *pv; 492176417Sthompsa struct g_llvm_lv *lv; 493176417Sthompsa struct g_llvm_segment *sg; 494176417Sthompsa 495176417Sthompsa /* Free all the structures */ 496176417Sthompsa while ((pv = LIST_FIRST(&vg->vg_pvs)) != NULL) { 497176417Sthompsa LIST_REMOVE(pv, pv_next); 498176417Sthompsa free(pv, M_GLLVM); 499176417Sthompsa } 500176417Sthompsa while ((lv = LIST_FIRST(&vg->vg_lvs)) != NULL) { 501176417Sthompsa while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) { 502176417Sthompsa LIST_REMOVE(sg, sg_next); 503176417Sthompsa free(sg, M_GLLVM); 504176417Sthompsa } 505176417Sthompsa LIST_REMOVE(lv, lv_next); 506176417Sthompsa free(lv, M_GLLVM); 507176417Sthompsa } 508176417Sthompsa LIST_REMOVE(vg, vg_next); 509176417Sthompsa free(vg, M_GLLVM); 510176417Sthompsa} 511176417Sthompsa 512176417Sthompsastatic void 513176417Sthompsag_llvm_taste_orphan(struct g_consumer *cp) 514176417Sthompsa{ 515176417Sthompsa 516176417Sthompsa KASSERT(1 == 0, ("%s called while tasting %s.", __func__, 517176417Sthompsa cp->provider->name)); 518176417Sthompsa} 519176417Sthompsa 520176417Sthompsastatic struct g_geom * 521176417Sthompsag_llvm_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 522176417Sthompsa{ 523176417Sthompsa struct g_consumer *cp; 524176417Sthompsa struct g_geom *gp; 525176417Sthompsa struct g_llvm_label ll; 526176417Sthompsa struct g_llvm_metadata md; 527176417Sthompsa struct g_llvm_vg *vg; 528176417Sthompsa int error; 529176417Sthompsa 530176417Sthompsa bzero(&md, sizeof(md)); 531176417Sthompsa 532176417Sthompsa g_topology_assert(); 533176417Sthompsa g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 534176417Sthompsa gp = g_new_geomf(mp, "linux_lvm:taste"); 535176417Sthompsa /* This orphan function should be never called. */ 536176417Sthompsa gp->orphan = g_llvm_taste_orphan; 537176417Sthompsa cp = g_new_consumer(gp); 538176417Sthompsa g_attach(cp, pp); 539176417Sthompsa error = g_llvm_read_label(cp, &ll); 540176417Sthompsa if (!error) 541176417Sthompsa error = g_llvm_read_md(cp, &md, &ll); 542176417Sthompsa g_detach(cp); 543176417Sthompsa g_destroy_consumer(cp); 544176417Sthompsa g_destroy_geom(gp); 545176417Sthompsa if (error != 0) 546176417Sthompsa return (NULL); 547176417Sthompsa 548176417Sthompsa vg = md.md_vg; 549176417Sthompsa if (vg->vg_geom == NULL) { 550176417Sthompsa /* new volume group */ 551176417Sthompsa gp = g_new_geomf(mp, "%s", vg->vg_name); 552176417Sthompsa gp->start = g_llvm_start; 553176417Sthompsa gp->spoiled = g_llvm_orphan; 554176417Sthompsa gp->orphan = g_llvm_orphan; 555176417Sthompsa gp->access = g_llvm_access; 556176417Sthompsa vg->vg_sectorsize = pp->sectorsize; 557176417Sthompsa vg->vg_extentsize *= vg->vg_sectorsize; 558176417Sthompsa vg->vg_geom = gp; 559176417Sthompsa gp->softc = vg; 560176417Sthompsa G_LLVM_DEBUG(1, "Created volume %s, extent size %zuK", 561176417Sthompsa vg->vg_name, vg->vg_extentsize / 1024); 562176417Sthompsa } 563176417Sthompsa 564176417Sthompsa /* initialise this disk in the volume group */ 565176417Sthompsa g_llvm_add_disk(vg, pp, ll.ll_uuid); 566176417Sthompsa return (vg->vg_geom); 567176417Sthompsa} 568176417Sthompsa 569176417Sthompsastatic int 570176417Sthompsag_llvm_destroy(struct g_llvm_vg *vg, int force) 571176417Sthompsa{ 572176417Sthompsa struct g_provider *pp; 573176417Sthompsa struct g_geom *gp; 574176417Sthompsa 575176417Sthompsa g_topology_assert(); 576176417Sthompsa if (vg == NULL) 577176417Sthompsa return (ENXIO); 578176417Sthompsa gp = vg->vg_geom; 579176417Sthompsa 580176417Sthompsa LIST_FOREACH(pp, &gp->provider, provider) { 581176417Sthompsa if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) { 582176417Sthompsa G_LLVM_DEBUG(1, "Device %s is still open (r%dw%de%d)", 583176417Sthompsa pp->name, pp->acr, pp->acw, pp->ace); 584176417Sthompsa if (!force) 585176417Sthompsa return (EBUSY); 586176417Sthompsa } 587176417Sthompsa } 588176417Sthompsa 589176417Sthompsa g_llvm_free_vg(gp->softc); 590176417Sthompsa gp->softc = NULL; 591176417Sthompsa g_wither_geom(gp, ENXIO); 592176417Sthompsa return (0); 593176417Sthompsa} 594176417Sthompsa 595176417Sthompsastatic int 596176417Sthompsag_llvm_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused, 597176417Sthompsa struct g_geom *gp) 598176417Sthompsa{ 599176417Sthompsa struct g_llvm_vg *vg; 600176417Sthompsa 601176417Sthompsa vg = gp->softc; 602176417Sthompsa return (g_llvm_destroy(vg, 0)); 603176417Sthompsa} 604176417Sthompsa 605176417Sthompsaint 606176417Sthompsag_llvm_read_label(struct g_consumer *cp, struct g_llvm_label *ll) 607176417Sthompsa{ 608176417Sthompsa struct g_provider *pp; 609176417Sthompsa u_char *buf; 610176417Sthompsa int i, error = 0; 611176417Sthompsa 612176417Sthompsa g_topology_assert(); 613176417Sthompsa 614176417Sthompsa /* The LVM label is stored on the first four sectors */ 615176417Sthompsa error = g_access(cp, 1, 0, 0); 616176417Sthompsa if (error != 0) 617176417Sthompsa return (error); 618176417Sthompsa pp = cp->provider; 619176417Sthompsa g_topology_unlock(); 620176417Sthompsa buf = g_read_data(cp, 0, pp->sectorsize * 4, &error); 621176417Sthompsa g_topology_lock(); 622176417Sthompsa g_access(cp, -1, 0, 0); 623176417Sthompsa if (buf == NULL) { 624176417Sthompsa G_LLVM_DEBUG(1, "Cannot read metadata from %s (error=%d)", 625176417Sthompsa pp->name, error); 626176417Sthompsa return (error); 627176417Sthompsa } 628176417Sthompsa 629176417Sthompsa /* Search the four sectors for the LVM label. */ 630176417Sthompsa for (i = 0; i < 4; i++) { 631176417Sthompsa error = llvm_label_decode(&buf[i * pp->sectorsize], ll, i); 632176417Sthompsa if (error == 0) 633176417Sthompsa break; /* found it */ 634176417Sthompsa } 635176417Sthompsa g_free(buf); 636176417Sthompsa return (error); 637176417Sthompsa} 638176417Sthompsa 639176417Sthompsaint 640176417Sthompsag_llvm_read_md(struct g_consumer *cp, struct g_llvm_metadata *md, 641176417Sthompsa struct g_llvm_label *ll) 642176417Sthompsa{ 643176417Sthompsa struct g_provider *pp; 644176417Sthompsa u_char *buf; 645176417Sthompsa int error; 646176417Sthompsa int size; 647176417Sthompsa 648176417Sthompsa g_topology_assert(); 649176417Sthompsa 650176417Sthompsa error = g_access(cp, 1, 0, 0); 651176417Sthompsa if (error != 0) 652176417Sthompsa return (error); 653176417Sthompsa pp = cp->provider; 654176417Sthompsa g_topology_unlock(); 655176417Sthompsa buf = g_read_data(cp, ll->ll_md_offset, pp->sectorsize, &error); 656176417Sthompsa g_topology_lock(); 657176417Sthompsa g_access(cp, -1, 0, 0); 658176417Sthompsa if (buf == NULL) { 659176417Sthompsa G_LLVM_DEBUG(0, "Cannot read metadata from %s (error=%d)", 660176417Sthompsa cp->provider->name, error); 661176417Sthompsa return (error); 662176417Sthompsa } 663176417Sthompsa 664176417Sthompsa error = llvm_md_decode(buf, md, ll); 665176417Sthompsa g_free(buf); 666176417Sthompsa if (error != 0) { 667176417Sthompsa return (error); 668176417Sthompsa } 669176417Sthompsa 670176417Sthompsa G_LLVM_DEBUG(1, "reading LVM2 config @ %s:%ju", pp->name, 671176417Sthompsa ll->ll_md_offset + md->md_reloffset); 672176417Sthompsa error = g_access(cp, 1, 0, 0); 673176417Sthompsa if (error != 0) 674176417Sthompsa return (error); 675176417Sthompsa pp = cp->provider; 676176417Sthompsa g_topology_unlock(); 677176417Sthompsa /* round up to the nearest sector */ 678176417Sthompsa size = md->md_relsize + 679176417Sthompsa (pp->sectorsize - md->md_relsize % pp->sectorsize); 680176417Sthompsa buf = g_read_data(cp, ll->ll_md_offset + md->md_reloffset, size, &error); 681176417Sthompsa g_topology_lock(); 682176417Sthompsa g_access(cp, -1, 0, 0); 683176417Sthompsa if (buf == NULL) { 684176417Sthompsa G_LLVM_DEBUG(0, "Cannot read LVM2 config from %s (error=%d)", 685176417Sthompsa pp->name, error); 686176417Sthompsa return (error); 687176417Sthompsa } 688176417Sthompsa buf[md->md_relsize] = '\0'; 689176417Sthompsa G_LLVM_DEBUG(10, "LVM config:\n%s\n", buf); 690176417Sthompsa error = llvm_textconf_decode(buf, md->md_relsize, md); 691176417Sthompsa g_free(buf); 692176417Sthompsa 693176417Sthompsa return (error); 694176417Sthompsa} 695176417Sthompsa 696176417Sthompsastatic int 697176417Sthompsallvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector) 698176417Sthompsa{ 699176417Sthompsa uint64_t off; 700176417Sthompsa char *uuid; 701176417Sthompsa 702176417Sthompsa /* Magic string */ 703176417Sthompsa if (bcmp("LABELONE", data , 8) != 0) 704176417Sthompsa return (EINVAL); 705176417Sthompsa 706176417Sthompsa /* We only support LVM2 text format */ 707176417Sthompsa if (bcmp("LVM2 001", data + 24, 8) != 0) { 708176417Sthompsa G_LLVM_DEBUG(0, "Unsupported LVM format"); 709176417Sthompsa return (EINVAL); 710176417Sthompsa } 711176417Sthompsa 712176417Sthompsa ll->ll_sector = le64dec(data + 8); 713176417Sthompsa ll->ll_crc = le32dec(data + 16); 714176417Sthompsa ll->ll_offset = le32dec(data + 20); 715176417Sthompsa 716176417Sthompsa if (ll->ll_sector != sector) { 717176417Sthompsa G_LLVM_DEBUG(0, "Expected sector %ju, found at %d", 718176417Sthompsa ll->ll_sector, sector); 719176417Sthompsa return (EINVAL); 720176417Sthompsa } 721176417Sthompsa 722176417Sthompsa off = ll->ll_offset; 723176417Sthompsa /* 724176417Sthompsa * convert the binary uuid to string format, the format is 725176417Sthompsa * xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx (6-4-4-4-4-4-6) 726176417Sthompsa */ 727176417Sthompsa uuid = ll->ll_uuid; 728176417Sthompsa bcopy(data + off, uuid, 6); 729176417Sthompsa off += 6; 730176417Sthompsa uuid += 6; 731176417Sthompsa *uuid++ = '-'; 732176417Sthompsa for (int i = 0; i < 5; i++) { 733176417Sthompsa bcopy(data + off, uuid, 4); 734176417Sthompsa off += 4; 735176417Sthompsa uuid += 4; 736176417Sthompsa *uuid++ = '-'; 737176417Sthompsa } 738176417Sthompsa bcopy(data + off, uuid, 6); 739176417Sthompsa off += 6; 740176417Sthompsa uuid += 6; 741176417Sthompsa *uuid++ = '\0'; 742176417Sthompsa 743176417Sthompsa ll->ll_size = le64dec(data + off); 744176417Sthompsa off += 8; 745176417Sthompsa ll->ll_pestart = le64dec(data + off); 746176417Sthompsa off += 16; 747176417Sthompsa 748176417Sthompsa /* Only one data section is supported */ 749176417Sthompsa if (le64dec(data + off) != 0) { 750176417Sthompsa G_LLVM_DEBUG(0, "Only one data section supported"); 751176417Sthompsa return (EINVAL); 752176417Sthompsa } 753176417Sthompsa 754176417Sthompsa off += 16; 755176417Sthompsa ll->ll_md_offset = le64dec(data + off); 756176417Sthompsa off += 8; 757176417Sthompsa ll->ll_md_size = le64dec(data + off); 758176417Sthompsa off += 8; 759176417Sthompsa 760176417Sthompsa G_LLVM_DEBUG(1, "LVM metadata: offset=%ju, size=%ju", ll->ll_md_offset, 761176417Sthompsa ll->ll_md_size); 762176417Sthompsa 763176417Sthompsa /* Only one data section is supported */ 764176417Sthompsa if (le64dec(data + off) != 0) { 765176417Sthompsa G_LLVM_DEBUG(0, "Only one metadata section supported"); 766176417Sthompsa return (EINVAL); 767176417Sthompsa } 768176417Sthompsa 769176417Sthompsa G_LLVM_DEBUG(2, "label uuid=%s", ll->ll_uuid); 770176417Sthompsa G_LLVM_DEBUG(2, "sector=%ju, crc=%u, offset=%u, size=%ju, pestart=%ju", 771176417Sthompsa ll->ll_sector, ll->ll_crc, ll->ll_offset, ll->ll_size, 772176417Sthompsa ll->ll_pestart); 773176417Sthompsa 774176417Sthompsa return (0); 775176417Sthompsa} 776176417Sthompsa 777176417Sthompsastatic int 778176417Sthompsallvm_md_decode(const u_char *data, struct g_llvm_metadata *md, 779176417Sthompsa struct g_llvm_label *ll) 780176417Sthompsa{ 781176417Sthompsa uint64_t off; 782176417Sthompsa char magic[16]; 783176417Sthompsa 784176417Sthompsa off = 0; 785176417Sthompsa md->md_csum = le32dec(data + off); 786176417Sthompsa off += 4; 787176417Sthompsa bcopy(data + off, magic, 16); 788176417Sthompsa off += 16; 789176417Sthompsa md->md_version = le32dec(data + off); 790176417Sthompsa off += 4; 791176417Sthompsa md->md_start = le64dec(data + off); 792176417Sthompsa off += 8; 793176417Sthompsa md->md_size = le64dec(data + off); 794176417Sthompsa off += 8; 795176417Sthompsa 796176417Sthompsa if (bcmp(G_LLVM_MAGIC, magic, 16) != 0) { 797176417Sthompsa G_LLVM_DEBUG(0, "Incorrect md magic number"); 798176417Sthompsa return (EINVAL); 799176417Sthompsa } 800176417Sthompsa if (md->md_version != 1) { 801176417Sthompsa G_LLVM_DEBUG(0, "Incorrect md version number (%u)", 802176417Sthompsa md->md_version); 803176417Sthompsa return (EINVAL); 804176417Sthompsa } 805176417Sthompsa if (md->md_start != ll->ll_md_offset) { 806176417Sthompsa G_LLVM_DEBUG(0, "Incorrect md offset (%ju)", md->md_start); 807176417Sthompsa return (EINVAL); 808176417Sthompsa } 809176417Sthompsa 810176417Sthompsa /* Aparently only one is ever returned */ 811176417Sthompsa md->md_reloffset = le64dec(data + off); 812176417Sthompsa off += 8; 813176417Sthompsa md->md_relsize = le64dec(data + off); 814176417Sthompsa off += 16; /* XXX skipped checksum */ 815176417Sthompsa 816176417Sthompsa if (le64dec(data + off) != 0) { 817176417Sthompsa G_LLVM_DEBUG(0, "Only one reloc supported"); 818176417Sthompsa return (EINVAL); 819176417Sthompsa } 820176417Sthompsa 821176417Sthompsa G_LLVM_DEBUG(3, "reloc: offset=%ju, size=%ju", 822176417Sthompsa md->md_reloffset, md->md_relsize); 823176417Sthompsa G_LLVM_DEBUG(3, "md: version=%u, start=%ju, size=%ju", 824176417Sthompsa md->md_version, md->md_start, md->md_size); 825176417Sthompsa 826176417Sthompsa return (0); 827176417Sthompsa} 828176417Sthompsa 829176417Sthompsa#define GRAB_NAME(tok, name, len) \ 830176417Sthompsa len = 0; \ 831176417Sthompsa while (tok[len] && (isalpha(tok[len]) || isdigit(tok[len])) && \ 832176417Sthompsa len < G_LLVM_NAMELEN - 1) \ 833176417Sthompsa len++; \ 834176417Sthompsa bcopy(tok, name, len); \ 835176417Sthompsa name[len] = '\0'; 836176417Sthompsa 837176417Sthompsa#define GRAB_INT(key, tok1, tok2, v) \ 838176417Sthompsa if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) { \ 839176417Sthompsa v = strtol(tok2, &tok1, 10); \ 840176417Sthompsa if (tok1 == tok2) \ 841176417Sthompsa /* strtol did not eat any of the buffer */ \ 842176417Sthompsa goto bad; \ 843176417Sthompsa continue; \ 844176417Sthompsa } 845176417Sthompsa 846176417Sthompsa#define GRAB_STR(key, tok1, tok2, v, len) \ 847176417Sthompsa if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) { \ 848176417Sthompsa strsep(&tok2, "\""); \ 849176417Sthompsa if (tok2 == NULL) \ 850176417Sthompsa continue; \ 851176417Sthompsa tok1 = strsep(&tok2, "\""); \ 852176417Sthompsa if (tok2 == NULL) \ 853176417Sthompsa continue; \ 854176417Sthompsa strncpy(v, tok1, len); \ 855176417Sthompsa continue; \ 856176417Sthompsa } 857176417Sthompsa 858176417Sthompsa#define SPLIT(key, value, str) \ 859176417Sthompsa key = strsep(&value, str); \ 860176417Sthompsa /* strip trailing whitespace on the key */ \ 861176417Sthompsa for (char *t = key; *t != '\0'; t++) \ 862176417Sthompsa if (isspace(*t)) { \ 863176417Sthompsa *t = '\0'; \ 864176417Sthompsa break; \ 865176417Sthompsa } 866176417Sthompsa 867176417Sthompsastatic int 868176417Sthompsallvm_textconf_decode(u_char *data, int buflen, struct g_llvm_metadata *md) 869176417Sthompsa{ 870176417Sthompsa struct g_llvm_vg *vg; 871176417Sthompsa char *buf = data; 872176417Sthompsa char *tok, *v; 873176417Sthompsa char name[G_LLVM_NAMELEN]; 874176417Sthompsa char uuid[G_LLVM_UUIDLEN]; 875176417Sthompsa int len; 876176417Sthompsa 877176417Sthompsa if (buf == NULL || *buf == '\0') 878176417Sthompsa return (EINVAL); 879176417Sthompsa 880176417Sthompsa tok = strsep(&buf, "\n"); 881176417Sthompsa if (tok == NULL) 882176417Sthompsa return (EINVAL); 883176417Sthompsa GRAB_NAME(tok, name, len); 884176417Sthompsa if (len == 0) 885176417Sthompsa return (EINVAL); 886176417Sthompsa 887176417Sthompsa /* check too see if the vg has already been loaded off another disk */ 888176417Sthompsa LIST_FOREACH(vg, &vg_list, vg_next) { 889176417Sthompsa if (strcmp(vg->vg_name, name) == 0) { 890176417Sthompsa uuid[0] = '\0'; 891176417Sthompsa /* grab the volume group uuid */ 892176417Sthompsa while ((tok = strsep(&buf, "\n")) != NULL) { 893176417Sthompsa if (strstr(tok, "{")) 894176417Sthompsa break; 895176417Sthompsa if (strstr(tok, "=")) { 896176417Sthompsa SPLIT(v, tok, "="); 897176417Sthompsa GRAB_STR("id", v, tok, uuid, 898176417Sthompsa sizeof(uuid)); 899176417Sthompsa } 900176417Sthompsa } 901176417Sthompsa if (strcmp(vg->vg_uuid, uuid) == 0) { 902176417Sthompsa /* existing vg */ 903176417Sthompsa md->md_vg = vg; 904176417Sthompsa return (0); 905176417Sthompsa } 906176417Sthompsa /* XXX different volume group with name clash! */ 907176417Sthompsa G_LLVM_DEBUG(0, 908176417Sthompsa "%s already exists, volume group not loaded", name); 909176417Sthompsa return (EINVAL); 910176417Sthompsa } 911176417Sthompsa } 912176417Sthompsa 913176417Sthompsa vg = malloc(sizeof(*vg), M_GLLVM, M_NOWAIT|M_ZERO); 914176417Sthompsa if (vg == NULL) 915176417Sthompsa return (ENOMEM); 916176417Sthompsa 917176417Sthompsa strncpy(vg->vg_name, name, sizeof(vg->vg_name)); 918176417Sthompsa LIST_INIT(&vg->vg_pvs); 919176417Sthompsa LIST_INIT(&vg->vg_lvs); 920176417Sthompsa 921176417Sthompsa#define VOL_FOREACH(func, tok, buf, p) \ 922176417Sthompsa while ((tok = strsep(buf, "\n")) != NULL) { \ 923176417Sthompsa if (strstr(tok, "{")) { \ 924176417Sthompsa func(buf, tok, p); \ 925176417Sthompsa continue; \ 926176417Sthompsa } \ 927176417Sthompsa if (strstr(tok, "}")) \ 928176417Sthompsa break; \ 929176417Sthompsa } 930176417Sthompsa 931176417Sthompsa while ((tok = strsep(&buf, "\n")) != NULL) { 932176417Sthompsa if (strcmp(tok, "physical_volumes {") == 0) { 933176417Sthompsa VOL_FOREACH(llvm_textconf_decode_pv, tok, &buf, vg); 934176417Sthompsa continue; 935176417Sthompsa } 936176417Sthompsa if (strcmp(tok, "logical_volumes {") == 0) { 937176417Sthompsa VOL_FOREACH(llvm_textconf_decode_lv, tok, &buf, vg); 938176417Sthompsa continue; 939176417Sthompsa } 940176417Sthompsa if (strstr(tok, "{")) { 941176417Sthompsa G_LLVM_DEBUG(2, "unknown section %s", tok); 942176417Sthompsa continue; 943176417Sthompsa } 944176417Sthompsa 945176417Sthompsa /* parse 'key = value' lines */ 946176417Sthompsa if (strstr(tok, "=")) { 947176417Sthompsa SPLIT(v, tok, "="); 948176417Sthompsa GRAB_STR("id", v, tok, vg->vg_uuid, sizeof(vg->vg_uuid)); 949176417Sthompsa GRAB_INT("extent_size", v, tok, vg->vg_extentsize); 950176417Sthompsa continue; 951176417Sthompsa } 952176417Sthompsa } 953176417Sthompsa /* basic checking */ 954176417Sthompsa if (vg->vg_extentsize == 0) 955176417Sthompsa goto bad; 956176417Sthompsa 957176417Sthompsa md->md_vg = vg; 958176417Sthompsa LIST_INSERT_HEAD(&vg_list, vg, vg_next); 959176417Sthompsa G_LLVM_DEBUG(3, "vg: name=%s uuid=%s", vg->vg_name, vg->vg_uuid); 960176417Sthompsa return(0); 961176417Sthompsa 962176417Sthompsabad: 963176417Sthompsa g_llvm_free_vg(vg); 964176417Sthompsa return (-1); 965176417Sthompsa} 966176417Sthompsa#undef VOL_FOREACH 967176417Sthompsa 968176417Sthompsastatic int 969176417Sthompsallvm_textconf_decode_pv(char **buf, char *tok, struct g_llvm_vg *vg) 970176417Sthompsa{ 971176417Sthompsa struct g_llvm_pv *pv; 972176417Sthompsa char *v; 973176417Sthompsa int len; 974176417Sthompsa 975176417Sthompsa if (*buf == NULL || **buf == '\0') 976176417Sthompsa return (EINVAL); 977176417Sthompsa 978176417Sthompsa pv = malloc(sizeof(*pv), M_GLLVM, M_NOWAIT|M_ZERO); 979176417Sthompsa if (pv == NULL) 980176417Sthompsa return (ENOMEM); 981176417Sthompsa 982176417Sthompsa pv->pv_vg = vg; 983176417Sthompsa len = 0; 984176417Sthompsa if (tok == NULL) 985176417Sthompsa goto bad; 986176417Sthompsa GRAB_NAME(tok, pv->pv_name, len); 987176417Sthompsa if (len == 0) 988176417Sthompsa goto bad; 989176417Sthompsa 990176417Sthompsa while ((tok = strsep(buf, "\n")) != NULL) { 991176417Sthompsa if (strstr(tok, "{")) 992176417Sthompsa goto bad; 993176417Sthompsa 994176417Sthompsa if (strstr(tok, "}")) 995176417Sthompsa break; 996176417Sthompsa 997176417Sthompsa /* parse 'key = value' lines */ 998176417Sthompsa if (strstr(tok, "=")) { 999176417Sthompsa SPLIT(v, tok, "="); 1000176417Sthompsa GRAB_STR("id", v, tok, pv->pv_uuid, sizeof(pv->pv_uuid)); 1001176417Sthompsa GRAB_INT("pe_start", v, tok, pv->pv_start); 1002176417Sthompsa GRAB_INT("pe_count", v, tok, pv->pv_count); 1003176417Sthompsa continue; 1004176417Sthompsa } 1005176417Sthompsa } 1006176417Sthompsa if (tok == NULL) 1007176417Sthompsa goto bad; 1008176417Sthompsa /* basic checking */ 1009176417Sthompsa if (pv->pv_count == 0) 1010176417Sthompsa goto bad; 1011176417Sthompsa 1012176417Sthompsa LIST_INSERT_HEAD(&vg->vg_pvs, pv, pv_next); 1013176417Sthompsa G_LLVM_DEBUG(3, "pv: name=%s uuid=%s", pv->pv_name, pv->pv_uuid); 1014176417Sthompsa 1015176417Sthompsa return (0); 1016176417Sthompsabad: 1017176417Sthompsa free(pv, M_GLLVM); 1018176417Sthompsa return (-1); 1019176417Sthompsa} 1020176417Sthompsa 1021176417Sthompsastatic int 1022176417Sthompsallvm_textconf_decode_lv(char **buf, char *tok, struct g_llvm_vg *vg) 1023176417Sthompsa{ 1024176417Sthompsa struct g_llvm_lv *lv; 1025176417Sthompsa struct g_llvm_segment *sg; 1026176417Sthompsa char *v; 1027176417Sthompsa int len; 1028176417Sthompsa 1029176417Sthompsa if (*buf == NULL || **buf == '\0') 1030176417Sthompsa return (EINVAL); 1031176417Sthompsa 1032176417Sthompsa lv = malloc(sizeof(*lv), M_GLLVM, M_NOWAIT|M_ZERO); 1033176417Sthompsa if (lv == NULL) 1034176417Sthompsa return (ENOMEM); 1035176417Sthompsa 1036176417Sthompsa lv->lv_vg = vg; 1037176417Sthompsa LIST_INIT(&lv->lv_segs); 1038176417Sthompsa 1039176417Sthompsa len = 0; 1040176417Sthompsa if (tok == NULL) 1041176417Sthompsa goto bad; 1042176417Sthompsa GRAB_NAME(tok, lv->lv_name, len); 1043176417Sthompsa if (len == 0) 1044176417Sthompsa goto bad; 1045176417Sthompsa 1046176417Sthompsa while ((tok = strsep(buf, "\n")) != NULL) { 1047176417Sthompsa if (strstr(tok, "{")) { 1048176417Sthompsa if (strstr(tok, "segment")) { 1049176417Sthompsa llvm_textconf_decode_sg(buf, tok, lv); 1050176417Sthompsa continue; 1051176417Sthompsa } else 1052176417Sthompsa /* unexpected section */ 1053176417Sthompsa goto bad; 1054176417Sthompsa } 1055176417Sthompsa 1056176417Sthompsa if (strstr(tok, "}")) 1057176417Sthompsa break; 1058176417Sthompsa 1059176417Sthompsa /* parse 'key = value' lines */ 1060176417Sthompsa if (strstr(tok, "=")) { 1061176417Sthompsa SPLIT(v, tok, "="); 1062176417Sthompsa GRAB_STR("id", v, tok, lv->lv_uuid, sizeof(lv->lv_uuid)); 1063176417Sthompsa GRAB_INT("segment_count", v, tok, lv->lv_sgcount); 1064176417Sthompsa continue; 1065176417Sthompsa } 1066176417Sthompsa } 1067176417Sthompsa if (tok == NULL) 1068176417Sthompsa goto bad; 1069176417Sthompsa if (lv->lv_sgcount == 0 || lv->lv_sgcount != lv->lv_numsegs) 1070176417Sthompsa /* zero or incomplete segment list */ 1071176417Sthompsa goto bad; 1072176417Sthompsa 1073176417Sthompsa /* Optimize for only one segment on the pv */ 1074176417Sthompsa lv->lv_firstsg = LIST_FIRST(&lv->lv_segs); 1075176417Sthompsa LIST_INSERT_HEAD(&vg->vg_lvs, lv, lv_next); 1076176417Sthompsa G_LLVM_DEBUG(3, "lv: name=%s uuid=%s", lv->lv_name, lv->lv_uuid); 1077176417Sthompsa 1078176417Sthompsa return (0); 1079176417Sthompsabad: 1080176417Sthompsa while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) { 1081176417Sthompsa LIST_REMOVE(sg, sg_next); 1082176417Sthompsa free(sg, M_GLLVM); 1083176417Sthompsa } 1084176417Sthompsa free(lv, M_GLLVM); 1085176417Sthompsa return (-1); 1086176417Sthompsa} 1087176417Sthompsa 1088176417Sthompsastatic int 1089176417Sthompsallvm_textconf_decode_sg(char **buf, char *tok, struct g_llvm_lv *lv) 1090176417Sthompsa{ 1091176417Sthompsa struct g_llvm_segment *sg; 1092176417Sthompsa char *v; 1093176417Sthompsa int count = 0; 1094176417Sthompsa 1095176417Sthompsa if (*buf == NULL || **buf == '\0') 1096176417Sthompsa return (EINVAL); 1097176417Sthompsa 1098176417Sthompsa sg = malloc(sizeof(*sg), M_GLLVM, M_NOWAIT|M_ZERO); 1099176417Sthompsa if (sg == NULL) 1100176417Sthompsa return (ENOMEM); 1101176417Sthompsa 1102176417Sthompsa while ((tok = strsep(buf, "\n")) != NULL) { 1103176417Sthompsa /* only a single linear stripe is supported */ 1104176417Sthompsa if (strstr(tok, "stripe_count")) { 1105176417Sthompsa SPLIT(v, tok, "="); 1106176417Sthompsa GRAB_INT("stripe_count", v, tok, count); 1107176417Sthompsa if (count != 1) 1108176417Sthompsa goto bad; 1109176417Sthompsa } 1110176417Sthompsa 1111176417Sthompsa if (strstr(tok, "{")) 1112176417Sthompsa goto bad; 1113176417Sthompsa 1114176417Sthompsa if (strstr(tok, "}")) 1115176417Sthompsa break; 1116176417Sthompsa 1117176417Sthompsa if (strcmp(tok, "stripes = [") == 0) { 1118176417Sthompsa tok = strsep(buf, "\n"); 1119176417Sthompsa if (tok == NULL) 1120176417Sthompsa goto bad; 1121176417Sthompsa 1122176417Sthompsa strsep(&tok, "\""); 1123176417Sthompsa if (tok == NULL) 1124176417Sthompsa goto bad; /* missing open quotes */ 1125176417Sthompsa v = strsep(&tok, "\""); 1126176417Sthompsa if (tok == NULL) 1127176417Sthompsa goto bad; /* missing close quotes */ 1128176417Sthompsa strncpy(sg->sg_pvname, v, sizeof(sg->sg_pvname)); 1129176417Sthompsa if (*tok != ',') 1130176417Sthompsa goto bad; /* missing comma for stripe */ 1131176417Sthompsa tok++; 1132176417Sthompsa 1133176417Sthompsa sg->sg_pvstart = strtol(tok, &v, 10); 1134176417Sthompsa if (v == tok) 1135176417Sthompsa /* strtol did not eat any of the buffer */ 1136176417Sthompsa goto bad; 1137176417Sthompsa 1138176417Sthompsa continue; 1139176417Sthompsa } 1140176417Sthompsa 1141176417Sthompsa /* parse 'key = value' lines */ 1142176417Sthompsa if (strstr(tok, "=")) { 1143176417Sthompsa SPLIT(v, tok, "="); 1144176417Sthompsa GRAB_INT("start_extent", v, tok, sg->sg_start); 1145176417Sthompsa GRAB_INT("extent_count", v, tok, sg->sg_count); 1146176417Sthompsa continue; 1147176417Sthompsa } 1148176417Sthompsa } 1149176417Sthompsa if (tok == NULL) 1150176417Sthompsa goto bad; 1151176417Sthompsa /* basic checking */ 1152176417Sthompsa if (count != 1 || sg->sg_count == 0) 1153176417Sthompsa goto bad; 1154176417Sthompsa 1155176417Sthompsa sg->sg_end = sg->sg_start + sg->sg_count - 1; 1156176417Sthompsa lv->lv_numsegs++; 1157176417Sthompsa lv->lv_extentcount += sg->sg_count; 1158176417Sthompsa LIST_INSERT_HEAD(&lv->lv_segs, sg, sg_next); 1159176417Sthompsa 1160176417Sthompsa return (0); 1161176417Sthompsabad: 1162176417Sthompsa free(sg, M_GLLVM); 1163176417Sthompsa return (-1); 1164176417Sthompsa} 1165176417Sthompsa#undef GRAB_NAME 1166176417Sthompsa#undef GRAB_INT 1167176417Sthompsa#undef GRAB_STR 1168176417Sthompsa#undef SPLIT 1169176417Sthompsa 1170176417Sthompsastatic struct g_class g_llvm_class = { 1171176417Sthompsa .name = G_LLVM_CLASS_NAME, 1172176417Sthompsa .version = G_VERSION, 1173176417Sthompsa .init = g_llvm_init, 1174176417Sthompsa .taste = g_llvm_taste, 1175176417Sthompsa .destroy_geom = g_llvm_destroy_geom 1176176417Sthompsa}; 1177176417Sthompsa 1178176417SthompsaDECLARE_GEOM_CLASS(g_llvm_class, g_linux_lvm); 1179