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