xref: /freebsd/sys/geom/geom_ccd.c (revision 0f557e0ac0fdc86f4c152b712478fd212370168d)
1a56bb8a5SSatoshi Asami /*
23b1746dfSPoul-Henning Kamp  * Copyright (c) 2003 Poul-Henning Kamp.
3a56bb8a5SSatoshi Asami  * Copyright (c) 1995 Jason R. Thorpe.
4a56bb8a5SSatoshi Asami  * Copyright (c) 1990, 1993
5a56bb8a5SSatoshi Asami  *	The Regents of the University of California.  All rights reserved.
63b1746dfSPoul-Henning Kamp  * All rights reserved.
73b1746dfSPoul-Henning Kamp  * Copyright (c) 1988 University of Utah.
8a56bb8a5SSatoshi Asami  *
9a56bb8a5SSatoshi Asami  * This code is derived from software contributed to Berkeley by
10a56bb8a5SSatoshi Asami  * the Systems Programming Group of the University of Utah Computer
11a56bb8a5SSatoshi Asami  * Science Department.
12a56bb8a5SSatoshi Asami  *
13a56bb8a5SSatoshi Asami  * Redistribution and use in source and binary forms, with or without
14a56bb8a5SSatoshi Asami  * modification, are permitted provided that the following conditions
15a56bb8a5SSatoshi Asami  * are met:
16a56bb8a5SSatoshi Asami  * 1. Redistributions of source code must retain the above copyright
17a56bb8a5SSatoshi Asami  *    notice, this list of conditions and the following disclaimer.
18a56bb8a5SSatoshi Asami  * 2. Redistributions in binary form must reproduce the above copyright
19a56bb8a5SSatoshi Asami  *    notice, this list of conditions and the following disclaimer in the
20a56bb8a5SSatoshi Asami  *    documentation and/or other materials provided with the distribution.
21a56bb8a5SSatoshi Asami  * 3. All advertising materials mentioning features or use of this software
22a56bb8a5SSatoshi Asami  *    must display the following acknowledgement:
233b1746dfSPoul-Henning Kamp  *	This product includes software developed for the NetBSD Project
243b1746dfSPoul-Henning Kamp  *	by Jason R. Thorpe.
253b1746dfSPoul-Henning Kamp  * 4. The names of the authors may not be used to endorse or promote products
263b1746dfSPoul-Henning Kamp  *    derived from this software without specific prior written permission.
27a56bb8a5SSatoshi Asami  *
283b1746dfSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
293b1746dfSPoul-Henning Kamp  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
303b1746dfSPoul-Henning Kamp  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
313b1746dfSPoul-Henning Kamp  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
323b1746dfSPoul-Henning Kamp  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
333b1746dfSPoul-Henning Kamp  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
343b1746dfSPoul-Henning Kamp  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
353b1746dfSPoul-Henning Kamp  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
363b1746dfSPoul-Henning Kamp  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37a56bb8a5SSatoshi Asami  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38a56bb8a5SSatoshi Asami  * SUCH DAMAGE.
39a56bb8a5SSatoshi Asami  *
40a56bb8a5SSatoshi Asami  * Dynamic configuration and disklabel support by:
41a56bb8a5SSatoshi Asami  *	Jason R. Thorpe <thorpej@nas.nasa.gov>
42a56bb8a5SSatoshi Asami  *	Numerical Aerodynamic Simulation Facility
43a56bb8a5SSatoshi Asami  *	Mail Stop 258-6
44a56bb8a5SSatoshi Asami  *	NASA Ames Research Center
45a56bb8a5SSatoshi Asami  *	Moffett Field, CA 94035
463b1746dfSPoul-Henning Kamp  *
473b1746dfSPoul-Henning Kamp  * from: Utah $Hdr: cd.c 1.6 90/11/28$
483b1746dfSPoul-Henning Kamp  *
493b1746dfSPoul-Henning Kamp  *	@(#)cd.c	8.2 (Berkeley) 11/16/93
503b1746dfSPoul-Henning Kamp  *
513b1746dfSPoul-Henning Kamp  *	$NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $
523b1746dfSPoul-Henning Kamp  *
533b1746dfSPoul-Henning Kamp  * $FreeBSD$
54a56bb8a5SSatoshi Asami  */
55a56bb8a5SSatoshi Asami 
56a56bb8a5SSatoshi Asami #include <sys/param.h>
57a56bb8a5SSatoshi Asami #include <sys/systm.h>
58e2a13e8cSSatoshi Asami #include <sys/kernel.h>
59b7b98418SPeter Wemm #include <sys/module.h>
60a56bb8a5SSatoshi Asami #include <sys/proc.h>
619626b608SPoul-Henning Kamp #include <sys/bio.h>
62a56bb8a5SSatoshi Asami #include <sys/malloc.h>
63a56bb8a5SSatoshi Asami #include <sys/namei.h>
64a56bb8a5SSatoshi Asami #include <sys/conf.h>
65a56bb8a5SSatoshi Asami #include <sys/stat.h>
662dd527b3SPoul-Henning Kamp #include <sys/disk.h>
67a56bb8a5SSatoshi Asami #include <sys/fcntl.h>
68a56bb8a5SSatoshi Asami #include <sys/vnode.h>
69189337d8SPoul-Henning Kamp #include <geom/geom.h>
70891619a6SPoul-Henning Kamp #include <geom/geom_disk.h>
71a56bb8a5SSatoshi Asami 
72d8594dfbSSatoshi Asami #include <sys/ccdvar.h>
73a56bb8a5SSatoshi Asami 
740f557e0aSPoul-Henning Kamp /*
750f557e0aSPoul-Henning Kamp  * Component info table.
760f557e0aSPoul-Henning Kamp  * Describes a single component of a concatenated disk.
770f557e0aSPoul-Henning Kamp  */
780f557e0aSPoul-Henning Kamp struct ccdcinfo {
790f557e0aSPoul-Henning Kamp 	struct vnode	*ci_vp;			/* device's vnode */
800f557e0aSPoul-Henning Kamp 	dev_t		ci_dev;			/* XXX: device's dev_t */
810f557e0aSPoul-Henning Kamp 	size_t		ci_size; 		/* size */
820f557e0aSPoul-Henning Kamp 	char		*ci_path;		/* path to component */
830f557e0aSPoul-Henning Kamp 	size_t		ci_pathlen;		/* length of component path */
840f557e0aSPoul-Henning Kamp };
850f557e0aSPoul-Henning Kamp 
860f557e0aSPoul-Henning Kamp /*
870f557e0aSPoul-Henning Kamp  * Interleave description table.
880f557e0aSPoul-Henning Kamp  * Computed at boot time to speed irregular-interleave lookups.
890f557e0aSPoul-Henning Kamp  * The idea is that we interleave in "groups".  First we interleave
900f557e0aSPoul-Henning Kamp  * evenly over all component disks up to the size of the smallest
910f557e0aSPoul-Henning Kamp  * component (the first group), then we interleave evenly over all
920f557e0aSPoul-Henning Kamp  * remaining disks up to the size of the next-smallest (second group),
930f557e0aSPoul-Henning Kamp  * and so on.
940f557e0aSPoul-Henning Kamp  *
950f557e0aSPoul-Henning Kamp  * Each table entry describes the interleave characteristics of one
960f557e0aSPoul-Henning Kamp  * of these groups.  For example if a concatenated disk consisted of
970f557e0aSPoul-Henning Kamp  * three components of 5, 3, and 7 DEV_BSIZE blocks interleaved at
980f557e0aSPoul-Henning Kamp  * DEV_BSIZE (1), the table would have three entries:
990f557e0aSPoul-Henning Kamp  *
1000f557e0aSPoul-Henning Kamp  *	ndisk	startblk	startoff	dev
1010f557e0aSPoul-Henning Kamp  *	3	0		0		0, 1, 2
1020f557e0aSPoul-Henning Kamp  *	2	9		3		0, 2
1030f557e0aSPoul-Henning Kamp  *	1	13		5		2
1040f557e0aSPoul-Henning Kamp  *	0	-		-		-
1050f557e0aSPoul-Henning Kamp  *
1060f557e0aSPoul-Henning Kamp  * which says that the first nine blocks (0-8) are interleaved over
1070f557e0aSPoul-Henning Kamp  * 3 disks (0, 1, 2) starting at block offset 0 on any component disk,
1080f557e0aSPoul-Henning Kamp  * the next 4 blocks (9-12) are interleaved over 2 disks (0, 2) starting
1090f557e0aSPoul-Henning Kamp  * at component block 3, and the remaining blocks (13-14) are on disk
1100f557e0aSPoul-Henning Kamp  * 2 starting at offset 5.
1110f557e0aSPoul-Henning Kamp  */
1120f557e0aSPoul-Henning Kamp struct ccdiinfo {
1130f557e0aSPoul-Henning Kamp 	int	ii_ndisk;	/* # of disks range is interleaved over */
1140f557e0aSPoul-Henning Kamp 	daddr_t	ii_startblk;	/* starting scaled block # for range */
1150f557e0aSPoul-Henning Kamp 	daddr_t	ii_startoff;	/* starting component offset (block #) */
1160f557e0aSPoul-Henning Kamp 	int	*ii_index;	/* ordered list of components in range */
1170f557e0aSPoul-Henning Kamp };
1180f557e0aSPoul-Henning Kamp 
1190f557e0aSPoul-Henning Kamp /*
1200f557e0aSPoul-Henning Kamp  * Concatenated disk pseudo-geometry information.
1210f557e0aSPoul-Henning Kamp  */
1220f557e0aSPoul-Henning Kamp struct ccdgeom {
1230f557e0aSPoul-Henning Kamp 	u_int32_t	ccg_secsize;	/* # bytes per sector */
1240f557e0aSPoul-Henning Kamp 	u_int32_t	ccg_nsectors;	/* # data sectors per track */
1250f557e0aSPoul-Henning Kamp 	u_int32_t	ccg_ntracks;	/* # tracks per cylinder */
1260f557e0aSPoul-Henning Kamp 	u_int32_t	ccg_ncylinders;	/* # cylinders per unit */
1270f557e0aSPoul-Henning Kamp };
1280f557e0aSPoul-Henning Kamp 
1290f557e0aSPoul-Henning Kamp 
1300f557e0aSPoul-Henning Kamp /*
1310f557e0aSPoul-Henning Kamp  * A concatenated disk is described by this structure.
1320f557e0aSPoul-Henning Kamp  */
1330f557e0aSPoul-Henning Kamp struct ccd_s {
1340f557e0aSPoul-Henning Kamp 	LIST_ENTRY(ccd_s) list;
1350f557e0aSPoul-Henning Kamp 
1360f557e0aSPoul-Henning Kamp 	int		 sc_unit;		/* logical unit number */
1370f557e0aSPoul-Henning Kamp 	struct vnode	 **sc_vpp;		/* array of component vnodes */
1380f557e0aSPoul-Henning Kamp 	int		 sc_flags;		/* flags */
1390f557e0aSPoul-Henning Kamp 	int		 sc_cflags;		/* configuration flags */
1400f557e0aSPoul-Henning Kamp 	size_t		 sc_size;		/* size of ccd */
1410f557e0aSPoul-Henning Kamp 	int		 sc_ileave;		/* interleave */
1420f557e0aSPoul-Henning Kamp 	u_int		 sc_nccdisks;		/* number of components */
1430f557e0aSPoul-Henning Kamp #define	CCD_MAXNDISKS	 65536
1440f557e0aSPoul-Henning Kamp 	struct ccdcinfo	 *sc_cinfo;		/* component info */
1450f557e0aSPoul-Henning Kamp 	struct ccdiinfo	 *sc_itable;		/* interleave table */
1460f557e0aSPoul-Henning Kamp 	struct ccdgeom   sc_geom;		/* pseudo geometry info */
1470f557e0aSPoul-Henning Kamp 	int		 sc_pick;		/* side of mirror picked */
1480f557e0aSPoul-Henning Kamp 	daddr_t		 sc_blk[2];		/* mirror localization */
1490f557e0aSPoul-Henning Kamp 	struct disk	 *sc_disk;
1500f557e0aSPoul-Henning Kamp 	struct cdev	 *__remove00;		/* XXX: remove when convenient */
1510f557e0aSPoul-Henning Kamp };
1520f557e0aSPoul-Henning Kamp 
15301706d20SPoul-Henning Kamp MALLOC_DEFINE(M_CCD, "CCD driver", "Concatenated Disk driver");
15401706d20SPoul-Henning Kamp 
155e7322872SSatoshi Asami /*
156e7322872SSatoshi Asami    This is how mirroring works (only writes are special):
157e7322872SSatoshi Asami 
158e7322872SSatoshi Asami    When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s
159e7322872SSatoshi Asami    linked together by the cb_mirror field.  "cb_pflags &
160e7322872SSatoshi Asami    CCDPF_MIRROR_DONE" is set to 0 on both of them.
161e7322872SSatoshi Asami 
162e7322872SSatoshi Asami    When a component returns to ccdiodone(), it checks if "cb_pflags &
163e7322872SSatoshi Asami    CCDPF_MIRROR_DONE" is set or not.  If not, it sets the partner's
164e7322872SSatoshi Asami    flag and returns.  If it is, it means its partner has already
165e7322872SSatoshi Asami    returned, so it will go to the regular cleanup.
166e7322872SSatoshi Asami 
167e7322872SSatoshi Asami  */
168e7322872SSatoshi Asami 
169a56bb8a5SSatoshi Asami struct ccdbuf {
1709d7f7369SPoul-Henning Kamp 	struct bio	cb_buf;		/* new I/O buf */
1719d7f7369SPoul-Henning Kamp 	struct bio	*cb_obp;	/* ptr. to original I/O buf */
1721464240eSMatthew Dillon 	struct ccdbuf	*cb_freenext;	/* free list link */
1730f76d6d8SPoul-Henning Kamp 	struct ccd_s	*cb_softc;
174a56bb8a5SSatoshi Asami 	int		cb_comp;	/* target component */
175e7322872SSatoshi Asami 	int		cb_pflags;	/* mirror/parity status flag */
176e7322872SSatoshi Asami 	struct ccdbuf	*cb_mirror;	/* mirror counterpart */
177a56bb8a5SSatoshi Asami };
178a56bb8a5SSatoshi Asami 
179e7322872SSatoshi Asami /* bits in cb_pflags */
180e7322872SSatoshi Asami #define CCDPF_MIRROR_DONE 1	/* if set, mirror counterpart is done */
181e7322872SSatoshi Asami 
18201706d20SPoul-Henning Kamp /* convinient macros for often-used statements */
18301706d20SPoul-Henning Kamp #define IS_ALLOCATED(unit)	(ccdfind(unit) != NULL)
18401706d20SPoul-Henning Kamp #define IS_INITED(cs)		(((cs)->sc_flags & CCDF_INITED) != 0)
18501706d20SPoul-Henning Kamp 
186ddbf51afSPoul-Henning Kamp static dev_t	ccdctldev;
187ddbf51afSPoul-Henning Kamp 
188ad3467e1SPoul-Henning Kamp static disk_strategy_t ccdstrategy;
1890f76d6d8SPoul-Henning Kamp static d_ioctl_t ccdctlioctl;
190d8594dfbSSatoshi Asami 
1911464240eSMatthew Dillon #define NCCDFREEHIWAT	16
1921464240eSMatthew Dillon 
193e2a13e8cSSatoshi Asami #define CDEV_MAJOR 74
194a56bb8a5SSatoshi Asami 
1950f76d6d8SPoul-Henning Kamp static struct cdevsw ccdctl_cdevsw = {
1967ac40f5fSPoul-Henning Kamp 	.d_open =	nullopen,
1977ac40f5fSPoul-Henning Kamp 	.d_close =	nullclose,
1987ac40f5fSPoul-Henning Kamp 	.d_ioctl =	ccdctlioctl,
1997ac40f5fSPoul-Henning Kamp 	.d_name =	"ccdctl",
2007ac40f5fSPoul-Henning Kamp 	.d_maj =	CDEV_MAJOR,
2010f76d6d8SPoul-Henning Kamp };
2020f76d6d8SPoul-Henning Kamp 
2030f76d6d8SPoul-Henning Kamp static LIST_HEAD(, ccd_s) ccd_softc_list =
2040f76d6d8SPoul-Henning Kamp 	LIST_HEAD_INITIALIZER(&ccd_softc_list);
20501706d20SPoul-Henning Kamp 
20601706d20SPoul-Henning Kamp static struct ccd_s *ccdfind(int);
20701706d20SPoul-Henning Kamp static struct ccd_s *ccdnew(int);
2080f76d6d8SPoul-Henning Kamp static int ccddestroy(struct ccd_s *);
209e2a13e8cSSatoshi Asami 
210b7b98418SPeter Wemm /* called during module initialization */
21101706d20SPoul-Henning Kamp static void ccdattach(void);
21201706d20SPoul-Henning Kamp static int ccd_modevent(module_t, int, void *);
213a56bb8a5SSatoshi Asami 
214a56bb8a5SSatoshi Asami /* called by biodone() at interrupt time */
21501706d20SPoul-Henning Kamp static void ccdiodone(struct bio *bp);
216a56bb8a5SSatoshi Asami 
21701706d20SPoul-Henning Kamp static void ccdstart(struct ccd_s *, struct bio *);
21801706d20SPoul-Henning Kamp static void ccdinterleave(struct ccd_s *, int);
219b40ce416SJulian Elischer static int ccdinit(struct ccd_s *, char **, struct thread *);
220b40ce416SJulian Elischer static int ccdlookup(char *, struct thread *p, struct vnode **);
2210f76d6d8SPoul-Henning Kamp static int ccdbuffer(struct ccdbuf **ret, struct ccd_s *,
22201706d20SPoul-Henning Kamp 		      struct bio *, daddr_t, caddr_t, long);
22301706d20SPoul-Henning Kamp static int ccdlock(struct ccd_s *);
22401706d20SPoul-Henning Kamp static void ccdunlock(struct ccd_s *);
225a56bb8a5SSatoshi Asami 
226a56bb8a5SSatoshi Asami 
2271464240eSMatthew Dillon /*
2280d88ef07SSatoshi Asami  * Number of blocks to untouched in front of a component partition.
2290d88ef07SSatoshi Asami  * This is to avoid violating its disklabel area when it starts at the
2300d88ef07SSatoshi Asami  * beginning of the slice.
2310d88ef07SSatoshi Asami  */
2321af0e025SSatoshi Asami #if !defined(CCD_OFFSET)
2330d88ef07SSatoshi Asami #define CCD_OFFSET 16
2341af0e025SSatoshi Asami #endif
2350d88ef07SSatoshi Asami 
23601706d20SPoul-Henning Kamp static struct ccd_s *
23701706d20SPoul-Henning Kamp ccdfind(int unit)
23801706d20SPoul-Henning Kamp {
23901706d20SPoul-Henning Kamp 	struct ccd_s *sc = NULL;
24001706d20SPoul-Henning Kamp 
24101706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
24201706d20SPoul-Henning Kamp 	LIST_FOREACH(sc, &ccd_softc_list, list) {
24301706d20SPoul-Henning Kamp 		if (sc->sc_unit == unit)
24401706d20SPoul-Henning Kamp 			break;
24501706d20SPoul-Henning Kamp 	}
24601706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
24701706d20SPoul-Henning Kamp 	return ((sc == NULL) || (sc->sc_unit != unit) ? NULL : sc);
24801706d20SPoul-Henning Kamp }
24901706d20SPoul-Henning Kamp 
25001706d20SPoul-Henning Kamp static struct ccd_s *
25101706d20SPoul-Henning Kamp ccdnew(int unit)
25201706d20SPoul-Henning Kamp {
25301706d20SPoul-Henning Kamp 	struct ccd_s *sc;
25401706d20SPoul-Henning Kamp 
25501706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
2560f76d6d8SPoul-Henning Kamp 	if (IS_ALLOCATED(unit) || unit > 32)
25701706d20SPoul-Henning Kamp 		return (NULL);
25801706d20SPoul-Henning Kamp 
259a163d034SWarner Losh 	MALLOC(sc, struct ccd_s *, sizeof(*sc), M_CCD, M_WAITOK | M_ZERO);
26001706d20SPoul-Henning Kamp 	sc->sc_unit = unit;
26101706d20SPoul-Henning Kamp 	LIST_INSERT_HEAD(&ccd_softc_list, sc, list);
26201706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
26301706d20SPoul-Henning Kamp 	return (sc);
26401706d20SPoul-Henning Kamp }
26501706d20SPoul-Henning Kamp 
26601706d20SPoul-Henning Kamp static int
2670f76d6d8SPoul-Henning Kamp ccddestroy(struct ccd_s *sc)
26801706d20SPoul-Henning Kamp {
26901706d20SPoul-Henning Kamp 
27001706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
27101706d20SPoul-Henning Kamp 	LIST_REMOVE(sc, list);
27201706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
27301706d20SPoul-Henning Kamp 	FREE(sc, M_CCD);
27401706d20SPoul-Henning Kamp 	return (0);
27501706d20SPoul-Henning Kamp }
27601706d20SPoul-Henning Kamp 
2770d88ef07SSatoshi Asami /*
278a56bb8a5SSatoshi Asami  * Called by main() during pseudo-device attachment.  All we need
27901706d20SPoul-Henning Kamp  * to do is to add devsw entries.
280a56bb8a5SSatoshi Asami  */
281e2738b4fSPoul-Henning Kamp static void
282b7b98418SPeter Wemm ccdattach()
283a56bb8a5SSatoshi Asami {
284a56bb8a5SSatoshi Asami 
2850f76d6d8SPoul-Henning Kamp 	ccdctldev = make_dev(&ccdctl_cdevsw, 0xffff00ff,
286ddbf51afSPoul-Henning Kamp 		UID_ROOT, GID_OPERATOR, 0640, "ccd.ctl");
287ddbf51afSPoul-Henning Kamp 	ccdctldev->si_drv1 = ccdctldev;
288b7b98418SPeter Wemm }
289d8594dfbSSatoshi Asami 
290b7b98418SPeter Wemm static int
29101706d20SPoul-Henning Kamp ccd_modevent(module_t mod, int type, void *data)
292b7b98418SPeter Wemm {
293b7b98418SPeter Wemm 	int error = 0;
294b7b98418SPeter Wemm 
295b7b98418SPeter Wemm 	switch (type) {
296b7b98418SPeter Wemm 	case MOD_LOAD:
297b7b98418SPeter Wemm 		ccdattach();
298b7b98418SPeter Wemm 		break;
299b7b98418SPeter Wemm 
300b7b98418SPeter Wemm 	case MOD_UNLOAD:
301b7b98418SPeter Wemm 		printf("ccd0: Unload not supported!\n");
302b7b98418SPeter Wemm 		error = EOPNOTSUPP;
303b7b98418SPeter Wemm 		break;
304b7b98418SPeter Wemm 
30555a13f7dSIan Dowse 	case MOD_SHUTDOWN:
306b7b98418SPeter Wemm 		break;
30755a13f7dSIan Dowse 
30855a13f7dSIan Dowse 	default:
30955a13f7dSIan Dowse 		error = EOPNOTSUPP;
310e2a13e8cSSatoshi Asami 	}
311b7b98418SPeter Wemm 	return (error);
312e2a13e8cSSatoshi Asami }
313b7b98418SPeter Wemm 
314d53dedeeSPoul-Henning Kamp DEV_MODULE(ccd, ccd_modevent, NULL);
315a56bb8a5SSatoshi Asami 
316a56bb8a5SSatoshi Asami static int
317b40ce416SJulian Elischer ccdinit(struct ccd_s *cs, char **cpaths, struct thread *td)
318a56bb8a5SSatoshi Asami {
3191464240eSMatthew Dillon 	struct ccdcinfo *ci = NULL;	/* XXX */
3201464240eSMatthew Dillon 	size_t size;
3211464240eSMatthew Dillon 	int ix;
322a56bb8a5SSatoshi Asami 	struct vnode *vp;
323a56bb8a5SSatoshi Asami 	size_t minsize;
324a56bb8a5SSatoshi Asami 	int maxsecsize;
325a56bb8a5SSatoshi Asami 	struct ccdgeom *ccg = &cs->sc_geom;
326e3f4d3b5SPoul-Henning Kamp 	char *tmppath = NULL;
3271464240eSMatthew Dillon 	int error = 0;
328ffee6e99SPoul-Henning Kamp 	off_t mediasize;
329ffee6e99SPoul-Henning Kamp 	u_int sectorsize;
330a56bb8a5SSatoshi Asami 
331a56bb8a5SSatoshi Asami 
332a56bb8a5SSatoshi Asami 	cs->sc_size = 0;
333a56bb8a5SSatoshi Asami 
334a56bb8a5SSatoshi Asami 	/* Allocate space for the component info. */
335a56bb8a5SSatoshi Asami 	cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
336a163d034SWarner Losh 	    M_CCD, M_WAITOK);
337a56bb8a5SSatoshi Asami 
338a56bb8a5SSatoshi Asami 	/*
339a56bb8a5SSatoshi Asami 	 * Verify that each component piece exists and record
340a56bb8a5SSatoshi Asami 	 * relevant information about it.
341a56bb8a5SSatoshi Asami 	 */
342a56bb8a5SSatoshi Asami 	maxsecsize = 0;
343a56bb8a5SSatoshi Asami 	minsize = 0;
344a163d034SWarner Losh 	tmppath = malloc(MAXPATHLEN, M_CCD, M_WAITOK);
345a56bb8a5SSatoshi Asami 	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
34601706d20SPoul-Henning Kamp 		vp = cs->sc_vpp[ix];
347a56bb8a5SSatoshi Asami 		ci = &cs->sc_cinfo[ix];
348a56bb8a5SSatoshi Asami 		ci->ci_vp = vp;
349a56bb8a5SSatoshi Asami 
350a56bb8a5SSatoshi Asami 		/*
351a56bb8a5SSatoshi Asami 		 * Copy in the pathname of the component.
352a56bb8a5SSatoshi Asami 		 */
353b4e36adfSMatthew Dillon 		if ((error = copyinstr(cpaths[ix], tmppath,
354b4e36adfSMatthew Dillon 		    MAXPATHLEN, &ci->ci_pathlen)) != 0) {
3551464240eSMatthew Dillon 			goto fail;
356a56bb8a5SSatoshi Asami 		}
357a163d034SWarner Losh 		ci->ci_path = malloc(ci->ci_pathlen, M_CCD, M_WAITOK);
358a56bb8a5SSatoshi Asami 		bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
359a56bb8a5SSatoshi Asami 
360684adedeSPoul-Henning Kamp 		ci->ci_dev = vn_todev(vp);
361a56bb8a5SSatoshi Asami 
362a56bb8a5SSatoshi Asami 		/*
363a56bb8a5SSatoshi Asami 		 * Get partition information for the component.
364a56bb8a5SSatoshi Asami 		 */
365ffee6e99SPoul-Henning Kamp 		error = VOP_IOCTL(vp, DIOCGMEDIASIZE, (caddr_t)&mediasize,
366ffee6e99SPoul-Henning Kamp 		    FREAD, td->td_ucred, td);
367ffee6e99SPoul-Henning Kamp 		if (error != 0) {
3681464240eSMatthew Dillon 			goto fail;
369a56bb8a5SSatoshi Asami 		}
370ffee6e99SPoul-Henning Kamp 		/*
371ffee6e99SPoul-Henning Kamp 		 * Get partition information for the component.
372ffee6e99SPoul-Henning Kamp 		 */
373ffee6e99SPoul-Henning Kamp 		error = VOP_IOCTL(vp, DIOCGSECTORSIZE, (caddr_t)&sectorsize,
374ffee6e99SPoul-Henning Kamp 		    FREAD, td->td_ucred, td);
375ffee6e99SPoul-Henning Kamp 		if (error != 0) {
3761464240eSMatthew Dillon 			goto fail;
377a56bb8a5SSatoshi Asami 		}
378ffee6e99SPoul-Henning Kamp 		if (sectorsize > maxsecsize)
379ffee6e99SPoul-Henning Kamp 			maxsecsize = sectorsize;
380ffee6e99SPoul-Henning Kamp 		size = mediasize / DEV_BSIZE - CCD_OFFSET;
381a56bb8a5SSatoshi Asami 
382a56bb8a5SSatoshi Asami 		/*
383a56bb8a5SSatoshi Asami 		 * Calculate the size, truncating to an interleave
384a56bb8a5SSatoshi Asami 		 * boundary if necessary.
385a56bb8a5SSatoshi Asami 		 */
386a56bb8a5SSatoshi Asami 
387a56bb8a5SSatoshi Asami 		if (cs->sc_ileave > 1)
388a56bb8a5SSatoshi Asami 			size -= size % cs->sc_ileave;
389a56bb8a5SSatoshi Asami 
390a56bb8a5SSatoshi Asami 		if (size == 0) {
3911464240eSMatthew Dillon 			error = ENODEV;
3921464240eSMatthew Dillon 			goto fail;
393a56bb8a5SSatoshi Asami 		}
394a56bb8a5SSatoshi Asami 
395a56bb8a5SSatoshi Asami 		if (minsize == 0 || size < minsize)
396a56bb8a5SSatoshi Asami 			minsize = size;
397a56bb8a5SSatoshi Asami 		ci->ci_size = size;
398a56bb8a5SSatoshi Asami 		cs->sc_size += size;
399a56bb8a5SSatoshi Asami 	}
400a56bb8a5SSatoshi Asami 
401b51ea356SPoul-Henning Kamp 	free(tmppath, M_CCD);
402e3f4d3b5SPoul-Henning Kamp 	tmppath = NULL;
403e3f4d3b5SPoul-Henning Kamp 
404a56bb8a5SSatoshi Asami 	/*
405a56bb8a5SSatoshi Asami 	 * Don't allow the interleave to be smaller than
406a56bb8a5SSatoshi Asami 	 * the biggest component sector.
407a56bb8a5SSatoshi Asami 	 */
408a56bb8a5SSatoshi Asami 	if ((cs->sc_ileave > 0) &&
409a56bb8a5SSatoshi Asami 	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
4101464240eSMatthew Dillon 		error = EINVAL;
4111464240eSMatthew Dillon 		goto fail;
412a56bb8a5SSatoshi Asami 	}
413a56bb8a5SSatoshi Asami 
414a56bb8a5SSatoshi Asami 	/*
415a56bb8a5SSatoshi Asami 	 * If uniform interleave is desired set all sizes to that of
4161464240eSMatthew Dillon 	 * the smallest component.  This will guarentee that a single
4171464240eSMatthew Dillon 	 * interleave table is generated.
4181464240eSMatthew Dillon 	 *
4191464240eSMatthew Dillon 	 * Lost space must be taken into account when calculating the
4201464240eSMatthew Dillon 	 * overall size.  Half the space is lost when CCDF_MIRROR is
421ddbf51afSPoul-Henning Kamp 	 * specified.
422a56bb8a5SSatoshi Asami 	 */
42301706d20SPoul-Henning Kamp 	if (cs->sc_flags & CCDF_UNIFORM) {
424a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo;
4251464240eSMatthew Dillon 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
426a56bb8a5SSatoshi Asami 			ci->ci_size = minsize;
4271464240eSMatthew Dillon 		}
42801706d20SPoul-Henning Kamp 		if (cs->sc_flags & CCDF_MIRROR) {
42934f35216SSatoshi Asami 			/*
43034f35216SSatoshi Asami 			 * Check to see if an even number of components
4311464240eSMatthew Dillon 			 * have been specified.  The interleave must also
4321464240eSMatthew Dillon 			 * be non-zero in order for us to be able to
4331464240eSMatthew Dillon 			 * guarentee the topology.
43434f35216SSatoshi Asami 			 */
43509b59204SSatoshi Asami 			if (cs->sc_nccdisks % 2) {
43601706d20SPoul-Henning Kamp 				printf("ccd%d: mirroring requires an even number of disks\n", cs->sc_unit );
4371464240eSMatthew Dillon 				error = EINVAL;
4381464240eSMatthew Dillon 				goto fail;
43934f35216SSatoshi Asami 			}
4401464240eSMatthew Dillon 			if (cs->sc_ileave == 0) {
44101706d20SPoul-Henning Kamp 				printf("ccd%d: an interleave must be specified when mirroring\n", cs->sc_unit);
4421464240eSMatthew Dillon 				error = EINVAL;
4431464240eSMatthew Dillon 				goto fail;
44409b59204SSatoshi Asami 			}
44509b59204SSatoshi Asami 			cs->sc_size = (cs->sc_nccdisks/2) * minsize;
4461464240eSMatthew Dillon 		} else {
4471464240eSMatthew Dillon 			if (cs->sc_ileave == 0) {
44801706d20SPoul-Henning Kamp 				printf("ccd%d: an interleave must be specified when using parity\n", cs->sc_unit);
4491464240eSMatthew Dillon 				error = EINVAL;
4501464240eSMatthew Dillon 				goto fail;
4511464240eSMatthew Dillon 			}
452a56bb8a5SSatoshi Asami 			cs->sc_size = cs->sc_nccdisks * minsize;
453a56bb8a5SSatoshi Asami 		}
4541464240eSMatthew Dillon 	}
455a56bb8a5SSatoshi Asami 
456a56bb8a5SSatoshi Asami 	/*
457a56bb8a5SSatoshi Asami 	 * Construct the interleave table.
458a56bb8a5SSatoshi Asami 	 */
45901706d20SPoul-Henning Kamp 	ccdinterleave(cs, cs->sc_unit);
460a56bb8a5SSatoshi Asami 
461a56bb8a5SSatoshi Asami 	/*
462a56bb8a5SSatoshi Asami 	 * Create pseudo-geometry based on 1MB cylinders.  It's
463a56bb8a5SSatoshi Asami 	 * pretty close.
464a56bb8a5SSatoshi Asami 	 */
465e59f3105SSøren Schmidt 	ccg->ccg_secsize = maxsecsize;
466a56bb8a5SSatoshi Asami 	ccg->ccg_ntracks = 1;
467e322ec4cSMatthew Dillon 	ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize;
468a56bb8a5SSatoshi Asami 	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
469a56bb8a5SSatoshi Asami 
470a56bb8a5SSatoshi Asami 	cs->sc_flags |= CCDF_INITED;
47101706d20SPoul-Henning Kamp 	cs->sc_cflags = cs->sc_flags;	/* So we can find out later... */
472a56bb8a5SSatoshi Asami 	return (0);
4731464240eSMatthew Dillon fail:
4741464240eSMatthew Dillon 	while (ci > cs->sc_cinfo) {
4751464240eSMatthew Dillon 		ci--;
476b51ea356SPoul-Henning Kamp 		free(ci->ci_path, M_CCD);
4771464240eSMatthew Dillon 	}
478e3f4d3b5SPoul-Henning Kamp 	if (tmppath != NULL)
479b51ea356SPoul-Henning Kamp 		free(tmppath, M_CCD);
480b51ea356SPoul-Henning Kamp 	free(cs->sc_cinfo, M_CCD);
4810f76d6d8SPoul-Henning Kamp 	ccddestroy(cs);
4821464240eSMatthew Dillon 	return (error);
483a56bb8a5SSatoshi Asami }
484a56bb8a5SSatoshi Asami 
485a56bb8a5SSatoshi Asami static void
48601706d20SPoul-Henning Kamp ccdinterleave(struct ccd_s *cs, int unit)
487a56bb8a5SSatoshi Asami {
4881464240eSMatthew Dillon 	struct ccdcinfo *ci, *smallci;
4891464240eSMatthew Dillon 	struct ccdiinfo *ii;
4901464240eSMatthew Dillon 	daddr_t bn, lbn;
4911464240eSMatthew Dillon 	int ix;
492a56bb8a5SSatoshi Asami 	u_long size;
493a56bb8a5SSatoshi Asami 
4941464240eSMatthew Dillon 
495a56bb8a5SSatoshi Asami 	/*
4961464240eSMatthew Dillon 	 * Allocate an interleave table.  The worst case occurs when each
4971464240eSMatthew Dillon 	 * of N disks is of a different size, resulting in N interleave
4981464240eSMatthew Dillon 	 * tables.
4991464240eSMatthew Dillon 	 *
500a56bb8a5SSatoshi Asami 	 * Chances are this is too big, but we don't care.
501a56bb8a5SSatoshi Asami 	 */
502a56bb8a5SSatoshi Asami 	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
503b51ea356SPoul-Henning Kamp 	cs->sc_itable = (struct ccdiinfo *)malloc(size, M_CCD,
504a163d034SWarner Losh 	    M_WAITOK | M_ZERO);
505a56bb8a5SSatoshi Asami 
506a56bb8a5SSatoshi Asami 	/*
507a56bb8a5SSatoshi Asami 	 * Trivial case: no interleave (actually interleave of disk size).
508a56bb8a5SSatoshi Asami 	 * Each table entry represents a single component in its entirety.
5091464240eSMatthew Dillon 	 *
510ddbf51afSPoul-Henning Kamp 	 * An interleave of 0 may not be used with a mirror setup.
511a56bb8a5SSatoshi Asami 	 */
512a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0) {
513a56bb8a5SSatoshi Asami 		bn = 0;
514a56bb8a5SSatoshi Asami 		ii = cs->sc_itable;
515a56bb8a5SSatoshi Asami 
516a56bb8a5SSatoshi Asami 		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
517a56bb8a5SSatoshi Asami 			/* Allocate space for ii_index. */
518a163d034SWarner Losh 			ii->ii_index = malloc(sizeof(int), M_CCD, M_WAITOK);
519a56bb8a5SSatoshi Asami 			ii->ii_ndisk = 1;
520a56bb8a5SSatoshi Asami 			ii->ii_startblk = bn;
521a56bb8a5SSatoshi Asami 			ii->ii_startoff = 0;
522a56bb8a5SSatoshi Asami 			ii->ii_index[0] = ix;
523a56bb8a5SSatoshi Asami 			bn += cs->sc_cinfo[ix].ci_size;
524a56bb8a5SSatoshi Asami 			ii++;
525a56bb8a5SSatoshi Asami 		}
526a56bb8a5SSatoshi Asami 		ii->ii_ndisk = 0;
527a56bb8a5SSatoshi Asami 		return;
528a56bb8a5SSatoshi Asami 	}
529a56bb8a5SSatoshi Asami 
530a56bb8a5SSatoshi Asami 	/*
531a56bb8a5SSatoshi Asami 	 * The following isn't fast or pretty; it doesn't have to be.
532a56bb8a5SSatoshi Asami 	 */
533a56bb8a5SSatoshi Asami 	size = 0;
534a56bb8a5SSatoshi Asami 	bn = lbn = 0;
535a56bb8a5SSatoshi Asami 	for (ii = cs->sc_itable; ; ii++) {
5361464240eSMatthew Dillon 		/*
5371464240eSMatthew Dillon 		 * Allocate space for ii_index.  We might allocate more then
5381464240eSMatthew Dillon 		 * we use.
5391464240eSMatthew Dillon 		 */
540a56bb8a5SSatoshi Asami 		ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks),
541a163d034SWarner Losh 		    M_CCD, M_WAITOK);
542a56bb8a5SSatoshi Asami 
543a56bb8a5SSatoshi Asami 		/*
544a56bb8a5SSatoshi Asami 		 * Locate the smallest of the remaining components
545a56bb8a5SSatoshi Asami 		 */
546a56bb8a5SSatoshi Asami 		smallci = NULL;
5471464240eSMatthew Dillon 		for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks];
5481464240eSMatthew Dillon 		    ci++) {
549a56bb8a5SSatoshi Asami 			if (ci->ci_size > size &&
550a56bb8a5SSatoshi Asami 			    (smallci == NULL ||
5511464240eSMatthew Dillon 			     ci->ci_size < smallci->ci_size)) {
552a56bb8a5SSatoshi Asami 				smallci = ci;
5531464240eSMatthew Dillon 			}
5541464240eSMatthew Dillon 		}
555a56bb8a5SSatoshi Asami 
556a56bb8a5SSatoshi Asami 		/*
557a56bb8a5SSatoshi Asami 		 * Nobody left, all done
558a56bb8a5SSatoshi Asami 		 */
559a56bb8a5SSatoshi Asami 		if (smallci == NULL) {
560a56bb8a5SSatoshi Asami 			ii->ii_ndisk = 0;
561e9fe7d1fSPoul-Henning Kamp 			free(ii->ii_index, M_CCD);
562a56bb8a5SSatoshi Asami 			break;
563a56bb8a5SSatoshi Asami 		}
564a56bb8a5SSatoshi Asami 
565a56bb8a5SSatoshi Asami 		/*
5661464240eSMatthew Dillon 		 * Record starting logical block using an sc_ileave blocksize.
567a56bb8a5SSatoshi Asami 		 */
568a56bb8a5SSatoshi Asami 		ii->ii_startblk = bn / cs->sc_ileave;
5691464240eSMatthew Dillon 
5701464240eSMatthew Dillon 		/*
5711464240eSMatthew Dillon 		 * Record starting comopnent block using an sc_ileave
5721464240eSMatthew Dillon 		 * blocksize.  This value is relative to the beginning of
5731464240eSMatthew Dillon 		 * a component disk.
5741464240eSMatthew Dillon 		 */
575a56bb8a5SSatoshi Asami 		ii->ii_startoff = lbn;
576a56bb8a5SSatoshi Asami 
577a56bb8a5SSatoshi Asami 		/*
578a56bb8a5SSatoshi Asami 		 * Determine how many disks take part in this interleave
579a56bb8a5SSatoshi Asami 		 * and record their indices.
580a56bb8a5SSatoshi Asami 		 */
581a56bb8a5SSatoshi Asami 		ix = 0;
582a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo;
5831464240eSMatthew Dillon 		    ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
5841464240eSMatthew Dillon 			if (ci->ci_size >= smallci->ci_size) {
585a56bb8a5SSatoshi Asami 				ii->ii_index[ix++] = ci - cs->sc_cinfo;
5861464240eSMatthew Dillon 			}
5871464240eSMatthew Dillon 		}
588a56bb8a5SSatoshi Asami 		ii->ii_ndisk = ix;
589a56bb8a5SSatoshi Asami 		bn += ix * (smallci->ci_size - size);
590a56bb8a5SSatoshi Asami 		lbn = smallci->ci_size / cs->sc_ileave;
591a56bb8a5SSatoshi Asami 		size = smallci->ci_size;
592a56bb8a5SSatoshi Asami 	}
593a56bb8a5SSatoshi Asami }
594a56bb8a5SSatoshi Asami 
595e2738b4fSPoul-Henning Kamp static void
59601706d20SPoul-Henning Kamp ccdstrategy(struct bio *bp)
597a56bb8a5SSatoshi Asami {
5980f76d6d8SPoul-Henning Kamp 	struct ccd_s *cs;
59974427f90SMatthew Dillon 	int pbn;        /* in sc_secsize chunks */
60074427f90SMatthew Dillon 	long sz;        /* in sc_secsize chunks */
60174427f90SMatthew Dillon 
602ad3467e1SPoul-Henning Kamp 	cs = bp->bio_disk->d_drv1;
6030f76d6d8SPoul-Henning Kamp 
6049d7f7369SPoul-Henning Kamp 	pbn = bp->bio_blkno / (cs->sc_geom.ccg_secsize / DEV_BSIZE);
6059d7f7369SPoul-Henning Kamp 	sz = howmany(bp->bio_bcount, cs->sc_geom.ccg_secsize);
60674427f90SMatthew Dillon 
60774427f90SMatthew Dillon 	/*
60874427f90SMatthew Dillon 	 * If out of bounds return an error. If at the EOF point,
60974427f90SMatthew Dillon 	 * simply read or write less.
61074427f90SMatthew Dillon 	 */
61174427f90SMatthew Dillon 
61274427f90SMatthew Dillon 	if (pbn < 0 || pbn >= cs->sc_size) {
6139d7f7369SPoul-Henning Kamp 		bp->bio_resid = bp->bio_bcount;
614724682d2SPoul-Henning Kamp 		if (pbn != cs->sc_size)
615724682d2SPoul-Henning Kamp 			biofinish(bp, NULL, EINVAL);
616724682d2SPoul-Henning Kamp 		else
617724682d2SPoul-Henning Kamp 			biodone(bp);
618724682d2SPoul-Henning Kamp 		return;
61974427f90SMatthew Dillon 	}
62074427f90SMatthew Dillon 
62174427f90SMatthew Dillon 	/*
62274427f90SMatthew Dillon 	 * If the request crosses EOF, truncate the request.
62374427f90SMatthew Dillon 	 */
62474427f90SMatthew Dillon 	if (pbn + sz > cs->sc_size) {
6259d7f7369SPoul-Henning Kamp 		bp->bio_bcount = (cs->sc_size - pbn) *
62674427f90SMatthew Dillon 		    cs->sc_geom.ccg_secsize;
62774427f90SMatthew Dillon 	}
628a56bb8a5SSatoshi Asami 
6299d7f7369SPoul-Henning Kamp 	bp->bio_resid = bp->bio_bcount;
630a56bb8a5SSatoshi Asami 
631a56bb8a5SSatoshi Asami 	/*
632a56bb8a5SSatoshi Asami 	 * "Start" the unit.
633a56bb8a5SSatoshi Asami 	 */
634a56bb8a5SSatoshi Asami 	ccdstart(cs, bp);
635a56bb8a5SSatoshi Asami 	return;
636a56bb8a5SSatoshi Asami }
637a56bb8a5SSatoshi Asami 
638a56bb8a5SSatoshi Asami static void
63901706d20SPoul-Henning Kamp ccdstart(struct ccd_s *cs, struct bio *bp)
640a56bb8a5SSatoshi Asami {
6411464240eSMatthew Dillon 	long bcount, rcount;
6420f76d6d8SPoul-Henning Kamp 	struct ccdbuf *cbp[2];
643a56bb8a5SSatoshi Asami 	caddr_t addr;
644a56bb8a5SSatoshi Asami 	daddr_t bn;
6450f76d6d8SPoul-Henning Kamp 	int err;
64677154759SPoul-Henning Kamp 	int sent;
647a56bb8a5SSatoshi Asami 
648a56bb8a5SSatoshi Asami 	/*
649a56bb8a5SSatoshi Asami 	 * Translate the partition-relative block number to an absolute.
650a56bb8a5SSatoshi Asami 	 */
6519d7f7369SPoul-Henning Kamp 	bn = bp->bio_blkno;
652a56bb8a5SSatoshi Asami 
653a56bb8a5SSatoshi Asami 	/*
654a56bb8a5SSatoshi Asami 	 * Allocate component buffers and fire off the requests
655a56bb8a5SSatoshi Asami 	 */
6569d7f7369SPoul-Henning Kamp 	addr = bp->bio_data;
65777154759SPoul-Henning Kamp 	sent = 0;
6589d7f7369SPoul-Henning Kamp 	for (bcount = bp->bio_bcount; bcount > 0; bcount -= rcount) {
6590f76d6d8SPoul-Henning Kamp 		err = ccdbuffer(cbp, cs, bp, bn, addr, bcount);
6600f76d6d8SPoul-Henning Kamp 		if (err) {
6610f76d6d8SPoul-Henning Kamp 			printf("ccdbuffer error %d\n", err);
66277154759SPoul-Henning Kamp 			if (!sent)
6632f912fc9SPoul-Henning Kamp 				biofinish(bp, NULL, err);
66477154759SPoul-Henning Kamp 			else {
66577154759SPoul-Henning Kamp 				/*
66677154759SPoul-Henning Kamp 				 * XXX: maybe a race where the partners
66777154759SPoul-Henning Kamp 				 * XXX: we sent already have been in
66877154759SPoul-Henning Kamp 				 * XXX: ccdiodone().  Single-threaded g_down
66977154759SPoul-Henning Kamp 				 * XXX: may protect against this.
67077154759SPoul-Henning Kamp 				 */
67177154759SPoul-Henning Kamp 				bp->bio_resid -= bcount;
67277154759SPoul-Henning Kamp 				bp->bio_error = err;
67377154759SPoul-Henning Kamp 				bp->bio_flags |= BIO_ERROR;
67477154759SPoul-Henning Kamp 			}
6750f76d6d8SPoul-Henning Kamp 			return;
6760f76d6d8SPoul-Henning Kamp 		}
6779d7f7369SPoul-Henning Kamp 		rcount = cbp[0]->cb_buf.bio_bcount;
6781464240eSMatthew Dillon 
6791464240eSMatthew Dillon 		if (cs->sc_cflags & CCDF_MIRROR) {
6801464240eSMatthew Dillon 			/*
6811464240eSMatthew Dillon 			 * Mirroring.  Writes go to both disks, reads are
6821464240eSMatthew Dillon 			 * taken from whichever disk seems most appropriate.
6831464240eSMatthew Dillon 			 *
6841464240eSMatthew Dillon 			 * We attempt to localize reads to the disk whos arm
6851464240eSMatthew Dillon 			 * is nearest the read request.  We ignore seeks due
6861464240eSMatthew Dillon 			 * to writes when making this determination and we
6871464240eSMatthew Dillon 			 * also try to avoid hogging.
6881464240eSMatthew Dillon 			 */
6899d7f7369SPoul-Henning Kamp 			if (cbp[0]->cb_buf.bio_cmd == BIO_WRITE) {
690d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[0]->cb_buf);
691d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[1]->cb_buf);
69277154759SPoul-Henning Kamp 				sent++;
6931464240eSMatthew Dillon 			} else {
6941464240eSMatthew Dillon 				int pick = cs->sc_pick;
6951464240eSMatthew Dillon 				daddr_t range = cs->sc_size / 16;
6961464240eSMatthew Dillon 
6971464240eSMatthew Dillon 				if (bn < cs->sc_blk[pick] - range ||
6981464240eSMatthew Dillon 				    bn > cs->sc_blk[pick] + range
6991464240eSMatthew Dillon 				) {
7001464240eSMatthew Dillon 					cs->sc_pick = pick = 1 - pick;
7011464240eSMatthew Dillon 				}
7021464240eSMatthew Dillon 				cs->sc_blk[pick] = bn + btodb(rcount);
703d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[pick]->cb_buf);
70477154759SPoul-Henning Kamp 				sent++;
7051464240eSMatthew Dillon 			}
7061464240eSMatthew Dillon 		} else {
7071464240eSMatthew Dillon 			/*
7081464240eSMatthew Dillon 			 * Not mirroring
7091464240eSMatthew Dillon 			 */
710d616ee08SPoul-Henning Kamp 			BIO_STRATEGY(&cbp[0]->cb_buf);
71177154759SPoul-Henning Kamp 			sent++;
7123bc746beSSatoshi Asami 		}
713a56bb8a5SSatoshi Asami 		bn += btodb(rcount);
714a56bb8a5SSatoshi Asami 		addr += rcount;
715a56bb8a5SSatoshi Asami 	}
716a56bb8a5SSatoshi Asami }
717a56bb8a5SSatoshi Asami 
718a56bb8a5SSatoshi Asami /*
719a56bb8a5SSatoshi Asami  * Build a component buffer header.
720a56bb8a5SSatoshi Asami  */
7210f76d6d8SPoul-Henning Kamp static int
72201706d20SPoul-Henning Kamp ccdbuffer(struct ccdbuf **cb, struct ccd_s *cs, struct bio *bp, daddr_t bn, caddr_t addr, long bcount)
723a56bb8a5SSatoshi Asami {
7241464240eSMatthew Dillon 	struct ccdcinfo *ci, *ci2 = NULL;	/* XXX */
7251464240eSMatthew Dillon 	struct ccdbuf *cbp;
7261464240eSMatthew Dillon 	daddr_t cbn, cboff;
7271464240eSMatthew Dillon 	off_t cbc;
728a56bb8a5SSatoshi Asami 
729a56bb8a5SSatoshi Asami 	/*
730a56bb8a5SSatoshi Asami 	 * Determine which component bn falls in.
731a56bb8a5SSatoshi Asami 	 */
732a56bb8a5SSatoshi Asami 	cbn = bn;
733a56bb8a5SSatoshi Asami 	cboff = 0;
734a56bb8a5SSatoshi Asami 
735a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0) {
7361464240eSMatthew Dillon 		/*
7371464240eSMatthew Dillon 		 * Serially concatenated and neither a mirror nor a parity
7381464240eSMatthew Dillon 		 * config.  This is a special case.
7391464240eSMatthew Dillon 		 */
7401464240eSMatthew Dillon 		daddr_t sblk;
741a56bb8a5SSatoshi Asami 
742a56bb8a5SSatoshi Asami 		sblk = 0;
743a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
744a56bb8a5SSatoshi Asami 			sblk += ci->ci_size;
745a56bb8a5SSatoshi Asami 		cbn -= sblk;
7461464240eSMatthew Dillon 	} else {
7471464240eSMatthew Dillon 		struct ccdiinfo *ii;
748a56bb8a5SSatoshi Asami 		int ccdisk, off;
749a56bb8a5SSatoshi Asami 
7501464240eSMatthew Dillon 		/*
7511464240eSMatthew Dillon 		 * Calculate cbn, the logical superblock (sc_ileave chunks),
7521464240eSMatthew Dillon 		 * and cboff, a normal block offset (DEV_BSIZE chunks) relative
7531464240eSMatthew Dillon 		 * to cbn.
7541464240eSMatthew Dillon 		 */
7551464240eSMatthew Dillon 		cboff = cbn % cs->sc_ileave;	/* DEV_BSIZE gran */
7561464240eSMatthew Dillon 		cbn = cbn / cs->sc_ileave;	/* DEV_BSIZE * ileave gran */
7571464240eSMatthew Dillon 
7581464240eSMatthew Dillon 		/*
7591464240eSMatthew Dillon 		 * Figure out which interleave table to use.
7601464240eSMatthew Dillon 		 */
7611464240eSMatthew Dillon 		for (ii = cs->sc_itable; ii->ii_ndisk; ii++) {
762a56bb8a5SSatoshi Asami 			if (ii->ii_startblk > cbn)
763a56bb8a5SSatoshi Asami 				break;
7641464240eSMatthew Dillon 		}
765a56bb8a5SSatoshi Asami 		ii--;
7661464240eSMatthew Dillon 
7671464240eSMatthew Dillon 		/*
7681464240eSMatthew Dillon 		 * off is the logical superblock relative to the beginning
7691464240eSMatthew Dillon 		 * of this interleave block.
7701464240eSMatthew Dillon 		 */
771a56bb8a5SSatoshi Asami 		off = cbn - ii->ii_startblk;
7721464240eSMatthew Dillon 
7731464240eSMatthew Dillon 		/*
7741464240eSMatthew Dillon 		 * We must calculate which disk component to use (ccdisk),
7751464240eSMatthew Dillon 		 * and recalculate cbn to be the superblock relative to
7761464240eSMatthew Dillon 		 * the beginning of the component.  This is typically done by
7771464240eSMatthew Dillon 		 * adding 'off' and ii->ii_startoff together.  However, 'off'
7781464240eSMatthew Dillon 		 * must typically be divided by the number of components in
7791464240eSMatthew Dillon 		 * this interleave array to be properly convert it from a
7801464240eSMatthew Dillon 		 * CCD-relative logical superblock number to a
7811464240eSMatthew Dillon 		 * component-relative superblock number.
7821464240eSMatthew Dillon 		 */
783a56bb8a5SSatoshi Asami 		if (ii->ii_ndisk == 1) {
7841464240eSMatthew Dillon 			/*
7851464240eSMatthew Dillon 			 * When we have just one disk, it can't be a mirror
7861464240eSMatthew Dillon 			 * or a parity config.
7871464240eSMatthew Dillon 			 */
788a56bb8a5SSatoshi Asami 			ccdisk = ii->ii_index[0];
789a56bb8a5SSatoshi Asami 			cbn = ii->ii_startoff + off;
790a56bb8a5SSatoshi Asami 		} else {
79109b59204SSatoshi Asami 			if (cs->sc_cflags & CCDF_MIRROR) {
7921464240eSMatthew Dillon 				/*
7931464240eSMatthew Dillon 				 * We have forced a uniform mapping, resulting
7941464240eSMatthew Dillon 				 * in a single interleave array.  We double
7951464240eSMatthew Dillon 				 * up on the first half of the available
7961464240eSMatthew Dillon 				 * components and our mirror is in the second
7971464240eSMatthew Dillon 				 * half.  This only works with a single
7981464240eSMatthew Dillon 				 * interleave array because doubling up
7991464240eSMatthew Dillon 				 * doubles the number of sectors, so there
8001464240eSMatthew Dillon 				 * cannot be another interleave array because
8011464240eSMatthew Dillon 				 * the next interleave array's calculations
8021464240eSMatthew Dillon 				 * would be off.
8031464240eSMatthew Dillon 				 */
8041464240eSMatthew Dillon 				int ndisk2 = ii->ii_ndisk / 2;
8051464240eSMatthew Dillon 				ccdisk = ii->ii_index[off % ndisk2];
8061464240eSMatthew Dillon 				cbn = ii->ii_startoff + off / ndisk2;
8071464240eSMatthew Dillon 				ci2 = &cs->sc_cinfo[ccdisk + ndisk2];
8081464240eSMatthew Dillon 			} else {
809a56bb8a5SSatoshi Asami 				ccdisk = ii->ii_index[off % ii->ii_ndisk];
810a56bb8a5SSatoshi Asami 				cbn = ii->ii_startoff + off / ii->ii_ndisk;
811a56bb8a5SSatoshi Asami 			}
8127ecb65faSSatoshi Asami 		}
8131464240eSMatthew Dillon 
814a56bb8a5SSatoshi Asami 		ci = &cs->sc_cinfo[ccdisk];
8151464240eSMatthew Dillon 
8161464240eSMatthew Dillon 		/*
8171464240eSMatthew Dillon 		 * Convert cbn from a superblock to a normal block so it
8181464240eSMatthew Dillon 		 * can be used to calculate (along with cboff) the normal
8191464240eSMatthew Dillon 		 * block index into this particular disk.
8201464240eSMatthew Dillon 		 */
8211464240eSMatthew Dillon 		cbn *= cs->sc_ileave;
822a56bb8a5SSatoshi Asami 	}
823a56bb8a5SSatoshi Asami 
824a56bb8a5SSatoshi Asami 	/*
825a56bb8a5SSatoshi Asami 	 * Fill in the component buf structure.
826a56bb8a5SSatoshi Asami 	 */
8270f76d6d8SPoul-Henning Kamp 	cbp = malloc(sizeof(struct ccdbuf), M_CCD, M_NOWAIT | M_ZERO);
8280f76d6d8SPoul-Henning Kamp 	if (cbp == NULL)
8290f76d6d8SPoul-Henning Kamp 		return (ENOMEM);
8309d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_cmd = bp->bio_cmd;
8319d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_done = ccdiodone;
8329d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_dev = ci->ci_dev;		/* XXX */
8339d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_blkno = cbn + cboff + CCD_OFFSET;
8349d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_offset = dbtob(cbn + cboff + CCD_OFFSET);
8359d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_data = addr;
8360f76d6d8SPoul-Henning Kamp 	cbp->cb_buf.bio_caller2 = cbp;
837a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0)
83840969e38SDavid Greenman               cbc = dbtob((off_t)(ci->ci_size - cbn));
839a56bb8a5SSatoshi Asami 	else
84040969e38SDavid Greenman               cbc = dbtob((off_t)(cs->sc_ileave - cboff));
8419d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_bcount = (cbc < bcount) ? cbc : bcount;
8429d7f7369SPoul-Henning Kamp  	cbp->cb_buf.bio_caller1 = (void*)cbp->cb_buf.bio_bcount;
843c0b89506SJohn Dyson 
844a56bb8a5SSatoshi Asami 	/*
845a56bb8a5SSatoshi Asami 	 * context for ccdiodone
846a56bb8a5SSatoshi Asami 	 */
847a56bb8a5SSatoshi Asami 	cbp->cb_obp = bp;
8480f76d6d8SPoul-Henning Kamp 	cbp->cb_softc = cs;
849a56bb8a5SSatoshi Asami 	cbp->cb_comp = ci - cs->sc_cinfo;
850a56bb8a5SSatoshi Asami 
8513bc746beSSatoshi Asami 	cb[0] = cbp;
8521464240eSMatthew Dillon 
8531464240eSMatthew Dillon 	/*
8541464240eSMatthew Dillon 	 * Note: both I/O's setup when reading from mirror, but only one
8551464240eSMatthew Dillon 	 * will be executed.
8561464240eSMatthew Dillon 	 */
8571464240eSMatthew Dillon 	if (cs->sc_cflags & CCDF_MIRROR) {
8581464240eSMatthew Dillon 		/* mirror, setup second I/O */
8590f76d6d8SPoul-Henning Kamp 		cbp = malloc(sizeof(struct ccdbuf), M_CCD, M_NOWAIT);
8600f76d6d8SPoul-Henning Kamp 		if (cbp == NULL) {
8610f76d6d8SPoul-Henning Kamp 			free(cb[0], M_CCD);
8620f76d6d8SPoul-Henning Kamp 			cb[0] = NULL;
8630f76d6d8SPoul-Henning Kamp 			return (ENOMEM);
8640f76d6d8SPoul-Henning Kamp 		}
865e9fe7d1fSPoul-Henning Kamp 		bcopy(cb[0], cbp, sizeof(struct ccdbuf));
8660238f932SPoul-Henning Kamp 		cbp->cb_buf.bio_caller2 = cbp;
8679d7f7369SPoul-Henning Kamp 		cbp->cb_buf.bio_dev = ci2->ci_dev;
8683bc746beSSatoshi Asami 		cbp->cb_comp = ci2 - cs->sc_cinfo;
8693bc746beSSatoshi Asami 		cb[1] = cbp;
870e7322872SSatoshi Asami 		/* link together the ccdbuf's and clear "mirror done" flag */
871e7322872SSatoshi Asami 		cb[0]->cb_mirror = cb[1];
872e7322872SSatoshi Asami 		cb[1]->cb_mirror = cb[0];
873e7322872SSatoshi Asami 		cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
874e7322872SSatoshi Asami 		cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
8753bc746beSSatoshi Asami 	}
8760f76d6d8SPoul-Henning Kamp 	return (0);
877a56bb8a5SSatoshi Asami }
878a56bb8a5SSatoshi Asami 
879a56bb8a5SSatoshi Asami /*
880a56bb8a5SSatoshi Asami  * Called at interrupt time.
881a56bb8a5SSatoshi Asami  * Mark the component as done and if all components are done,
882a56bb8a5SSatoshi Asami  * take a ccd interrupt.
883a56bb8a5SSatoshi Asami  */
884e2738b4fSPoul-Henning Kamp static void
88501706d20SPoul-Henning Kamp ccdiodone(struct bio *ibp)
886a56bb8a5SSatoshi Asami {
8870f76d6d8SPoul-Henning Kamp 	struct ccdbuf *cbp;
8880f76d6d8SPoul-Henning Kamp 	struct bio *bp;
889360d71d1SPoul-Henning Kamp 	struct ccd_s *cs;
8900f76d6d8SPoul-Henning Kamp 	int count;
891a56bb8a5SSatoshi Asami 
8920f76d6d8SPoul-Henning Kamp 	cbp = ibp->bio_caller2;
8930f76d6d8SPoul-Henning Kamp 	cs = cbp->cb_softc;
8940f76d6d8SPoul-Henning Kamp 	bp = cbp->cb_obp;
8951464240eSMatthew Dillon 	/*
8961464240eSMatthew Dillon 	 * If an error occured, report it.  If this is a mirrored
8971464240eSMatthew Dillon 	 * configuration and the first of two possible reads, do not
8981464240eSMatthew Dillon 	 * set the error in the bp yet because the second read may
8991464240eSMatthew Dillon 	 * succeed.
9001464240eSMatthew Dillon 	 */
901a56bb8a5SSatoshi Asami 
9029d7f7369SPoul-Henning Kamp 	if (cbp->cb_buf.bio_flags & BIO_ERROR) {
9031464240eSMatthew Dillon 		const char *msg = "";
9041464240eSMatthew Dillon 
905360d71d1SPoul-Henning Kamp 		if ((cs->sc_cflags & CCDF_MIRROR) &&
9069d7f7369SPoul-Henning Kamp 		    (cbp->cb_buf.bio_cmd == BIO_READ) &&
9071464240eSMatthew Dillon 		    (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
9081464240eSMatthew Dillon 			/*
9091464240eSMatthew Dillon 			 * We will try our read on the other disk down
9101464240eSMatthew Dillon 			 * below, also reverse the default pick so if we
9111464240eSMatthew Dillon 			 * are doing a scan we do not keep hitting the
9121464240eSMatthew Dillon 			 * bad disk first.
9131464240eSMatthew Dillon 			 */
9141464240eSMatthew Dillon 
9151464240eSMatthew Dillon 			msg = ", trying other disk";
9161464240eSMatthew Dillon 			cs->sc_pick = 1 - cs->sc_pick;
9179d7f7369SPoul-Henning Kamp 			cs->sc_blk[cs->sc_pick] = bp->bio_blkno;
9181464240eSMatthew Dillon 		} else {
9199d7f7369SPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
9209d7f7369SPoul-Henning Kamp 			bp->bio_error = cbp->cb_buf.bio_error ?
9219d7f7369SPoul-Henning Kamp 			    cbp->cb_buf.bio_error : EIO;
9221464240eSMatthew Dillon 		}
92392faa7b5SMaxime Henrion 		printf("ccd%d: error %d on component %d block %jd "
9240f76d6d8SPoul-Henning Kamp 		    "(ccd block %jd)%s\n", cs->sc_unit, bp->bio_error,
9250f76d6d8SPoul-Henning Kamp 		    cbp->cb_comp,
92692faa7b5SMaxime Henrion 		    (intmax_t)cbp->cb_buf.bio_blkno, (intmax_t)bp->bio_blkno,
92792faa7b5SMaxime Henrion 		    msg);
928a56bb8a5SSatoshi Asami 	}
929e7322872SSatoshi Asami 
9301464240eSMatthew Dillon 	/*
9311464240eSMatthew Dillon 	 * Process mirror.  If we are writing, I/O has been initiated on both
9321464240eSMatthew Dillon 	 * buffers and we fall through only after both are finished.
9331464240eSMatthew Dillon 	 *
9341464240eSMatthew Dillon 	 * If we are reading only one I/O is initiated at a time.  If an
9351464240eSMatthew Dillon 	 * error occurs we initiate the second I/O and return, otherwise
9361464240eSMatthew Dillon 	 * we free the second I/O without initiating it.
9371464240eSMatthew Dillon 	 */
9381464240eSMatthew Dillon 
939360d71d1SPoul-Henning Kamp 	if (cs->sc_cflags & CCDF_MIRROR) {
9409d7f7369SPoul-Henning Kamp 		if (cbp->cb_buf.bio_cmd == BIO_WRITE) {
9411464240eSMatthew Dillon 			/*
9421464240eSMatthew Dillon 			 * When writing, handshake with the second buffer
9431464240eSMatthew Dillon 			 * to determine when both are done.  If both are not
9441464240eSMatthew Dillon 			 * done, return here.
9451464240eSMatthew Dillon 			 */
946e7322872SSatoshi Asami 			if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
947e7322872SSatoshi Asami 				cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
948e9fe7d1fSPoul-Henning Kamp 				free(cbp, M_CCD);
949e7322872SSatoshi Asami 				return;
950e7322872SSatoshi Asami 			}
9511464240eSMatthew Dillon 		} else {
9521464240eSMatthew Dillon 			/*
9531464240eSMatthew Dillon 			 * When reading, either dispose of the second buffer
9541464240eSMatthew Dillon 			 * or initiate I/O on the second buffer if an error
9551464240eSMatthew Dillon 			 * occured with this one.
9561464240eSMatthew Dillon 			 */
9571464240eSMatthew Dillon 			if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
9589d7f7369SPoul-Henning Kamp 				if (cbp->cb_buf.bio_flags & BIO_ERROR) {
9591464240eSMatthew Dillon 					cbp->cb_mirror->cb_pflags |=
9601464240eSMatthew Dillon 					    CCDPF_MIRROR_DONE;
961d616ee08SPoul-Henning Kamp 					BIO_STRATEGY(&cbp->cb_mirror->cb_buf);
962e9fe7d1fSPoul-Henning Kamp 					free(cbp, M_CCD);
9631464240eSMatthew Dillon 					return;
9641464240eSMatthew Dillon 				} else {
965e9fe7d1fSPoul-Henning Kamp 					free(cbp->cb_mirror, M_CCD);
9661464240eSMatthew Dillon 				}
9671464240eSMatthew Dillon 			}
9681464240eSMatthew Dillon 		}
9691464240eSMatthew Dillon 	}
970e7322872SSatoshi Asami 
97125d1a00bSMatthew Dillon 	/*
9729d7f7369SPoul-Henning Kamp 	 * use bio_caller1 to determine how big the original request was rather
9739d7f7369SPoul-Henning Kamp 	 * then bio_bcount, because bio_bcount may have been truncated for EOF.
97425d1a00bSMatthew Dillon 	 *
97525d1a00bSMatthew Dillon 	 * XXX We check for an error, but we do not test the resid for an
97625d1a00bSMatthew Dillon 	 * aligned EOF condition.  This may result in character & block
97725d1a00bSMatthew Dillon 	 * device access not recognizing EOF properly when read or written
97825d1a00bSMatthew Dillon 	 * sequentially, but will not effect filesystems.
97925d1a00bSMatthew Dillon 	 */
9809d7f7369SPoul-Henning Kamp 	count = (long)cbp->cb_buf.bio_caller1;
981e9fe7d1fSPoul-Henning Kamp 	free(cbp, M_CCD);
982a56bb8a5SSatoshi Asami 
983a56bb8a5SSatoshi Asami 	/*
984a56bb8a5SSatoshi Asami 	 * If all done, "interrupt".
985a56bb8a5SSatoshi Asami 	 */
9869d7f7369SPoul-Henning Kamp 	bp->bio_resid -= count;
9879d7f7369SPoul-Henning Kamp 	if (bp->bio_resid < 0)
988a56bb8a5SSatoshi Asami 		panic("ccdiodone: count");
989360d71d1SPoul-Henning Kamp 	if (bp->bio_resid == 0) {
990360d71d1SPoul-Henning Kamp 		if (bp->bio_flags & BIO_ERROR)
991360d71d1SPoul-Henning Kamp 			bp->bio_resid = bp->bio_bcount;
99260794e04SPoul-Henning Kamp 		biodone(bp);
993360d71d1SPoul-Henning Kamp 	}
994a56bb8a5SSatoshi Asami }
995a56bb8a5SSatoshi Asami 
9960f76d6d8SPoul-Henning Kamp static int ccdioctltoo(int unit, u_long cmd, caddr_t data, int flag, struct thread *td);
9970f76d6d8SPoul-Henning Kamp 
998e2738b4fSPoul-Henning Kamp static int
9990f76d6d8SPoul-Henning Kamp ccdctlioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
1000a56bb8a5SSatoshi Asami {
1001ddbf51afSPoul-Henning Kamp 	struct ccd_ioctl *ccio;
1002ddbf51afSPoul-Henning Kamp 	u_int unit;
1003ddbf51afSPoul-Henning Kamp 
1004ddbf51afSPoul-Henning Kamp 	switch (cmd) {
1005ddbf51afSPoul-Henning Kamp 	case CCDIOCSET:
1006ddbf51afSPoul-Henning Kamp 	case CCDIOCCLR:
1007ddbf51afSPoul-Henning Kamp 		ccio = (struct ccd_ioctl *)data;
1008ddbf51afSPoul-Henning Kamp 		unit = ccio->ccio_size;
10090f76d6d8SPoul-Henning Kamp 		return (ccdioctltoo(unit, cmd, data, flag, td));
1010ddbf51afSPoul-Henning Kamp 	default:
10110f557e0aSPoul-Henning Kamp 		return (ENOIOCTL);
1012ddbf51afSPoul-Henning Kamp 	}
1013ddbf51afSPoul-Henning Kamp }
1014ddbf51afSPoul-Henning Kamp 
1015ddbf51afSPoul-Henning Kamp static int
10160f76d6d8SPoul-Henning Kamp ccdioctltoo(int unit, u_long cmd, caddr_t data, int flag, struct thread *td)
1017ddbf51afSPoul-Henning Kamp {
1018a56bb8a5SSatoshi Asami 	int i, j, lookedup = 0, error = 0;
101901706d20SPoul-Henning Kamp 	struct ccd_s *cs;
1020a56bb8a5SSatoshi Asami 	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
10210f76d6d8SPoul-Henning Kamp 	struct ccdgeom *ccg;
1022a56bb8a5SSatoshi Asami 	char **cpp;
1023a56bb8a5SSatoshi Asami 	struct vnode **vpp;
1024a56bb8a5SSatoshi Asami 
102501706d20SPoul-Henning Kamp 	cs = ccdfind(unit);
1026a56bb8a5SSatoshi Asami 	switch (cmd) {
1027a56bb8a5SSatoshi Asami 	case CCDIOCSET:
10280f76d6d8SPoul-Henning Kamp 		if (cs == NULL)
10290f76d6d8SPoul-Henning Kamp 			cs = ccdnew(unit);
103001706d20SPoul-Henning Kamp 		if (IS_INITED(cs))
1031a56bb8a5SSatoshi Asami 			return (EBUSY);
1032a56bb8a5SSatoshi Asami 
1033a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1034a56bb8a5SSatoshi Asami 			return (EBADF);
1035a56bb8a5SSatoshi Asami 
1036b4e36adfSMatthew Dillon 		if ((error = ccdlock(cs)) != 0)
1037a56bb8a5SSatoshi Asami 			return (error);
1038a56bb8a5SSatoshi Asami 
1039bf61e266SKris Kennaway 		if (ccio->ccio_ndisks > CCD_MAXNDISKS)
1040bf61e266SKris Kennaway 			return (EINVAL);
1041bf61e266SKris Kennaway 
1042a56bb8a5SSatoshi Asami 		/* Fill in some important bits. */
104301706d20SPoul-Henning Kamp 		cs->sc_ileave = ccio->ccio_ileave;
1044f05f44f0SPoul-Henning Kamp 		if (cs->sc_ileave == 0 && (ccio->ccio_flags & CCDF_MIRROR)) {
1045f05f44f0SPoul-Henning Kamp 			printf("ccd%d: disabling mirror, interleave is 0\n",
1046f05f44f0SPoul-Henning Kamp 			    unit);
1047f05f44f0SPoul-Henning Kamp 			ccio->ccio_flags &= ~(CCDF_MIRROR);
1048b8e29b55SSatoshi Asami 		}
104909b59204SSatoshi Asami 		if ((ccio->ccio_flags & CCDF_MIRROR) &&
10507ecb65faSSatoshi Asami 		    !(ccio->ccio_flags & CCDF_UNIFORM)) {
105109b59204SSatoshi Asami 			printf("ccd%d: mirror/parity forces uniform flag\n",
105209b59204SSatoshi Asami 			       unit);
10537ecb65faSSatoshi Asami 			ccio->ccio_flags |= CCDF_UNIFORM;
10547ecb65faSSatoshi Asami 		}
105501706d20SPoul-Henning Kamp 		cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK;
1056a56bb8a5SSatoshi Asami 
1057a56bb8a5SSatoshi Asami 		/*
1058a56bb8a5SSatoshi Asami 		 * Allocate space for and copy in the array of
1059a56bb8a5SSatoshi Asami 		 * componet pathnames and device numbers.
1060a56bb8a5SSatoshi Asami 		 */
1061a56bb8a5SSatoshi Asami 		cpp = malloc(ccio->ccio_ndisks * sizeof(char *),
1062a163d034SWarner Losh 		    M_CCD, M_WAITOK);
1063a56bb8a5SSatoshi Asami 		vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *),
1064a163d034SWarner Losh 		    M_CCD, M_WAITOK);
1065a56bb8a5SSatoshi Asami 
1066a56bb8a5SSatoshi Asami 		error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
1067a56bb8a5SSatoshi Asami 		    ccio->ccio_ndisks * sizeof(char **));
1068a56bb8a5SSatoshi Asami 		if (error) {
1069b51ea356SPoul-Henning Kamp 			free(vpp, M_CCD);
1070b51ea356SPoul-Henning Kamp 			free(cpp, M_CCD);
1071a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1072a56bb8a5SSatoshi Asami 			return (error);
1073a56bb8a5SSatoshi Asami 		}
1074a56bb8a5SSatoshi Asami 
1075a56bb8a5SSatoshi Asami 
1076a56bb8a5SSatoshi Asami 		for (i = 0; i < ccio->ccio_ndisks; ++i) {
1077b40ce416SJulian Elischer 			if ((error = ccdlookup(cpp[i], td, &vpp[i])) != 0) {
1078a56bb8a5SSatoshi Asami 				for (j = 0; j < lookedup; ++j)
1079a56bb8a5SSatoshi Asami 					(void)vn_close(vpp[j], FREAD|FWRITE,
1080a854ed98SJohn Baldwin 					    td->td_ucred, td);
1081b51ea356SPoul-Henning Kamp 				free(vpp, M_CCD);
1082b51ea356SPoul-Henning Kamp 				free(cpp, M_CCD);
1083a56bb8a5SSatoshi Asami 				ccdunlock(cs);
1084a56bb8a5SSatoshi Asami 				return (error);
1085a56bb8a5SSatoshi Asami 			}
1086a56bb8a5SSatoshi Asami 			++lookedup;
1087a56bb8a5SSatoshi Asami 		}
108801706d20SPoul-Henning Kamp 		cs->sc_vpp = vpp;
108901706d20SPoul-Henning Kamp 		cs->sc_nccdisks = ccio->ccio_ndisks;
1090a56bb8a5SSatoshi Asami 
1091a56bb8a5SSatoshi Asami 		/*
1092a56bb8a5SSatoshi Asami 		 * Initialize the ccd.  Fills in the softc for us.
1093a56bb8a5SSatoshi Asami 		 */
1094b40ce416SJulian Elischer 		if ((error = ccdinit(cs, cpp, td)) != 0) {
1095a56bb8a5SSatoshi Asami 			for (j = 0; j < lookedup; ++j)
1096ba88dfc7SJohn Baldwin 				(void)vn_close(vpp[j], FREAD|FWRITE,
1097a854ed98SJohn Baldwin 				    td->td_ucred, td);
109801706d20SPoul-Henning Kamp 			/*
109901706d20SPoul-Henning Kamp 			 * We can't ccddestroy() cs just yet, because nothing
110001706d20SPoul-Henning Kamp 			 * prevents user-level app to do another ioctl()
110101706d20SPoul-Henning Kamp 			 * without closing the device first, therefore
110201706d20SPoul-Henning Kamp 			 * declare unit null and void and let ccdclose()
110301706d20SPoul-Henning Kamp 			 * destroy it when it is safe to do so.
110401706d20SPoul-Henning Kamp 			 */
110501706d20SPoul-Henning Kamp 			cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED);
1106b51ea356SPoul-Henning Kamp 			free(vpp, M_CCD);
1107b51ea356SPoul-Henning Kamp 			free(cpp, M_CCD);
1108a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1109a56bb8a5SSatoshi Asami 			return (error);
1110a56bb8a5SSatoshi Asami 		}
11116b267654SPoul-Henning Kamp 		free(cpp, M_CCD);
1112a56bb8a5SSatoshi Asami 
1113a56bb8a5SSatoshi Asami 		/*
1114a56bb8a5SSatoshi Asami 		 * The ccd has been successfully initialized, so
1115a56bb8a5SSatoshi Asami 		 * we can place it into the array and read the disklabel.
1116a56bb8a5SSatoshi Asami 		 */
1117a56bb8a5SSatoshi Asami 		ccio->ccio_unit = unit;
1118a56bb8a5SSatoshi Asami 		ccio->ccio_size = cs->sc_size;
11190f76d6d8SPoul-Henning Kamp 		ccg = &cs->sc_geom;
1120a163d034SWarner Losh 		cs->sc_disk = malloc(sizeof(struct disk), M_CCD,
1121a163d034SWarner Losh 		    M_ZERO | M_WAITOK);
1122806d5cffSPoul-Henning Kamp 		cs->sc_disk->d_strategy = ccdstrategy;
1123806d5cffSPoul-Henning Kamp 		cs->sc_disk->d_name = "ccd";
11240f76d6d8SPoul-Henning Kamp 		cs->sc_disk->d_sectorsize = ccg->ccg_secsize;
11250f76d6d8SPoul-Henning Kamp 		cs->sc_disk->d_mediasize =
11260f76d6d8SPoul-Henning Kamp 		    cs->sc_size * (off_t)ccg->ccg_secsize;
11270f76d6d8SPoul-Henning Kamp 		cs->sc_disk->d_fwsectors = ccg->ccg_nsectors;
11280f76d6d8SPoul-Henning Kamp 		cs->sc_disk->d_fwheads = ccg->ccg_ntracks;
1129ad3467e1SPoul-Henning Kamp 		cs->sc_disk->d_drv1 = cs;
1130ad3467e1SPoul-Henning Kamp 		cs->sc_disk->d_maxsize = MAXPHYS;
1131ad3467e1SPoul-Henning Kamp 		disk_create(unit, cs->sc_disk, 0, NULL, NULL);
1132a56bb8a5SSatoshi Asami 
1133a56bb8a5SSatoshi Asami 		ccdunlock(cs);
1134a56bb8a5SSatoshi Asami 
1135a56bb8a5SSatoshi Asami 		break;
1136a56bb8a5SSatoshi Asami 
1137a56bb8a5SSatoshi Asami 	case CCDIOCCLR:
11380f76d6d8SPoul-Henning Kamp 		if (cs == NULL)
11390f76d6d8SPoul-Henning Kamp 			return (ENXIO);
11400f76d6d8SPoul-Henning Kamp 
114101706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
1142a56bb8a5SSatoshi Asami 			return (ENXIO);
1143a56bb8a5SSatoshi Asami 
1144a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1145a56bb8a5SSatoshi Asami 			return (EBADF);
1146a56bb8a5SSatoshi Asami 
1147b4e36adfSMatthew Dillon 		if ((error = ccdlock(cs)) != 0)
1148a56bb8a5SSatoshi Asami 			return (error);
1149a56bb8a5SSatoshi Asami 
1150af8862e4SPoul-Henning Kamp 		/* Don't unconfigure if any other partitions are open */
1151806d5cffSPoul-Henning Kamp 		if (cs->sc_disk->d_flags & DISKFLAG_OPEN) {
1152a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1153a56bb8a5SSatoshi Asami 			return (EBUSY);
1154a56bb8a5SSatoshi Asami 		}
1155a56bb8a5SSatoshi Asami 
1156b82ff758SPoul-Henning Kamp 		disk_destroy(cs->sc_disk);
11570f76d6d8SPoul-Henning Kamp 		free(cs->sc_disk, M_CCD);
11580f76d6d8SPoul-Henning Kamp 		cs->sc_disk = NULL;
115901706d20SPoul-Henning Kamp 		/* Declare unit null and void (reset all flags) */
116001706d20SPoul-Henning Kamp 		cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED);
1161a56bb8a5SSatoshi Asami 
1162a56bb8a5SSatoshi Asami 		/* Close the components and free their pathnames. */
1163a56bb8a5SSatoshi Asami 		for (i = 0; i < cs->sc_nccdisks; ++i) {
1164a56bb8a5SSatoshi Asami 			/*
1165a56bb8a5SSatoshi Asami 			 * XXX: this close could potentially fail and
1166a56bb8a5SSatoshi Asami 			 * cause Bad Things.  Maybe we need to force
1167a56bb8a5SSatoshi Asami 			 * the close to happen?
1168a56bb8a5SSatoshi Asami 			 */
1169a56bb8a5SSatoshi Asami 			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1170a854ed98SJohn Baldwin 			    td->td_ucred, td);
1171b51ea356SPoul-Henning Kamp 			free(cs->sc_cinfo[i].ci_path, M_CCD);
1172a56bb8a5SSatoshi Asami 		}
1173a56bb8a5SSatoshi Asami 
1174a56bb8a5SSatoshi Asami 		/* Free interleave index. */
1175a56bb8a5SSatoshi Asami 		for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
1176b51ea356SPoul-Henning Kamp 			free(cs->sc_itable[i].ii_index, M_CCD);
1177a56bb8a5SSatoshi Asami 
1178a56bb8a5SSatoshi Asami 		/* Free component info and interleave table. */
1179b51ea356SPoul-Henning Kamp 		free(cs->sc_cinfo, M_CCD);
1180b51ea356SPoul-Henning Kamp 		free(cs->sc_itable, M_CCD);
1181b51ea356SPoul-Henning Kamp 		free(cs->sc_vpp, M_CCD);
1182a56bb8a5SSatoshi Asami 
1183a56bb8a5SSatoshi Asami 		/* This must be atomic. */
1184a56bb8a5SSatoshi Asami 		ccdunlock(cs);
11850f76d6d8SPoul-Henning Kamp 		ccddestroy(cs);
1186a56bb8a5SSatoshi Asami 
1187a56bb8a5SSatoshi Asami 		break;
1188a56bb8a5SSatoshi Asami 	}
1189a56bb8a5SSatoshi Asami 
1190a56bb8a5SSatoshi Asami 	return (0);
1191a56bb8a5SSatoshi Asami }
1192a56bb8a5SSatoshi Asami 
1193a56bb8a5SSatoshi Asami 
1194a56bb8a5SSatoshi Asami /*
1195a56bb8a5SSatoshi Asami  * Lookup the provided name in the filesystem.  If the file exists,
1196a56bb8a5SSatoshi Asami  * is a valid block device, and isn't being used by anyone else,
1197a56bb8a5SSatoshi Asami  * set *vpp to the file's vnode.
1198a56bb8a5SSatoshi Asami  */
1199a56bb8a5SSatoshi Asami static int
1200b40ce416SJulian Elischer ccdlookup(char *path, struct thread *td, struct vnode **vpp)
1201a56bb8a5SSatoshi Asami {
1202a56bb8a5SSatoshi Asami 	struct nameidata nd;
1203a56bb8a5SSatoshi Asami 	struct vnode *vp;
1204e6796b67SKirk McKusick 	int error, flags;
1205a56bb8a5SSatoshi Asami 
1206b40ce416SJulian Elischer 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, td);
1207e6796b67SKirk McKusick 	flags = FREAD | FWRITE;
1208e6796b67SKirk McKusick 	if ((error = vn_open(&nd, &flags, 0)) != 0) {
1209a56bb8a5SSatoshi Asami 		return (error);
1210a56bb8a5SSatoshi Asami 	}
1211a56bb8a5SSatoshi Asami 	vp = nd.ni_vp;
1212a56bb8a5SSatoshi Asami 
121337ab0e0dSJeff Roberson 	if (vrefcnt(vp) > 1) {
1214762e6b85SEivind Eklund 		error = EBUSY;
1215762e6b85SEivind Eklund 		goto bad;
1216a56bb8a5SSatoshi Asami 	}
1217a56bb8a5SSatoshi Asami 
1218ba4ad1fcSPoul-Henning Kamp 	if (!vn_isdisk(vp, &error))
1219762e6b85SEivind Eklund 		goto bad;
1220a56bb8a5SSatoshi Asami 
1221a56bb8a5SSatoshi Asami 
1222b40ce416SJulian Elischer 	VOP_UNLOCK(vp, 0, td);
1223762e6b85SEivind Eklund 	NDFREE(&nd, NDF_ONLY_PNBUF);
1224a56bb8a5SSatoshi Asami 	*vpp = vp;
1225a56bb8a5SSatoshi Asami 	return (0);
1226762e6b85SEivind Eklund bad:
1227b40ce416SJulian Elischer 	VOP_UNLOCK(vp, 0, td);
1228762e6b85SEivind Eklund 	NDFREE(&nd, NDF_ONLY_PNBUF);
1229762e6b85SEivind Eklund 	/* vn_close does vrele() for vp */
1230a854ed98SJohn Baldwin 	(void)vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
1231762e6b85SEivind Eklund 	return (error);
1232a56bb8a5SSatoshi Asami }
1233a56bb8a5SSatoshi Asami 
1234a56bb8a5SSatoshi Asami /*
1235a56bb8a5SSatoshi Asami 
1236a56bb8a5SSatoshi Asami  * Wait interruptibly for an exclusive lock.
1237a56bb8a5SSatoshi Asami  *
1238a56bb8a5SSatoshi Asami  * XXX
1239a56bb8a5SSatoshi Asami  * Several drivers do this; it should be abstracted and made MP-safe.
1240a56bb8a5SSatoshi Asami  */
1241a56bb8a5SSatoshi Asami static int
124201706d20SPoul-Henning Kamp ccdlock(struct ccd_s *cs)
1243a56bb8a5SSatoshi Asami {
1244a56bb8a5SSatoshi Asami 	int error;
1245a56bb8a5SSatoshi Asami 
1246a56bb8a5SSatoshi Asami 	while ((cs->sc_flags & CCDF_LOCKED) != 0) {
1247a56bb8a5SSatoshi Asami 		cs->sc_flags |= CCDF_WANTED;
1248a56bb8a5SSatoshi Asami 		if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0)
1249a56bb8a5SSatoshi Asami 			return (error);
1250a56bb8a5SSatoshi Asami 	}
1251a56bb8a5SSatoshi Asami 	cs->sc_flags |= CCDF_LOCKED;
1252a56bb8a5SSatoshi Asami 	return (0);
1253a56bb8a5SSatoshi Asami }
1254a56bb8a5SSatoshi Asami 
1255a56bb8a5SSatoshi Asami /*
1256a56bb8a5SSatoshi Asami  * Unlock and wake up any waiters.
1257a56bb8a5SSatoshi Asami  */
1258a56bb8a5SSatoshi Asami static void
125901706d20SPoul-Henning Kamp ccdunlock(struct ccd_s *cs)
1260a56bb8a5SSatoshi Asami {
1261a56bb8a5SSatoshi Asami 
1262a56bb8a5SSatoshi Asami 	cs->sc_flags &= ~CCDF_LOCKED;
1263a56bb8a5SSatoshi Asami 	if ((cs->sc_flags & CCDF_WANTED) != 0) {
1264a56bb8a5SSatoshi Asami 		cs->sc_flags &= ~CCDF_WANTED;
1265a56bb8a5SSatoshi Asami 		wakeup(cs);
1266a56bb8a5SSatoshi Asami 	}
1267a56bb8a5SSatoshi Asami }
1268189337d8SPoul-Henning Kamp 
1269189337d8SPoul-Henning Kamp static struct sbuf *
12700f557e0aSPoul-Henning Kamp g_ccd_list(int unit)
1271189337d8SPoul-Henning Kamp {
1272189337d8SPoul-Henning Kamp 	struct sbuf *sb;
1273189337d8SPoul-Henning Kamp 	struct ccd_s *cs;
1274189337d8SPoul-Henning Kamp 	int i;
1275189337d8SPoul-Henning Kamp 
1276189337d8SPoul-Henning Kamp 	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
1277189337d8SPoul-Henning Kamp 	sbuf_clear(sb);
1278189337d8SPoul-Henning Kamp 	LIST_FOREACH(cs, &ccd_softc_list, list) {
1279189337d8SPoul-Henning Kamp 		if (!IS_INITED(cs))
1280189337d8SPoul-Henning Kamp 			continue;
12810f557e0aSPoul-Henning Kamp 		if (unit >= 0 && unit != cs->sc_unit)
12820f557e0aSPoul-Henning Kamp 			continue;
1283189337d8SPoul-Henning Kamp 		sbuf_printf(sb, "ccd%d\t\t%d\t%d\t",
1284189337d8SPoul-Henning Kamp 		    cs->sc_unit, cs->sc_ileave, cs->sc_cflags & CCDF_USERMASK);
1285189337d8SPoul-Henning Kamp 
1286189337d8SPoul-Henning Kamp 		for (i = 0; i < cs->sc_nccdisks; ++i) {
1287189337d8SPoul-Henning Kamp 			sbuf_printf(sb, "%s%s", i == 0 ? "" : " ",
1288189337d8SPoul-Henning Kamp 			    cs->sc_cinfo[i].ci_path);
1289189337d8SPoul-Henning Kamp 		}
1290189337d8SPoul-Henning Kamp 		sbuf_printf(sb, "\n");
1291189337d8SPoul-Henning Kamp 	}
1292189337d8SPoul-Henning Kamp 	sbuf_finish(sb);
1293189337d8SPoul-Henning Kamp 	return (sb);
1294189337d8SPoul-Henning Kamp }
1295189337d8SPoul-Henning Kamp 
1296189337d8SPoul-Henning Kamp static void
1297189337d8SPoul-Henning Kamp g_ccd_config(struct gctl_req *req, struct g_class *mp, char const *verb)
1298189337d8SPoul-Henning Kamp {
1299189337d8SPoul-Henning Kamp 	struct sbuf *sb;
13000f557e0aSPoul-Henning Kamp 	int u, *up;
1301189337d8SPoul-Henning Kamp 
1302189337d8SPoul-Henning Kamp 	g_topology_assert();
1303189337d8SPoul-Henning Kamp 	if (!strcmp(verb, "create geom")) {
1304189337d8SPoul-Henning Kamp 		gctl_error(req, "TBD");
1305189337d8SPoul-Henning Kamp 	} else if (!strcmp(verb, "destroy geom")) {
1306189337d8SPoul-Henning Kamp 		gctl_error(req, "TBD");
1307189337d8SPoul-Henning Kamp 	} else if (!strcmp(verb, "list")) {
13080f557e0aSPoul-Henning Kamp 		up = gctl_get_paraml(req, "unit", sizeof (int));
13090f557e0aSPoul-Henning Kamp 		u = *up;
13100f557e0aSPoul-Henning Kamp 		sb = g_ccd_list(u);
1311189337d8SPoul-Henning Kamp 		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1312189337d8SPoul-Henning Kamp 	} else {
1313189337d8SPoul-Henning Kamp 		gctl_error(req, "unknown verb");
1314189337d8SPoul-Henning Kamp 	}
1315189337d8SPoul-Henning Kamp }
1316189337d8SPoul-Henning Kamp 
1317189337d8SPoul-Henning Kamp static struct g_class g_ccd_class = {
1318189337d8SPoul-Henning Kamp 	.name = "CCD",
1319189337d8SPoul-Henning Kamp 	.ctlreq = g_ccd_config,
1320189337d8SPoul-Henning Kamp };
1321189337d8SPoul-Henning Kamp 
1322189337d8SPoul-Henning Kamp DECLARE_GEOM_CLASS(g_ccd_class, g_ccd);
1323