g_concat.c revision 134344
1219974Smav/*-
2219974Smav * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3219974Smav * All rights reserved.
4219974Smav *
5219974Smav * Redistribution and use in source and binary forms, with or without
6219974Smav * modification, are permitted provided that the following conditions
7219974Smav * are met:
8219974Smav * 1. Redistributions of source code must retain the above copyright
9219974Smav *    notice, this list of conditions and the following disclaimer.
10219974Smav * 2. Redistributions in binary form must reproduce the above copyright
11219974Smav *    notice, this list of conditions and the following disclaimer in the
12219974Smav *    documentation and/or other materials provided with the distribution.
13219974Smav *
14219974Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15219974Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16219974Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17219974Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18219974Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19219974Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20219974Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21219974Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22219974Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23219974Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24219974Smav * SUCH DAMAGE.
25219974Smav */
26219974Smav
27219974Smav#include <sys/cdefs.h>
28219974Smav__FBSDID("$FreeBSD: head/sys/geom/concat/g_concat.c 134344 2004-08-26 12:42:47Z pjd $");
29219974Smav
30219974Smav#include <sys/param.h>
31219974Smav#include <sys/systm.h>
32219974Smav#include <sys/kernel.h>
33219974Smav#include <sys/module.h>
34219974Smav#include <sys/lock.h>
35219974Smav#include <sys/mutex.h>
36219974Smav#include <sys/bio.h>
37219974Smav#include <sys/sysctl.h>
38219974Smav#include <sys/malloc.h>
39219974Smav#include <geom/geom.h>
40219974Smav#include <geom/concat/g_concat.h>
41219974Smav
42219974Smav
43219974Smavstatic MALLOC_DEFINE(M_CONCAT, "concat data", "GEOM_CONCAT Data");
44219974Smav
45219974SmavSYSCTL_DECL(_kern_geom);
46219974SmavSYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW, 0, "GEOM_CONCAT stuff");
47219974Smavstatic u_int g_concat_debug = 0;
48219974SmavSYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RW, &g_concat_debug, 0,
49219974Smav    "Debug level");
50219974Smav
51219974Smavstatic int g_concat_destroy(struct g_concat_softc *sc, boolean_t force);
52219974Smavstatic int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp,
53219974Smav    struct g_geom *gp);
54219974Smav
55219974Smavstatic g_taste_t g_concat_taste;
56219974Smavstatic g_ctl_req_t g_concat_config;
57219974Smavstatic g_dumpconf_t g_concat_dumpconf;
58219974Smav
59219974Smavstruct g_class g_concat_class = {
60219974Smav	.name = G_CONCAT_CLASS_NAME,
61219974Smav	.version = G_VERSION,
62219974Smav	.ctlreq = g_concat_config,
63219974Smav	.taste = g_concat_taste,
64219974Smav	.destroy_geom = g_concat_destroy_geom
65219974Smav};
66219974Smav
67219974Smav
68219974Smav/*
69219974Smav * Greatest Common Divisor.
70219974Smav */
71219974Smavstatic u_int
72219974Smavgcd(u_int a, u_int b)
73219974Smav{
74219974Smav	u_int c;
75219974Smav
76219974Smav	while (b != 0) {
77219974Smav		c = a;
78219974Smav		a = b;
79219974Smav		b = (c % b);
80219974Smav	}
81219974Smav	return (a);
82219974Smav}
83219974Smav
84219974Smav/*
85219974Smav * Least Common Multiple.
86219974Smav */
87219974Smavstatic u_int
88219974Smavlcm(u_int a, u_int b)
89219974Smav{
90219974Smav
91219974Smav	return ((a * b) / gcd(a, b));
92219974Smav}
93219974Smav
94219974Smav/*
95219974Smav * Return the number of valid disks.
96219974Smav */
97219974Smavstatic u_int
98219974Smavg_concat_nvalid(struct g_concat_softc *sc)
99219974Smav{
100219974Smav	u_int i, no;
101219974Smav
102219974Smav	no = 0;
103219974Smav	for (i = 0; i < sc->sc_ndisks; i++) {
104219974Smav		if (sc->sc_disks[i].d_consumer != NULL)
105219974Smav			no++;
106219974Smav	}
107219974Smav
108219974Smav	return (no);
109219974Smav}
110219974Smav
111219974Smavstatic void
112219974Smavg_concat_remove_disk(struct g_concat_disk *disk)
113219974Smav{
114219974Smav	struct g_consumer *cp;
115219974Smav	struct g_concat_softc *sc;
116219974Smav
117219974Smav	KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__));
118219974Smav	sc = disk->d_softc;
119219974Smav	cp = disk->d_consumer;
120219974Smav
121219974Smav	G_CONCAT_DEBUG(0, "Disk %s removed from %s.", cp->provider->name,
122219974Smav	    sc->sc_name);
123219974Smav
124219974Smav	disk->d_consumer = NULL;
125219974Smav	if (sc->sc_provider != NULL) {
126219974Smav		g_orphan_provider(sc->sc_provider, ENXIO);
127219974Smav		sc->sc_provider = NULL;
128219974Smav		G_CONCAT_DEBUG(0, "Device %s removed.", sc->sc_name);
129219974Smav	}
130219974Smav
131219974Smav	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
132219974Smav		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
133219974Smav	g_detach(cp);
134219974Smav	g_destroy_consumer(cp);
135219974Smav}
136219974Smav
137219974Smavstatic void
138219974Smavg_concat_orphan(struct g_consumer *cp)
139219974Smav{
140219974Smav	struct g_concat_softc *sc;
141219974Smav	struct g_concat_disk *disk;
142219974Smav	struct g_geom *gp;
143219974Smav
144219974Smav	g_topology_assert();
145219974Smav	gp = cp->geom;
146219974Smav	sc = gp->softc;
147219974Smav	if (sc == NULL)
148219974Smav		return;
149219974Smav
150219974Smav	disk = cp->private;
151219974Smav	if (disk == NULL)	/* Possible? */
152219974Smav		return;
153219974Smav	g_concat_remove_disk(disk);
154219974Smav
155219974Smav	/* If there are no valid disks anymore, remove device. */
156219974Smav	if (g_concat_nvalid(sc) == 0)
157219974Smav		g_concat_destroy(sc, 1);
158219974Smav}
159219974Smav
160219974Smavstatic int
161219974Smavg_concat_access(struct g_provider *pp, int dr, int dw, int de)
162219974Smav{
163219974Smav	struct g_consumer *cp1, *cp2;
164219974Smav	struct g_concat_softc *sc;
165219974Smav	struct g_geom *gp;
166219974Smav	int error;
167219974Smav
168219974Smav	gp = pp->geom;
169219974Smav	sc = gp->softc;
170219974Smav
171219974Smav	if (sc == NULL) {
172219974Smav		/*
173219974Smav		 * It looks like geom is being withered.
174219974Smav		 * In that case we allow only negative requests.
175219974Smav		 */
176219974Smav		KASSERT(dr <= 0 && dw <= 0 && de <= 0,
177219974Smav		    ("Positive access request (device=%s).", pp->name));
178219974Smav		if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 &&
179219974Smav		    (pp->ace + de) == 0) {
180219974Smav			G_CONCAT_DEBUG(0, "Device %s definitely destroyed.",
181219974Smav			    gp->name);
182219974Smav		}
183219974Smav		return (0);
184219974Smav	}
185219974Smav
186219974Smav	/* On first open, grab an extra "exclusive" bit */
187219974Smav	if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
188219974Smav		de++;
189219974Smav	/* ... and let go of it on last close */
190219974Smav	if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0)
191219974Smav		de--;
192219974Smav
193219974Smav	error = ENXIO;
194219974Smav	LIST_FOREACH(cp1, &gp->consumer, consumer) {
195219974Smav		error = g_access(cp1, dr, dw, de);
196219974Smav		if (error == 0)
197219974Smav			continue;
198219974Smav		/*
199219974Smav		 * If we fail here, backout all previous changes.
200219974Smav		 */
201219974Smav		LIST_FOREACH(cp2, &gp->consumer, consumer) {
202219974Smav			if (cp1 == cp2)
203219974Smav				return (error);
204219974Smav			g_access(cp2, -dr, -dw, -de);
205219974Smav		}
206219974Smav		/* NOTREACHED */
207219974Smav	}
208219974Smav
209219974Smav	return (error);
210219974Smav}
211219974Smav
212219974Smavstatic void
213219974Smavg_concat_start(struct bio *bp)
214219974Smav{
215219974Smav	struct g_concat_softc *sc;
216219974Smav	struct g_concat_disk *disk;
217219974Smav	struct g_provider *pp;
218219974Smav	off_t offset, end, length, off, len;
219219974Smav	struct bio *cbp;
220219974Smav	char *addr;
221219974Smav	u_int no;
222219974Smav
223219974Smav	pp = bp->bio_to;
224219974Smav	sc = pp->geom->softc;
225219974Smav	/*
226219974Smav	 * If sc == NULL, provider's error should be set and g_concat_start()
227219974Smav	 * should not be called at all.
228219974Smav	 */
229219974Smav	KASSERT(sc != NULL,
230219974Smav	    ("Provider's error should be set (error=%d)(device=%s).",
231219974Smav	    bp->bio_to->error, bp->bio_to->name));
232219974Smav
233219974Smav	G_CONCAT_LOGREQ(bp, "Request received.");
234219974Smav
235219974Smav	switch (bp->bio_cmd) {
236219974Smav	case BIO_READ:
237219974Smav	case BIO_WRITE:
238219974Smav	case BIO_DELETE:
239219974Smav		break;
240219974Smav	case BIO_GETATTR:
241219974Smav		/* To which provider it should be delivered? */
242219974Smav	default:
243219974Smav		g_io_deliver(bp, EOPNOTSUPP);
244219974Smav		return;
245219974Smav	}
246219974Smav
247219974Smav	offset = bp->bio_offset;
248219974Smav	length = bp->bio_length;
249219974Smav	addr = bp->bio_data;
250219974Smav	end = offset + length;
251219974Smav
252219974Smav	for (no = 0; no < sc->sc_ndisks; no++) {
253219974Smav		disk = &sc->sc_disks[no];
254219974Smav		if (disk->d_end <= offset)
255219974Smav			continue;
256219974Smav		if (disk->d_start >= end)
257219974Smav			break;
258219974Smav
259219974Smav		off = offset - disk->d_start;
260219974Smav		len = MIN(length, disk->d_end - offset);
261219974Smav		length -= len;
262219974Smav		offset += len;
263219974Smav
264219974Smav		cbp = g_clone_bio(bp);
265219974Smav		if (cbp == NULL) {
266219974Smav			if (bp->bio_error == 0)
267219974Smav				bp->bio_error = ENOMEM;
268219974Smav			return;
269219974Smav		}
270219974Smav		/*
271219974Smav		 * Fill in the component buf structure.
272219974Smav		 */
273219974Smav		cbp->bio_done = g_std_done;
274219974Smav		cbp->bio_offset = off;
275219974Smav		cbp->bio_data = addr;
276219974Smav		addr += len;
277219974Smav		cbp->bio_length = len;
278219974Smav		cbp->bio_to = disk->d_consumer->provider;
279219974Smav		G_CONCAT_LOGREQ(cbp, "Sending request.");
280219974Smav		g_io_request(cbp, disk->d_consumer);
281219974Smav
282219974Smav		if (length == 0)
283219974Smav			break;
284219974Smav	}
285219974Smav
286219974Smav	KASSERT(length == 0,
287219974Smav	    ("Length is still greater than 0 (class=%s, name=%s).",
288219974Smav	    bp->bio_to->geom->class->name, bp->bio_to->geom->name));
289219974Smav}
290219974Smav
291219974Smavstatic void
292219974Smavg_concat_check_and_run(struct g_concat_softc *sc)
293219974Smav{
294219974Smav	struct g_concat_disk *disk;
295219974Smav	u_int no, sectorsize = 0;
296219974Smav	off_t start;
297219974Smav
298219974Smav	if (g_concat_nvalid(sc) != sc->sc_ndisks)
299219974Smav		return;
300219974Smav
301219974Smav	sc->sc_provider = g_new_providerf(sc->sc_geom, "concat/%s",
302219974Smav	    sc->sc_name);
303219974Smav	start = 0;
304219974Smav	for (no = 0; no < sc->sc_ndisks; no++) {
305219974Smav		disk = &sc->sc_disks[no];
306219974Smav		disk->d_start = start;
307219974Smav		disk->d_end = disk->d_start +
308219974Smav		    disk->d_consumer->provider->mediasize;
309219974Smav		if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC)
310219974Smav			disk->d_end -= disk->d_consumer->provider->sectorsize;
311219974Smav		start = disk->d_end;
312219974Smav		if (no == 0)
313219974Smav			sectorsize = disk->d_consumer->provider->sectorsize;
314219974Smav		else {
315219974Smav			sectorsize = lcm(sectorsize,
316219974Smav			    disk->d_consumer->provider->sectorsize);
317219974Smav		}
318219974Smav	}
319219974Smav	sc->sc_provider->sectorsize = sectorsize;
320219974Smav	/* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */
321219974Smav	sc->sc_provider->mediasize = start;
322219974Smav	g_error_provider(sc->sc_provider, 0);
323219974Smav
324219974Smav	G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_name);
325219974Smav}
326219974Smav
327219974Smavstatic int
328219974Smavg_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md)
329219974Smav{
330219974Smav	struct g_provider *pp;
331219974Smav	u_char *buf;
332219974Smav	int error;
333219974Smav
334219974Smav	g_topology_assert();
335219974Smav
336219974Smav	error = g_access(cp, 1, 0, 0);
337219974Smav	if (error != 0)
338219974Smav		return (error);
339219974Smav	pp = cp->provider;
340219974Smav	g_topology_unlock();
341219974Smav	buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
342219974Smav	    &error);
343219974Smav	g_topology_lock();
344219974Smav	g_access(cp, -1, 0, 0);
345219974Smav	if (buf == NULL)
346219974Smav		return (error);
347219974Smav
348219974Smav	/* Decode metadata. */
349219974Smav	concat_metadata_decode(buf, md);
350219974Smav	g_free(buf);
351219974Smav
352219974Smav	return (0);
353219974Smav}
354219974Smav
355219974Smav/*
356219974Smav * Add disk to given device.
357219974Smav */
358219974Smavstatic int
359219974Smavg_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no)
360219974Smav{
361219974Smav	struct g_concat_disk *disk;
362219974Smav	struct g_consumer *cp, *fcp;
363219974Smav	struct g_geom *gp;
364219974Smav	int error;
365219974Smav
366219974Smav	/* Metadata corrupted? */
367219974Smav	if (no >= sc->sc_ndisks)
368219974Smav		return (EINVAL);
369219974Smav
370219974Smav	disk = &sc->sc_disks[no];
371219974Smav	/* Check if disk is not already attached. */
372219974Smav	if (disk->d_consumer != NULL)
373219974Smav		return (EEXIST);
374219974Smav
375219974Smav	gp = sc->sc_geom;
376219974Smav	fcp = LIST_FIRST(&gp->consumer);
377219974Smav
378219974Smav	cp = g_new_consumer(gp);
379219974Smav	error = g_attach(cp, pp);
380219974Smav	if (error != 0) {
381219974Smav		g_destroy_consumer(cp);
382219974Smav		return (error);
383219974Smav	}
384219974Smav
385219974Smav	if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) {
386219974Smav		error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
387219974Smav		if (error != 0) {
388219974Smav			g_detach(cp);
389219974Smav			g_destroy_consumer(cp);
390219974Smav			return (error);
391219974Smav		}
392219974Smav	}
393219974Smav	if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) {
394219974Smav		struct g_concat_metadata md;
395219974Smav
396219974Smav		/* Re-read metadata. */
397219974Smav		error = g_concat_read_metadata(cp, &md);
398219974Smav		if (error != 0)
399219974Smav			goto fail;
400219974Smav
401219974Smav		if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 ||
402219974Smav		    strcmp(md.md_name, sc->sc_name) != 0 ||
403219974Smav		    md.md_id != sc->sc_id) {
404219974Smav			G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name);
405219974Smav			goto fail;
406219974Smav		}
407219974Smav	}
408219974Smav
409219974Smav	cp->private = disk;
410219974Smav	disk->d_consumer = cp;
411219974Smav	disk->d_softc = sc;
412219974Smav	disk->d_start = 0;	/* not yet */
413219974Smav	disk->d_end = 0;	/* not yet */
414219974Smav
415219974Smav	G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name);
416219974Smav
417219974Smav	g_concat_check_and_run(sc);
418219974Smav
419219974Smav	return (0);
420219974Smavfail:
421219974Smav	if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0))
422219974Smav		g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace);
423219974Smav	g_detach(cp);
424219974Smav	g_destroy_consumer(cp);
425219974Smav	return (error);
426219974Smav}
427219974Smav
428219974Smavstatic struct g_geom *
429219974Smavg_concat_create(struct g_class *mp, const struct g_concat_metadata *md,
430219974Smav    u_int type)
431219974Smav{
432219974Smav	struct g_concat_softc *sc;
433219974Smav	struct g_geom *gp;
434219974Smav	u_int no;
435219974Smav
436219974Smav	G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name,
437219974Smav	    md->md_id);
438219974Smav
439219974Smav	/* Two disks is minimum. */
440219974Smav	if (md->md_all <= 1)
441219974Smav		return (NULL);
442219974Smav
443219974Smav	/* Check for duplicate unit */
444219974Smav	LIST_FOREACH(gp, &mp->geom, geom) {
445219974Smav		sc = gp->softc;
446219974Smav		if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) {
447219974Smav			G_CONCAT_DEBUG(0, "Device %s already configured.",
448219974Smav			    gp->name);
449219974Smav			return (NULL);
450219974Smav		}
451219974Smav	}
452219974Smav	gp = g_new_geomf(mp, "%s", md->md_name);
453219974Smav	gp->softc = NULL;	/* for a moment */
454219974Smav
455219974Smav	sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO);
456219974Smav	gp->start = g_concat_start;
457219974Smav	gp->spoiled = g_concat_orphan;
458219974Smav	gp->orphan = g_concat_orphan;
459219974Smav	gp->access = g_concat_access;
460219974Smav	gp->dumpconf = g_concat_dumpconf;
461219974Smav
462219974Smav	sc->sc_id = md->md_id;
463219974Smav	sc->sc_ndisks = md->md_all;
464219974Smav	sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks,
465219974Smav	    M_CONCAT, M_WAITOK | M_ZERO);
466219974Smav	for (no = 0; no < sc->sc_ndisks; no++)
467219974Smav		sc->sc_disks[no].d_consumer = NULL;
468219974Smav	sc->sc_type = type;
469219974Smav
470219974Smav	gp->softc = sc;
471219974Smav	sc->sc_geom = gp;
472219974Smav	sc->sc_provider = NULL;
473219974Smav
474219974Smav	G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id);
475219974Smav
476219974Smav	return (gp);
477219974Smav}
478219974Smav
479219974Smavstatic int
480219974Smavg_concat_destroy(struct g_concat_softc *sc, boolean_t force)
481219974Smav{
482219974Smav	struct g_provider *pp;
483219974Smav	struct g_geom *gp;
484219974Smav	u_int no;
485219974Smav
486219974Smav	g_topology_assert();
487219974Smav
488219974Smav	if (sc == NULL)
489219974Smav		return (ENXIO);
490219974Smav
491219974Smav	pp = sc->sc_provider;
492219974Smav	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
493219974Smav		if (force) {
494219974Smav			G_CONCAT_DEBUG(0, "Device %s is still open, so it "
495219974Smav			    "can't be definitely removed.", pp->name);
496219974Smav		} else {
497219974Smav			G_CONCAT_DEBUG(1,
498219974Smav			    "Device %s is still open (r%dw%de%d).", pp->name,
499219974Smav			    pp->acr, pp->acw, pp->ace);
500219974Smav			return (EBUSY);
501219974Smav		}
502219974Smav	}
503219974Smav
504219974Smav	for (no = 0; no < sc->sc_ndisks; no++) {
505219974Smav		if (sc->sc_disks[no].d_consumer != NULL)
506219974Smav			g_concat_remove_disk(&sc->sc_disks[no]);
507219974Smav	}
508219974Smav
509219974Smav	gp = sc->sc_geom;
510219974Smav	gp->softc = NULL;
511219974Smav	KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)",
512219974Smav	    gp->name));
513219974Smav	free(sc->sc_disks, M_CONCAT);
514219974Smav	free(sc, M_CONCAT);
515219974Smav
516219974Smav	pp = LIST_FIRST(&gp->provider);
517219974Smav	if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0))
518219974Smav		G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name);
519219974Smav
520219974Smav	g_wither_geom(gp, ENXIO);
521219974Smav
522219974Smav	return (0);
523219974Smav}
524219974Smav
525219974Smavstatic int
526219974Smavg_concat_destroy_geom(struct gctl_req *req __unused,
527219974Smav    struct g_class *mp __unused, struct g_geom *gp)
528219974Smav{
529219974Smav	struct g_concat_softc *sc;
530219974Smav
531219974Smav	sc = gp->softc;
532219974Smav	return (g_concat_destroy(sc, 0));
533219974Smav}
534219974Smav
535219974Smavstatic struct g_geom *
536219974Smavg_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
537219974Smav{
538219974Smav	struct g_concat_metadata md;
539219974Smav	struct g_concat_softc *sc;
540219974Smav	struct g_consumer *cp;
541219974Smav	struct g_geom *gp;
542219974Smav	int error;
543219974Smav
544220209Smav	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
545219974Smav	g_topology_assert();
546220209Smav
547219974Smav	G_CONCAT_DEBUG(3, "Tasting %s.", pp->name);
548219974Smav	/* Skip providers with 0 sectorsize. */
549219974Smav	if (pp->sectorsize == 0)
550219974Smav		return (NULL);
551219974Smav
552219974Smav	gp = g_new_geomf(mp, "concat:taste");
553219974Smav	gp->start = g_concat_start;
554219974Smav	gp->access = g_concat_access;
555219974Smav	gp->orphan = g_concat_orphan;
556219974Smav	cp = g_new_consumer(gp);
557219974Smav	g_attach(cp, pp);
558219974Smav	error = g_concat_read_metadata(cp, &md);
559219974Smav	g_detach(cp);
560219974Smav	g_destroy_consumer(cp);
561219974Smav	g_destroy_geom(gp);
562219974Smav	if (error != 0)
563219974Smav		return (NULL);
564219974Smav	gp = NULL;
565219974Smav
566219974Smav	if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0)
567219974Smav		return (NULL);
568219974Smav	if (md.md_version > G_CONCAT_VERSION) {
569219974Smav		printf("geom_concat.ko module is too old to handle %s.\n",
570219974Smav		    pp->name);
571219974Smav		return (NULL);
572219974Smav	}
573219974Smav	/*
574219974Smav	 * Backward compatibility:
575219974Smav	 * There was no md_provider field in earlier versions of metadata.
576219974Smav	 */
577219974Smav	if (md.md_version < 3)
578219974Smav		bzero(md.md_provider, sizeof(md.md_provider));
579219974Smav
580219974Smav	if (md.md_provider[0] != '\0' && strcmp(md.md_provider, pp->name) != 0)
581219974Smav		return (NULL);
582219974Smav
583219974Smav	/*
584219974Smav	 * Let's check if device already exists.
585219974Smav	 */
586219974Smav	sc = NULL;
587219974Smav	LIST_FOREACH(gp, &mp->geom, geom) {
588219974Smav		sc = gp->softc;
589219974Smav		if (sc == NULL)
590219974Smav			continue;
591219974Smav		if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC)
592219974Smav			continue;
593219974Smav		if (strcmp(md.md_name, sc->sc_name) != 0)
594219974Smav			continue;
595219974Smav		if (md.md_id != sc->sc_id)
596219974Smav			continue;
597219974Smav		break;
598219974Smav	}
599219974Smav	if (gp != NULL) {
600219974Smav		G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
601219974Smav		error = g_concat_add_disk(sc, pp, md.md_no);
602219974Smav		if (error != 0) {
603219974Smav			G_CONCAT_DEBUG(0,
604219974Smav			    "Cannot add disk %s to %s (error=%d).", pp->name,
605219974Smav			    gp->name, error);
606219974Smav			return (NULL);
607219974Smav		}
608219974Smav	} else {
609219974Smav		gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC);
610219974Smav		if (gp == NULL) {
611219974Smav			G_CONCAT_DEBUG(0, "Cannot create device %s.",
612219974Smav			    md.md_name);
613219974Smav			return (NULL);
614219974Smav		}
615219974Smav		sc = gp->softc;
616219974Smav		G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
617219974Smav		error = g_concat_add_disk(sc, pp, md.md_no);
618219974Smav		if (error != 0) {
619219974Smav			G_CONCAT_DEBUG(0,
620219974Smav			    "Cannot add disk %s to %s (error=%d).", pp->name,
621219974Smav			    gp->name, error);
622219974Smav			g_concat_destroy(sc, 1);
623219974Smav			return (NULL);
624219974Smav		}
625219974Smav	}
626219974Smav
627219974Smav	return (gp);
628219974Smav}
629219974Smav
630219974Smavstatic void
631219974Smavg_concat_ctl_create(struct gctl_req *req, struct g_class *mp)
632219974Smav{
633219974Smav	u_int attached, no;
634219974Smav	struct g_concat_metadata md;
635219974Smav	struct g_provider *pp;
636219974Smav	struct g_concat_softc *sc;
637219974Smav	struct g_geom *gp;
638219974Smav	struct sbuf *sb;
639219974Smav	const char *name;
640219974Smav	char param[16];
641219974Smav	int *nargs;
642219974Smav
643219974Smav	g_topology_assert();
644219974Smav	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
645219974Smav	if (nargs == NULL) {
646219974Smav		gctl_error(req, "No '%s' argument.", "nargs");
647219974Smav		return;
648219974Smav	}
649219974Smav	if (*nargs <= 2) {
650219974Smav		gctl_error(req, "Too few arguments.");
651219974Smav		return;
652219974Smav	}
653219974Smav
654219974Smav	strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic));
655219974Smav	md.md_version = G_CONCAT_VERSION;
656219974Smav	name = gctl_get_asciiparam(req, "arg0");
657219974Smav	if (name == NULL) {
658219974Smav		gctl_error(req, "No 'arg%u' argument.", 0);
659219974Smav		return;
660219974Smav	}
661219974Smav	strlcpy(md.md_name, name, sizeof(md.md_name));
662219974Smav	md.md_id = arc4random();
663219974Smav	md.md_no = 0;
664219974Smav	md.md_all = *nargs - 1;
665219974Smav	bzero(md.md_provider, sizeof(md.md_provider));
666219974Smav
667219974Smav	/* Check all providers are valid */
668219974Smav	for (no = 1; no < *nargs; no++) {
669219974Smav		snprintf(param, sizeof(param), "arg%u", no);
670219974Smav		name = gctl_get_asciiparam(req, param);
671219974Smav		if (name == NULL) {
672219974Smav			gctl_error(req, "No 'arg%u' argument.", no);
673219974Smav			return;
674219974Smav		}
675219974Smav		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
676219974Smav			name += strlen("/dev/");
677219974Smav		pp = g_provider_by_name(name);
678219974Smav		if (pp == NULL) {
679219974Smav			G_CONCAT_DEBUG(1, "Disk %s is invalid.", name);
680219974Smav			gctl_error(req, "Disk %s is invalid.", name);
681219974Smav			return;
682219974Smav		}
683219974Smav	}
684219974Smav
685219974Smav	gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL);
686219974Smav	if (gp == NULL) {
687219974Smav		gctl_error(req, "Can't configure %s.", md.md_name);
688219974Smav		return;
689219974Smav	}
690219974Smav
691219974Smav	sc = gp->softc;
692219974Smav	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
693219974Smav	sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
694219974Smav	for (attached = 0, no = 1; no < *nargs; no++) {
695219974Smav		snprintf(param, sizeof(param), "arg%u", no);
696219974Smav		name = gctl_get_asciiparam(req, param);
697219974Smav		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
698219974Smav			name += strlen("/dev/");
699219974Smav		pp = g_provider_by_name(name);
700219974Smav		KASSERT(pp != NULL, ("Provider %s disappear?!", name));
701219974Smav		if (g_concat_add_disk(sc, pp, no - 1) != 0) {
702219974Smav			G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.",
703219974Smav			    no, pp->name, gp->name);
704219974Smav			sbuf_printf(sb, " %s", pp->name);
705219974Smav			continue;
706219974Smav		}
707219974Smav		attached++;
708219974Smav	}
709219974Smav	sbuf_finish(sb);
710219974Smav	if (md.md_all != attached) {
711219974Smav		g_concat_destroy(gp->softc, 1);
712219974Smav		gctl_error(req, "%s", sbuf_data(sb));
713219974Smav	}
714219974Smav	sbuf_delete(sb);
715219974Smav}
716219974Smav
717219974Smavstatic struct g_concat_softc *
718219974Smavg_concat_find_device(struct g_class *mp, const char *name)
719219974Smav{
720219974Smav	struct g_concat_softc *sc;
721219974Smav	struct g_geom *gp;
722219974Smav
723219974Smav	LIST_FOREACH(gp, &mp->geom, geom) {
724219974Smav		sc = gp->softc;
725219974Smav		if (sc == NULL)
726219974Smav			continue;
727219974Smav		if (strcmp(sc->sc_name, name) == 0)
728219974Smav			return (sc);
729219974Smav	}
730219974Smav	return (NULL);
731219974Smav}
732219974Smav
733219974Smavstatic void
734219974Smavg_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp)
735219974Smav{
736219974Smav	struct g_concat_softc *sc;
737219974Smav	int *force, *nargs, error;
738219974Smav	const char *name;
739219974Smav	char param[16];
740219974Smav	u_int i;
741219974Smav
742219974Smav	g_topology_assert();
743219974Smav
744219974Smav	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
745219974Smav	if (nargs == NULL) {
746219974Smav		gctl_error(req, "No '%s' argument.", "nargs");
747219974Smav		return;
748219974Smav	}
749219974Smav	if (*nargs <= 0) {
750219974Smav		gctl_error(req, "Missing device(s).");
751219974Smav		return;
752219974Smav	}
753219974Smav	force = gctl_get_paraml(req, "force", sizeof(*force));
754219974Smav	if (force == NULL) {
755219974Smav		gctl_error(req, "No '%s' argument.", "force");
756219974Smav		return;
757219974Smav	}
758219974Smav
759219974Smav	for (i = 0; i < (u_int)*nargs; i++) {
760219974Smav		snprintf(param, sizeof(param), "arg%u", i);
761219974Smav		name = gctl_get_asciiparam(req, param);
762219974Smav		if (name == NULL) {
763219974Smav			gctl_error(req, "No 'arg%u' argument.", i);
764219974Smav			return;
765219974Smav		}
766219974Smav		sc = g_concat_find_device(mp, name);
767219974Smav		if (sc == NULL) {
768219974Smav			gctl_error(req, "No such device: %s.", name);
769219974Smav			return;
770219974Smav		}
771219974Smav		error = g_concat_destroy(sc, *force);
772219974Smav		if (error != 0) {
773219974Smav			gctl_error(req, "Cannot destroy device %s (error=%d).",
774219974Smav			    sc->sc_name, error);
775219974Smav			return;
776219974Smav		}
777219974Smav	}
778219974Smav}
779219974Smav
780219974Smavstatic void
781219974Smavg_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb)
782219974Smav{
783219974Smav	uint32_t *version;
784219974Smav
785219974Smav	g_topology_assert();
786219974Smav
787219974Smav	version = gctl_get_paraml(req, "version", sizeof(*version));
788219974Smav	if (version == NULL) {
789219974Smav		gctl_error(req, "No '%s' argument.", "version");
790219974Smav		return;
791219974Smav	}
792219974Smav	if (*version != G_CONCAT_VERSION) {
793219974Smav		gctl_error(req, "Userland and kernel parts are out of sync.");
794219974Smav		return;
795219974Smav	}
796219974Smav
797219974Smav	if (strcmp(verb, "create") == 0) {
798219974Smav		g_concat_ctl_create(req, mp);
799219974Smav		return;
800219974Smav	} else if (strcmp(verb, "destroy") == 0 ||
801219974Smav	    strcmp(verb, "stop") == 0) {
802219974Smav		g_concat_ctl_destroy(req, mp);
803219974Smav		return;
804219974Smav	}
805219974Smav	gctl_error(req, "Unknown verb.");
806219974Smav}
807219974Smav
808219974Smavstatic void
809219974Smavg_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
810219974Smav    struct g_consumer *cp, struct g_provider *pp)
811219974Smav{
812219974Smav	struct g_concat_softc *sc;
813219974Smav
814219974Smav	g_topology_assert();
815219974Smav	sc = gp->softc;
816219974Smav	if (sc == NULL)
817219974Smav		return;
818219974Smav	if (pp != NULL) {
819219974Smav		/* Nothing here. */
820219974Smav	} else if (cp != NULL) {
821219974Smav		struct g_concat_disk *disk;
822219974Smav
823219974Smav		disk = cp->private;
824219974Smav		if (disk == NULL)
825219974Smav			return;
826219974Smav		sbuf_printf(sb, "%s<End>%jd</End>\n", indent,
827219974Smav		    (intmax_t)disk->d_end);
828219974Smav		sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent,
829219974Smav		    (intmax_t)disk->d_start);
830219974Smav	} else {
831219974Smav		sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id);
832219974Smav		sbuf_printf(sb, "%s<Type>", indent);
833219974Smav		switch (sc->sc_type) {
834219974Smav		case G_CONCAT_TYPE_AUTOMATIC:
835219974Smav			sbuf_printf(sb, "AUTOMATIC");
836219974Smav			break;
837219974Smav		case G_CONCAT_TYPE_MANUAL:
838219974Smav			sbuf_printf(sb, "MANUAL");
839219974Smav			break;
840219974Smav		default:
841219974Smav			sbuf_printf(sb, "UNKNOWN");
842219974Smav			break;
843219974Smav		}
844219974Smav		sbuf_printf(sb, "</Type>\n");
845219974Smav		sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n",
846219974Smav		    indent, sc->sc_ndisks, g_concat_nvalid(sc));
847219974Smav		sbuf_printf(sb, "%s<State>", indent);
848219974Smav		if (sc->sc_provider != NULL && sc->sc_provider->error == 0)
849219974Smav			sbuf_printf(sb, "UP");
850219974Smav		else
851219974Smav			sbuf_printf(sb, "DOWN");
852219974Smav		sbuf_printf(sb, "</State>\n");
853219974Smav	}
854219974Smav}
855219974Smav
856219974SmavDECLARE_GEOM_CLASS(g_concat_class, g_concat);
857219974Smav