xref: /freebsd/sys/geom/geom_ccd.c (revision 3b1746df8b34cbfb85904f68e3b3d6af4067a2ce)
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>
66f368af93SMaxime Henrion #include <sys/stdint.h>
67e2738b4fSPoul-Henning Kamp #include <sys/sysctl.h>
682dd527b3SPoul-Henning Kamp #include <sys/disk.h>
697812d86fSPoul-Henning Kamp #include <sys/disklabel.h>
70b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
71a56bb8a5SSatoshi Asami #include <sys/fcntl.h>
72a56bb8a5SSatoshi Asami #include <sys/vnode.h>
73a56bb8a5SSatoshi Asami 
74d8594dfbSSatoshi Asami #include <sys/ccdvar.h>
75a56bb8a5SSatoshi Asami 
7601706d20SPoul-Henning Kamp MALLOC_DEFINE(M_CCD, "CCD driver", "Concatenated Disk driver");
7701706d20SPoul-Henning Kamp 
78a56bb8a5SSatoshi Asami #if defined(CCDDEBUG) && !defined(DEBUG)
79a56bb8a5SSatoshi Asami #define DEBUG
80a56bb8a5SSatoshi Asami #endif
81a56bb8a5SSatoshi Asami 
82a56bb8a5SSatoshi Asami #ifdef DEBUG
83a56bb8a5SSatoshi Asami #define CCDB_FOLLOW	0x01
84a56bb8a5SSatoshi Asami #define CCDB_INIT	0x02
85a56bb8a5SSatoshi Asami #define CCDB_IO		0x04
86a56bb8a5SSatoshi Asami #define CCDB_LABEL	0x08
87a56bb8a5SSatoshi Asami #define CCDB_VNODE	0x10
88e2738b4fSPoul-Henning Kamp static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL |
89e2738b4fSPoul-Henning Kamp     CCDB_VNODE;
90e2738b4fSPoul-Henning Kamp SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, "");
91a56bb8a5SSatoshi Asami #endif
92a56bb8a5SSatoshi Asami 
93ddbf51afSPoul-Henning Kamp static u_int
94ddbf51afSPoul-Henning Kamp ccdunit(dev_t dev)
95ddbf51afSPoul-Henning Kamp {
96ddbf51afSPoul-Henning Kamp         return (((minor(dev) >> 16) & 0x1e0) | ((minor(dev) >> 3) & 0x1f));
97ddbf51afSPoul-Henning Kamp }
98ddbf51afSPoul-Henning Kamp 
99ddbf51afSPoul-Henning Kamp #define ccdpart(x)	(minor(x) & 7)
100a56bb8a5SSatoshi Asami 
101e7322872SSatoshi Asami /*
102e7322872SSatoshi Asami    This is how mirroring works (only writes are special):
103e7322872SSatoshi Asami 
104e7322872SSatoshi Asami    When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s
105e7322872SSatoshi Asami    linked together by the cb_mirror field.  "cb_pflags &
106e7322872SSatoshi Asami    CCDPF_MIRROR_DONE" is set to 0 on both of them.
107e7322872SSatoshi Asami 
108e7322872SSatoshi Asami    When a component returns to ccdiodone(), it checks if "cb_pflags &
109e7322872SSatoshi Asami    CCDPF_MIRROR_DONE" is set or not.  If not, it sets the partner's
110e7322872SSatoshi Asami    flag and returns.  If it is, it means its partner has already
111e7322872SSatoshi Asami    returned, so it will go to the regular cleanup.
112e7322872SSatoshi Asami 
113e7322872SSatoshi Asami  */
114e7322872SSatoshi Asami 
115a56bb8a5SSatoshi Asami struct ccdbuf {
1169d7f7369SPoul-Henning Kamp 	struct bio	cb_buf;		/* new I/O buf */
1179d7f7369SPoul-Henning Kamp 	struct bio	*cb_obp;	/* ptr. to original I/O buf */
1181464240eSMatthew Dillon 	struct ccdbuf	*cb_freenext;	/* free list link */
119a56bb8a5SSatoshi Asami 	int		cb_unit;	/* target unit */
120a56bb8a5SSatoshi Asami 	int		cb_comp;	/* target component */
121e7322872SSatoshi Asami 	int		cb_pflags;	/* mirror/parity status flag */
122e7322872SSatoshi Asami 	struct ccdbuf	*cb_mirror;	/* mirror counterpart */
123a56bb8a5SSatoshi Asami };
124a56bb8a5SSatoshi Asami 
125e7322872SSatoshi Asami /* bits in cb_pflags */
126e7322872SSatoshi Asami #define CCDPF_MIRROR_DONE 1	/* if set, mirror counterpart is done */
127e7322872SSatoshi Asami 
128a56bb8a5SSatoshi Asami #define CCDLABELDEV(dev)	\
129d8594dfbSSatoshi Asami 	(makedev(major((dev)), dkmakeminor(ccdunit((dev)), 0, RAW_PART)))
130a56bb8a5SSatoshi Asami 
13101706d20SPoul-Henning Kamp /* convinient macros for often-used statements */
13201706d20SPoul-Henning Kamp #define IS_ALLOCATED(unit)	(ccdfind(unit) != NULL)
13301706d20SPoul-Henning Kamp #define IS_INITED(cs)		(((cs)->sc_flags & CCDF_INITED) != 0)
13401706d20SPoul-Henning Kamp 
135ddbf51afSPoul-Henning Kamp 
136ddbf51afSPoul-Henning Kamp static dev_t	ccdctldev;
137ddbf51afSPoul-Henning Kamp 
138ddbf51afSPoul-Henning Kamp 
139e2738b4fSPoul-Henning Kamp static d_open_t ccdopen;
140e2738b4fSPoul-Henning Kamp static d_close_t ccdclose;
141e2738b4fSPoul-Henning Kamp static d_strategy_t ccdstrategy;
142e2738b4fSPoul-Henning Kamp static d_ioctl_t ccdioctl;
143ddbf51afSPoul-Henning Kamp static d_ioctl_t ccdioctltoo;
144e2738b4fSPoul-Henning Kamp static d_psize_t ccdsize;
145d8594dfbSSatoshi Asami 
1461464240eSMatthew Dillon #define NCCDFREEHIWAT	16
1471464240eSMatthew Dillon 
148e2a13e8cSSatoshi Asami #define CDEV_MAJOR 74
149a56bb8a5SSatoshi Asami 
150f7ea2f55SJulian Elischer static struct cdevsw ccd_cdevsw = {
1514e2f199eSPoul-Henning Kamp 	/* open */	ccdopen,
1524e2f199eSPoul-Henning Kamp 	/* close */	ccdclose,
1534e2f199eSPoul-Henning Kamp 	/* read */	physread,
1544e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
1554e2f199eSPoul-Henning Kamp 	/* ioctl */	ccdioctl,
1564e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
1574e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
1584e2f199eSPoul-Henning Kamp 	/* strategy */	ccdstrategy,
1594e2f199eSPoul-Henning Kamp 	/* name */	"ccd",
1604e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
1616ede0accSPoul-Henning Kamp 	/* dump */	nodump,
1624e2f199eSPoul-Henning Kamp 	/* psize */	ccdsize,
1634e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
1644e2f199eSPoul-Henning Kamp };
16501706d20SPoul-Henning Kamp static LIST_HEAD(, ccd_s) ccd_softc_list = LIST_HEAD_INITIALIZER(&ccd_softc_list);
16601706d20SPoul-Henning Kamp 
16701706d20SPoul-Henning Kamp static struct ccd_s *ccdfind(int);
16801706d20SPoul-Henning Kamp static struct ccd_s *ccdnew(int);
16901706d20SPoul-Henning Kamp static int ccddestroy(struct ccd_s *, struct proc *);
170e2a13e8cSSatoshi Asami 
171b7b98418SPeter Wemm /* called during module initialization */
17201706d20SPoul-Henning Kamp static void ccdattach(void);
17301706d20SPoul-Henning Kamp static int ccd_modevent(module_t, int, void *);
174a56bb8a5SSatoshi Asami 
175a56bb8a5SSatoshi Asami /* called by biodone() at interrupt time */
17601706d20SPoul-Henning Kamp static void ccdiodone(struct bio *bp);
177a56bb8a5SSatoshi Asami 
17801706d20SPoul-Henning Kamp static void ccdstart(struct ccd_s *, struct bio *);
17901706d20SPoul-Henning Kamp static void ccdinterleave(struct ccd_s *, int);
18001706d20SPoul-Henning Kamp static void ccdintr(struct ccd_s *, struct bio *);
181b40ce416SJulian Elischer static int ccdinit(struct ccd_s *, char **, struct thread *);
182b40ce416SJulian Elischer static int ccdlookup(char *, struct thread *p, struct vnode **);
18301706d20SPoul-Henning Kamp static void ccdbuffer(struct ccdbuf **ret, struct ccd_s *,
18401706d20SPoul-Henning Kamp 		      struct bio *, daddr_t, caddr_t, long);
18501706d20SPoul-Henning Kamp static void ccdgetdisklabel(dev_t);
18601706d20SPoul-Henning Kamp static void ccdmakedisklabel(struct ccd_s *);
18701706d20SPoul-Henning Kamp static int ccdlock(struct ccd_s *);
18801706d20SPoul-Henning Kamp static void ccdunlock(struct ccd_s *);
189a56bb8a5SSatoshi Asami 
190a56bb8a5SSatoshi Asami #ifdef DEBUG
19101706d20SPoul-Henning Kamp static void printiinfo(struct ccdiinfo *);
192a56bb8a5SSatoshi Asami #endif
193a56bb8a5SSatoshi Asami 
194a56bb8a5SSatoshi Asami /* Non-private for the benefit of libkvm. */
1951464240eSMatthew Dillon struct ccdbuf *ccdfreebufs;
1961464240eSMatthew Dillon static int numccdfreebufs;
197a56bb8a5SSatoshi Asami 
198a56bb8a5SSatoshi Asami /*
1991464240eSMatthew Dillon  * getccdbuf() -	Allocate and zero a ccd buffer.
2001464240eSMatthew Dillon  *
2011464240eSMatthew Dillon  *	This routine is called at splbio().
2021464240eSMatthew Dillon  */
2031464240eSMatthew Dillon 
2041464240eSMatthew Dillon static __inline
2051464240eSMatthew Dillon struct ccdbuf *
2061464240eSMatthew Dillon getccdbuf(struct ccdbuf *cpy)
2071464240eSMatthew Dillon {
2081464240eSMatthew Dillon 	struct ccdbuf *cbp;
2091464240eSMatthew Dillon 
2101464240eSMatthew Dillon 	/*
2111464240eSMatthew Dillon 	 * Allocate from freelist or malloc as necessary
2121464240eSMatthew Dillon 	 */
2131464240eSMatthew Dillon 	if ((cbp = ccdfreebufs) != NULL) {
2141464240eSMatthew Dillon 		ccdfreebufs = cbp->cb_freenext;
2151464240eSMatthew Dillon 		--numccdfreebufs;
2161464240eSMatthew Dillon 	} else {
2171464240eSMatthew Dillon 		cbp = malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK);
2181464240eSMatthew Dillon 	}
2191464240eSMatthew Dillon 
2201464240eSMatthew Dillon 	/*
2211464240eSMatthew Dillon 	 * Used by mirroring code
2221464240eSMatthew Dillon 	 */
2231464240eSMatthew Dillon 	if (cpy)
2241464240eSMatthew Dillon 		bcopy(cpy, cbp, sizeof(struct ccdbuf));
2251464240eSMatthew Dillon 	else
2261464240eSMatthew Dillon 		bzero(cbp, sizeof(struct ccdbuf));
2271464240eSMatthew Dillon 
2281464240eSMatthew Dillon 	/*
2299d7f7369SPoul-Henning Kamp 	 * independant struct bio initialization
2301464240eSMatthew Dillon 	 */
2311464240eSMatthew Dillon 
2321464240eSMatthew Dillon 	return(cbp);
2331464240eSMatthew Dillon }
2341464240eSMatthew Dillon 
2351464240eSMatthew Dillon /*
2361441456fSGreg Lehey  * putccdbuf() -	Free a ccd buffer.
2371464240eSMatthew Dillon  *
2381464240eSMatthew Dillon  *	This routine is called at splbio().
2391464240eSMatthew Dillon  */
2401464240eSMatthew Dillon 
2411464240eSMatthew Dillon static __inline
2421464240eSMatthew Dillon void
2431464240eSMatthew Dillon putccdbuf(struct ccdbuf *cbp)
2441464240eSMatthew Dillon {
24574427f90SMatthew Dillon 
2461464240eSMatthew Dillon 	if (numccdfreebufs < NCCDFREEHIWAT) {
2471464240eSMatthew Dillon 		cbp->cb_freenext = ccdfreebufs;
2481464240eSMatthew Dillon 		ccdfreebufs = cbp;
2491464240eSMatthew Dillon 		++numccdfreebufs;
2501464240eSMatthew Dillon 	} else {
2511464240eSMatthew Dillon 		free((caddr_t)cbp, M_DEVBUF);
2521464240eSMatthew Dillon 	}
2531464240eSMatthew Dillon }
2541464240eSMatthew Dillon 
2551464240eSMatthew Dillon 
2561464240eSMatthew Dillon /*
2570d88ef07SSatoshi Asami  * Number of blocks to untouched in front of a component partition.
2580d88ef07SSatoshi Asami  * This is to avoid violating its disklabel area when it starts at the
2590d88ef07SSatoshi Asami  * beginning of the slice.
2600d88ef07SSatoshi Asami  */
2611af0e025SSatoshi Asami #if !defined(CCD_OFFSET)
2620d88ef07SSatoshi Asami #define CCD_OFFSET 16
2631af0e025SSatoshi Asami #endif
2640d88ef07SSatoshi Asami 
26501706d20SPoul-Henning Kamp static struct ccd_s *
26601706d20SPoul-Henning Kamp ccdfind(int unit)
26701706d20SPoul-Henning Kamp {
26801706d20SPoul-Henning Kamp 	struct ccd_s *sc = NULL;
26901706d20SPoul-Henning Kamp 
27001706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
27101706d20SPoul-Henning Kamp 	LIST_FOREACH(sc, &ccd_softc_list, list) {
27201706d20SPoul-Henning Kamp 		if (sc->sc_unit == unit)
27301706d20SPoul-Henning Kamp 			break;
27401706d20SPoul-Henning Kamp 	}
27501706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
27601706d20SPoul-Henning Kamp 	return ((sc == NULL) || (sc->sc_unit != unit) ? NULL : sc);
27701706d20SPoul-Henning Kamp }
27801706d20SPoul-Henning Kamp 
27901706d20SPoul-Henning Kamp static struct ccd_s *
28001706d20SPoul-Henning Kamp ccdnew(int unit)
28101706d20SPoul-Henning Kamp {
28201706d20SPoul-Henning Kamp 	struct ccd_s *sc;
28301706d20SPoul-Henning Kamp 
28401706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
28501706d20SPoul-Henning Kamp 	if (IS_ALLOCATED(unit) || unit > DKMAXUNIT)
28601706d20SPoul-Henning Kamp 		return (NULL);
28701706d20SPoul-Henning Kamp 
28801706d20SPoul-Henning Kamp 	MALLOC(sc, struct ccd_s *, sizeof(*sc), M_CCD, M_WAITOK | M_ZERO);
28901706d20SPoul-Henning Kamp 	sc->sc_unit = unit;
29001706d20SPoul-Henning Kamp 	LIST_INSERT_HEAD(&ccd_softc_list, sc, list);
29101706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
29201706d20SPoul-Henning Kamp 	return (sc);
29301706d20SPoul-Henning Kamp }
29401706d20SPoul-Henning Kamp 
29501706d20SPoul-Henning Kamp static int
29601706d20SPoul-Henning Kamp ccddestroy(struct ccd_s *sc, struct proc *p)
29701706d20SPoul-Henning Kamp {
29801706d20SPoul-Henning Kamp 
29901706d20SPoul-Henning Kamp 	/* XXX: LOCK(unique unit numbers) */
30001706d20SPoul-Henning Kamp 	LIST_REMOVE(sc, list);
30101706d20SPoul-Henning Kamp 	/* XXX: UNLOCK(unique unit numbers) */
30201706d20SPoul-Henning Kamp 	FREE(sc, M_CCD);
30301706d20SPoul-Henning Kamp 	return (0);
30401706d20SPoul-Henning Kamp }
30501706d20SPoul-Henning Kamp 
306a6b1634eSPoul-Henning Kamp static void
307a6b1634eSPoul-Henning Kamp ccd_clone(void *arg, char *name, int namelen, dev_t *dev)
308a6b1634eSPoul-Henning Kamp {
309a6b1634eSPoul-Henning Kamp 	int i, u;
310a6b1634eSPoul-Henning Kamp 	char *s;
311a6b1634eSPoul-Henning Kamp 
312a6b1634eSPoul-Henning Kamp 	if (*dev != NODEV)
313a6b1634eSPoul-Henning Kamp 		return;
314db901281SPoul-Henning Kamp 	i = dev_stdclone(name, &s, "ccd", &u);
315a6b1634eSPoul-Henning Kamp 	if (i != 2)
316a6b1634eSPoul-Henning Kamp 		return;
317896dba5aSPoul-Henning Kamp 	if (*s < 'a' || *s > 'h')
318a6b1634eSPoul-Henning Kamp 		return;
319a6b1634eSPoul-Henning Kamp 	if (s[1] != '\0')
320a6b1634eSPoul-Henning Kamp 		return;
321a6b1634eSPoul-Henning Kamp 	*dev = make_dev(&ccd_cdevsw, u * 8 + *s - 'a',
322a6b1634eSPoul-Henning Kamp 		UID_ROOT, GID_OPERATOR, 0640, name);
323a6b1634eSPoul-Henning Kamp }
324a6b1634eSPoul-Henning Kamp 
3250d88ef07SSatoshi Asami /*
326a56bb8a5SSatoshi Asami  * Called by main() during pseudo-device attachment.  All we need
32701706d20SPoul-Henning Kamp  * to do is to add devsw entries.
328a56bb8a5SSatoshi Asami  */
329e2738b4fSPoul-Henning Kamp static void
330b7b98418SPeter Wemm ccdattach()
331a56bb8a5SSatoshi Asami {
332a56bb8a5SSatoshi Asami 
333ddbf51afSPoul-Henning Kamp 	ccdctldev = make_dev(&ccd_cdevsw, 0xffff00ff,
334ddbf51afSPoul-Henning Kamp 		UID_ROOT, GID_OPERATOR, 0640, "ccd.ctl");
335ddbf51afSPoul-Henning Kamp 	ccdctldev->si_drv1 = ccdctldev;
336db901281SPoul-Henning Kamp 	EVENTHANDLER_REGISTER(dev_clone, ccd_clone, 0, 1000);
337b7b98418SPeter Wemm }
338d8594dfbSSatoshi Asami 
339b7b98418SPeter Wemm static int
34001706d20SPoul-Henning Kamp ccd_modevent(module_t mod, int type, void *data)
341b7b98418SPeter Wemm {
342b7b98418SPeter Wemm 	int error = 0;
343b7b98418SPeter Wemm 
344b7b98418SPeter Wemm 	switch (type) {
345b7b98418SPeter Wemm 	case MOD_LOAD:
346b7b98418SPeter Wemm 		ccdattach();
347b7b98418SPeter Wemm 		break;
348b7b98418SPeter Wemm 
349b7b98418SPeter Wemm 	case MOD_UNLOAD:
350b7b98418SPeter Wemm 		printf("ccd0: Unload not supported!\n");
351b7b98418SPeter Wemm 		error = EOPNOTSUPP;
352b7b98418SPeter Wemm 		break;
353b7b98418SPeter Wemm 
35455a13f7dSIan Dowse 	case MOD_SHUTDOWN:
355b7b98418SPeter Wemm 		break;
35655a13f7dSIan Dowse 
35755a13f7dSIan Dowse 	default:
35855a13f7dSIan Dowse 		error = EOPNOTSUPP;
359e2a13e8cSSatoshi Asami 	}
360b7b98418SPeter Wemm 	return (error);
361e2a13e8cSSatoshi Asami }
362b7b98418SPeter Wemm 
363d53dedeeSPoul-Henning Kamp DEV_MODULE(ccd, ccd_modevent, NULL);
364a56bb8a5SSatoshi Asami 
365a56bb8a5SSatoshi Asami static int
366b40ce416SJulian Elischer ccdinit(struct ccd_s *cs, char **cpaths, struct thread *td)
367a56bb8a5SSatoshi Asami {
3681464240eSMatthew Dillon 	struct ccdcinfo *ci = NULL;	/* XXX */
3691464240eSMatthew Dillon 	size_t size;
3701464240eSMatthew Dillon 	int ix;
371a56bb8a5SSatoshi Asami 	struct vnode *vp;
372a56bb8a5SSatoshi Asami 	size_t minsize;
373a56bb8a5SSatoshi Asami 	int maxsecsize;
374a56bb8a5SSatoshi Asami 	struct ccdgeom *ccg = &cs->sc_geom;
375e3f4d3b5SPoul-Henning Kamp 	char *tmppath = NULL;
3761464240eSMatthew Dillon 	int error = 0;
377ffee6e99SPoul-Henning Kamp 	off_t mediasize;
378ffee6e99SPoul-Henning Kamp 	u_int sectorsize;
379a56bb8a5SSatoshi Asami 
380a56bb8a5SSatoshi Asami #ifdef DEBUG
381a56bb8a5SSatoshi Asami 	if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
38201706d20SPoul-Henning Kamp 		printf("ccdinit: unit %d\n", cs->sc_unit);
383a56bb8a5SSatoshi Asami #endif
384a56bb8a5SSatoshi Asami 
385a56bb8a5SSatoshi Asami 	cs->sc_size = 0;
386a56bb8a5SSatoshi Asami 
387a56bb8a5SSatoshi Asami 	/* Allocate space for the component info. */
388a56bb8a5SSatoshi Asami 	cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
389a56bb8a5SSatoshi Asami 	    M_DEVBUF, M_WAITOK);
390a56bb8a5SSatoshi Asami 
391a56bb8a5SSatoshi Asami 	/*
392a56bb8a5SSatoshi Asami 	 * Verify that each component piece exists and record
393a56bb8a5SSatoshi Asami 	 * relevant information about it.
394a56bb8a5SSatoshi Asami 	 */
395a56bb8a5SSatoshi Asami 	maxsecsize = 0;
396a56bb8a5SSatoshi Asami 	minsize = 0;
397e3f4d3b5SPoul-Henning Kamp 	tmppath = malloc(MAXPATHLEN, M_DEVBUF, M_WAITOK);
398a56bb8a5SSatoshi Asami 	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
39901706d20SPoul-Henning Kamp 		vp = cs->sc_vpp[ix];
400a56bb8a5SSatoshi Asami 		ci = &cs->sc_cinfo[ix];
401a56bb8a5SSatoshi Asami 		ci->ci_vp = vp;
402a56bb8a5SSatoshi Asami 
403a56bb8a5SSatoshi Asami 		/*
404a56bb8a5SSatoshi Asami 		 * Copy in the pathname of the component.
405a56bb8a5SSatoshi Asami 		 */
406b4e36adfSMatthew Dillon 		if ((error = copyinstr(cpaths[ix], tmppath,
407b4e36adfSMatthew Dillon 		    MAXPATHLEN, &ci->ci_pathlen)) != 0) {
408a56bb8a5SSatoshi Asami #ifdef DEBUG
409a56bb8a5SSatoshi Asami 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
410a56bb8a5SSatoshi Asami 				printf("ccd%d: can't copy path, error = %d\n",
41101706d20SPoul-Henning Kamp 				    cs->sc_unit, error);
412a56bb8a5SSatoshi Asami #endif
4131464240eSMatthew Dillon 			goto fail;
414a56bb8a5SSatoshi Asami 		}
415a56bb8a5SSatoshi Asami 		ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK);
416a56bb8a5SSatoshi Asami 		bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
417a56bb8a5SSatoshi Asami 
418684adedeSPoul-Henning Kamp 		ci->ci_dev = vn_todev(vp);
419a56bb8a5SSatoshi Asami 
420a56bb8a5SSatoshi Asami 		/*
421a56bb8a5SSatoshi Asami 		 * Get partition information for the component.
422a56bb8a5SSatoshi Asami 		 */
423ffee6e99SPoul-Henning Kamp 		error = VOP_IOCTL(vp, DIOCGMEDIASIZE, (caddr_t)&mediasize,
424ffee6e99SPoul-Henning Kamp 		    FREAD, td->td_ucred, td);
425ffee6e99SPoul-Henning Kamp 		if (error != 0) {
426a56bb8a5SSatoshi Asami #ifdef DEBUG
427a56bb8a5SSatoshi Asami 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
428a56bb8a5SSatoshi Asami 				 printf("ccd%d: %s: ioctl failed, error = %d\n",
42901706d20SPoul-Henning Kamp 				     cs->sc_unit, ci->ci_path, error);
430a56bb8a5SSatoshi Asami #endif
4311464240eSMatthew Dillon 			goto fail;
432a56bb8a5SSatoshi Asami 		}
433ffee6e99SPoul-Henning Kamp 		/*
434ffee6e99SPoul-Henning Kamp 		 * Get partition information for the component.
435ffee6e99SPoul-Henning Kamp 		 */
436ffee6e99SPoul-Henning Kamp 		error = VOP_IOCTL(vp, DIOCGSECTORSIZE, (caddr_t)&sectorsize,
437ffee6e99SPoul-Henning Kamp 		    FREAD, td->td_ucred, td);
438ffee6e99SPoul-Henning Kamp 		if (error != 0) {
439a56bb8a5SSatoshi Asami #ifdef DEBUG
440a56bb8a5SSatoshi Asami 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
441ffee6e99SPoul-Henning Kamp 				 printf("ccd%d: %s: ioctl failed, error = %d\n",
442ffee6e99SPoul-Henning Kamp 				     cs->sc_unit, ci->ci_path, error);
443a56bb8a5SSatoshi Asami #endif
4441464240eSMatthew Dillon 			goto fail;
445a56bb8a5SSatoshi Asami 		}
446ffee6e99SPoul-Henning Kamp 		if (sectorsize > maxsecsize)
447ffee6e99SPoul-Henning Kamp 			maxsecsize = sectorsize;
448ffee6e99SPoul-Henning Kamp 		size = mediasize / DEV_BSIZE - CCD_OFFSET;
449a56bb8a5SSatoshi Asami 
450a56bb8a5SSatoshi Asami 		/*
451a56bb8a5SSatoshi Asami 		 * Calculate the size, truncating to an interleave
452a56bb8a5SSatoshi Asami 		 * boundary if necessary.
453a56bb8a5SSatoshi Asami 		 */
454a56bb8a5SSatoshi Asami 
455a56bb8a5SSatoshi Asami 		if (cs->sc_ileave > 1)
456a56bb8a5SSatoshi Asami 			size -= size % cs->sc_ileave;
457a56bb8a5SSatoshi Asami 
458a56bb8a5SSatoshi Asami 		if (size == 0) {
459a56bb8a5SSatoshi Asami #ifdef DEBUG
460a56bb8a5SSatoshi Asami 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
461a56bb8a5SSatoshi Asami 				printf("ccd%d: %s: size == 0\n",
46201706d20SPoul-Henning Kamp 				    cs->sc_unit, ci->ci_path);
463a56bb8a5SSatoshi Asami #endif
4641464240eSMatthew Dillon 			error = ENODEV;
4651464240eSMatthew Dillon 			goto fail;
466a56bb8a5SSatoshi Asami 		}
467a56bb8a5SSatoshi Asami 
468a56bb8a5SSatoshi Asami 		if (minsize == 0 || size < minsize)
469a56bb8a5SSatoshi Asami 			minsize = size;
470a56bb8a5SSatoshi Asami 		ci->ci_size = size;
471a56bb8a5SSatoshi Asami 		cs->sc_size += size;
472a56bb8a5SSatoshi Asami 	}
473a56bb8a5SSatoshi Asami 
474e3f4d3b5SPoul-Henning Kamp 	free(tmppath, M_DEVBUF);
475e3f4d3b5SPoul-Henning Kamp 	tmppath = NULL;
476e3f4d3b5SPoul-Henning Kamp 
477a56bb8a5SSatoshi Asami 	/*
478a56bb8a5SSatoshi Asami 	 * Don't allow the interleave to be smaller than
479a56bb8a5SSatoshi Asami 	 * the biggest component sector.
480a56bb8a5SSatoshi Asami 	 */
481a56bb8a5SSatoshi Asami 	if ((cs->sc_ileave > 0) &&
482a56bb8a5SSatoshi Asami 	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
483a56bb8a5SSatoshi Asami #ifdef DEBUG
484a56bb8a5SSatoshi Asami 		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
485a56bb8a5SSatoshi Asami 			printf("ccd%d: interleave must be at least %d\n",
48601706d20SPoul-Henning Kamp 			    cs->sc_unit, (maxsecsize / DEV_BSIZE));
487a56bb8a5SSatoshi Asami #endif
4881464240eSMatthew Dillon 		error = EINVAL;
4891464240eSMatthew Dillon 		goto fail;
490a56bb8a5SSatoshi Asami 	}
491a56bb8a5SSatoshi Asami 
492a56bb8a5SSatoshi Asami 	/*
493a56bb8a5SSatoshi Asami 	 * If uniform interleave is desired set all sizes to that of
4941464240eSMatthew Dillon 	 * the smallest component.  This will guarentee that a single
4951464240eSMatthew Dillon 	 * interleave table is generated.
4961464240eSMatthew Dillon 	 *
4971464240eSMatthew Dillon 	 * Lost space must be taken into account when calculating the
4981464240eSMatthew Dillon 	 * overall size.  Half the space is lost when CCDF_MIRROR is
499ddbf51afSPoul-Henning Kamp 	 * specified.
500a56bb8a5SSatoshi Asami 	 */
50101706d20SPoul-Henning Kamp 	if (cs->sc_flags & CCDF_UNIFORM) {
502a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo;
5031464240eSMatthew Dillon 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
504a56bb8a5SSatoshi Asami 			ci->ci_size = minsize;
5051464240eSMatthew Dillon 		}
50601706d20SPoul-Henning Kamp 		if (cs->sc_flags & CCDF_MIRROR) {
50734f35216SSatoshi Asami 			/*
50834f35216SSatoshi Asami 			 * Check to see if an even number of components
5091464240eSMatthew Dillon 			 * have been specified.  The interleave must also
5101464240eSMatthew Dillon 			 * be non-zero in order for us to be able to
5111464240eSMatthew Dillon 			 * guarentee the topology.
51234f35216SSatoshi Asami 			 */
51309b59204SSatoshi Asami 			if (cs->sc_nccdisks % 2) {
51401706d20SPoul-Henning Kamp 				printf("ccd%d: mirroring requires an even number of disks\n", cs->sc_unit );
5151464240eSMatthew Dillon 				error = EINVAL;
5161464240eSMatthew Dillon 				goto fail;
51734f35216SSatoshi Asami 			}
5181464240eSMatthew Dillon 			if (cs->sc_ileave == 0) {
51901706d20SPoul-Henning Kamp 				printf("ccd%d: an interleave must be specified when mirroring\n", cs->sc_unit);
5201464240eSMatthew Dillon 				error = EINVAL;
5211464240eSMatthew Dillon 				goto fail;
52209b59204SSatoshi Asami 			}
52309b59204SSatoshi Asami 			cs->sc_size = (cs->sc_nccdisks/2) * minsize;
5241464240eSMatthew Dillon 		} else {
5251464240eSMatthew Dillon 			if (cs->sc_ileave == 0) {
52601706d20SPoul-Henning Kamp 				printf("ccd%d: an interleave must be specified when using parity\n", cs->sc_unit);
5271464240eSMatthew Dillon 				error = EINVAL;
5281464240eSMatthew Dillon 				goto fail;
5291464240eSMatthew Dillon 			}
530a56bb8a5SSatoshi Asami 			cs->sc_size = cs->sc_nccdisks * minsize;
531a56bb8a5SSatoshi Asami 		}
5321464240eSMatthew Dillon 	}
533a56bb8a5SSatoshi Asami 
534a56bb8a5SSatoshi Asami 	/*
535a56bb8a5SSatoshi Asami 	 * Construct the interleave table.
536a56bb8a5SSatoshi Asami 	 */
53701706d20SPoul-Henning Kamp 	ccdinterleave(cs, cs->sc_unit);
538a56bb8a5SSatoshi Asami 
539a56bb8a5SSatoshi Asami 	/*
540a56bb8a5SSatoshi Asami 	 * Create pseudo-geometry based on 1MB cylinders.  It's
541a56bb8a5SSatoshi Asami 	 * pretty close.
542a56bb8a5SSatoshi Asami 	 */
543e59f3105SSøren Schmidt 	ccg->ccg_secsize = maxsecsize;
544a56bb8a5SSatoshi Asami 	ccg->ccg_ntracks = 1;
545e322ec4cSMatthew Dillon 	ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize;
546a56bb8a5SSatoshi Asami 	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
547a56bb8a5SSatoshi Asami 
548b2dfb1f9SJustin T. Gibbs 	/*
549d64ada50SJens Schweikhardt 	 * Add a devstat entry for this device.
550b2dfb1f9SJustin T. Gibbs 	 */
55101706d20SPoul-Henning Kamp 	devstat_add_entry(&cs->device_stats, "ccd", cs->sc_unit,
552b2dfb1f9SJustin T. Gibbs 			  ccg->ccg_secsize, DEVSTAT_ALL_SUPPORTED,
55386b2c846SKenneth D. Merry 			  DEVSTAT_TYPE_STORARRAY |DEVSTAT_TYPE_IF_OTHER,
55486b2c846SKenneth D. Merry 			  DEVSTAT_PRIORITY_ARRAY);
555a56bb8a5SSatoshi Asami 
556a56bb8a5SSatoshi Asami 	cs->sc_flags |= CCDF_INITED;
55701706d20SPoul-Henning Kamp 	cs->sc_cflags = cs->sc_flags;	/* So we can find out later... */
558a56bb8a5SSatoshi Asami 	return (0);
5591464240eSMatthew Dillon fail:
5601464240eSMatthew Dillon 	while (ci > cs->sc_cinfo) {
5611464240eSMatthew Dillon 		ci--;
5621464240eSMatthew Dillon 		free(ci->ci_path, M_DEVBUF);
5631464240eSMatthew Dillon 	}
564e3f4d3b5SPoul-Henning Kamp 	if (tmppath != NULL)
565e3f4d3b5SPoul-Henning Kamp 		free(tmppath, M_DEVBUF);
5661464240eSMatthew Dillon 	free(cs->sc_cinfo, M_DEVBUF);
5671464240eSMatthew Dillon 	return (error);
568a56bb8a5SSatoshi Asami }
569a56bb8a5SSatoshi Asami 
570a56bb8a5SSatoshi Asami static void
57101706d20SPoul-Henning Kamp ccdinterleave(struct ccd_s *cs, int unit)
572a56bb8a5SSatoshi Asami {
5731464240eSMatthew Dillon 	struct ccdcinfo *ci, *smallci;
5741464240eSMatthew Dillon 	struct ccdiinfo *ii;
5751464240eSMatthew Dillon 	daddr_t bn, lbn;
5761464240eSMatthew Dillon 	int ix;
577a56bb8a5SSatoshi Asami 	u_long size;
578a56bb8a5SSatoshi Asami 
579a56bb8a5SSatoshi Asami #ifdef DEBUG
580a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_INIT)
5816a5a4d0aSAndrew Gallatin 		printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave);
582a56bb8a5SSatoshi Asami #endif
5831464240eSMatthew Dillon 
584a56bb8a5SSatoshi Asami 	/*
5851464240eSMatthew Dillon 	 * Allocate an interleave table.  The worst case occurs when each
5861464240eSMatthew Dillon 	 * of N disks is of a different size, resulting in N interleave
5871464240eSMatthew Dillon 	 * tables.
5881464240eSMatthew Dillon 	 *
589a56bb8a5SSatoshi Asami 	 * Chances are this is too big, but we don't care.
590a56bb8a5SSatoshi Asami 	 */
591a56bb8a5SSatoshi Asami 	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
5927cc0979fSDavid Malone 	cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF,
5937cc0979fSDavid Malone 	    M_WAITOK | M_ZERO);
594a56bb8a5SSatoshi Asami 
595a56bb8a5SSatoshi Asami 	/*
596a56bb8a5SSatoshi Asami 	 * Trivial case: no interleave (actually interleave of disk size).
597a56bb8a5SSatoshi Asami 	 * Each table entry represents a single component in its entirety.
5981464240eSMatthew Dillon 	 *
599ddbf51afSPoul-Henning Kamp 	 * An interleave of 0 may not be used with a mirror setup.
600a56bb8a5SSatoshi Asami 	 */
601a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0) {
602a56bb8a5SSatoshi Asami 		bn = 0;
603a56bb8a5SSatoshi Asami 		ii = cs->sc_itable;
604a56bb8a5SSatoshi Asami 
605a56bb8a5SSatoshi Asami 		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
606a56bb8a5SSatoshi Asami 			/* Allocate space for ii_index. */
607a56bb8a5SSatoshi Asami 			ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK);
608a56bb8a5SSatoshi Asami 			ii->ii_ndisk = 1;
609a56bb8a5SSatoshi Asami 			ii->ii_startblk = bn;
610a56bb8a5SSatoshi Asami 			ii->ii_startoff = 0;
611a56bb8a5SSatoshi Asami 			ii->ii_index[0] = ix;
612a56bb8a5SSatoshi Asami 			bn += cs->sc_cinfo[ix].ci_size;
613a56bb8a5SSatoshi Asami 			ii++;
614a56bb8a5SSatoshi Asami 		}
615a56bb8a5SSatoshi Asami 		ii->ii_ndisk = 0;
616a56bb8a5SSatoshi Asami #ifdef DEBUG
617a56bb8a5SSatoshi Asami 		if (ccddebug & CCDB_INIT)
618a56bb8a5SSatoshi Asami 			printiinfo(cs->sc_itable);
619a56bb8a5SSatoshi Asami #endif
620a56bb8a5SSatoshi Asami 		return;
621a56bb8a5SSatoshi Asami 	}
622a56bb8a5SSatoshi Asami 
623a56bb8a5SSatoshi Asami 	/*
624a56bb8a5SSatoshi Asami 	 * The following isn't fast or pretty; it doesn't have to be.
625a56bb8a5SSatoshi Asami 	 */
626a56bb8a5SSatoshi Asami 	size = 0;
627a56bb8a5SSatoshi Asami 	bn = lbn = 0;
628a56bb8a5SSatoshi Asami 	for (ii = cs->sc_itable; ; ii++) {
6291464240eSMatthew Dillon 		/*
6301464240eSMatthew Dillon 		 * Allocate space for ii_index.  We might allocate more then
6311464240eSMatthew Dillon 		 * we use.
6321464240eSMatthew Dillon 		 */
633a56bb8a5SSatoshi Asami 		ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks),
634a56bb8a5SSatoshi Asami 		    M_DEVBUF, M_WAITOK);
635a56bb8a5SSatoshi Asami 
636a56bb8a5SSatoshi Asami 		/*
637a56bb8a5SSatoshi Asami 		 * Locate the smallest of the remaining components
638a56bb8a5SSatoshi Asami 		 */
639a56bb8a5SSatoshi Asami 		smallci = NULL;
6401464240eSMatthew Dillon 		for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks];
6411464240eSMatthew Dillon 		    ci++) {
642a56bb8a5SSatoshi Asami 			if (ci->ci_size > size &&
643a56bb8a5SSatoshi Asami 			    (smallci == NULL ||
6441464240eSMatthew Dillon 			     ci->ci_size < smallci->ci_size)) {
645a56bb8a5SSatoshi Asami 				smallci = ci;
6461464240eSMatthew Dillon 			}
6471464240eSMatthew Dillon 		}
648a56bb8a5SSatoshi Asami 
649a56bb8a5SSatoshi Asami 		/*
650a56bb8a5SSatoshi Asami 		 * Nobody left, all done
651a56bb8a5SSatoshi Asami 		 */
652a56bb8a5SSatoshi Asami 		if (smallci == NULL) {
653a56bb8a5SSatoshi Asami 			ii->ii_ndisk = 0;
654a56bb8a5SSatoshi Asami 			break;
655a56bb8a5SSatoshi Asami 		}
656a56bb8a5SSatoshi Asami 
657a56bb8a5SSatoshi Asami 		/*
6581464240eSMatthew Dillon 		 * Record starting logical block using an sc_ileave blocksize.
659a56bb8a5SSatoshi Asami 		 */
660a56bb8a5SSatoshi Asami 		ii->ii_startblk = bn / cs->sc_ileave;
6611464240eSMatthew Dillon 
6621464240eSMatthew Dillon 		/*
6631464240eSMatthew Dillon 		 * Record starting comopnent block using an sc_ileave
6641464240eSMatthew Dillon 		 * blocksize.  This value is relative to the beginning of
6651464240eSMatthew Dillon 		 * a component disk.
6661464240eSMatthew Dillon 		 */
667a56bb8a5SSatoshi Asami 		ii->ii_startoff = lbn;
668a56bb8a5SSatoshi Asami 
669a56bb8a5SSatoshi Asami 		/*
670a56bb8a5SSatoshi Asami 		 * Determine how many disks take part in this interleave
671a56bb8a5SSatoshi Asami 		 * and record their indices.
672a56bb8a5SSatoshi Asami 		 */
673a56bb8a5SSatoshi Asami 		ix = 0;
674a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo;
6751464240eSMatthew Dillon 		    ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
6761464240eSMatthew Dillon 			if (ci->ci_size >= smallci->ci_size) {
677a56bb8a5SSatoshi Asami 				ii->ii_index[ix++] = ci - cs->sc_cinfo;
6781464240eSMatthew Dillon 			}
6791464240eSMatthew Dillon 		}
680a56bb8a5SSatoshi Asami 		ii->ii_ndisk = ix;
681a56bb8a5SSatoshi Asami 		bn += ix * (smallci->ci_size - size);
682a56bb8a5SSatoshi Asami 		lbn = smallci->ci_size / cs->sc_ileave;
683a56bb8a5SSatoshi Asami 		size = smallci->ci_size;
684a56bb8a5SSatoshi Asami 	}
685a56bb8a5SSatoshi Asami #ifdef DEBUG
686a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_INIT)
687a56bb8a5SSatoshi Asami 		printiinfo(cs->sc_itable);
688a56bb8a5SSatoshi Asami #endif
689a56bb8a5SSatoshi Asami }
690a56bb8a5SSatoshi Asami 
691a56bb8a5SSatoshi Asami /* ARGSUSED */
692e2738b4fSPoul-Henning Kamp static int
693b40ce416SJulian Elischer ccdopen(dev_t dev, int flags, int fmt, struct thread *td)
694a56bb8a5SSatoshi Asami {
695a56bb8a5SSatoshi Asami 	int unit = ccdunit(dev);
69601706d20SPoul-Henning Kamp 	struct ccd_s *cs;
697a56bb8a5SSatoshi Asami 	struct disklabel *lp;
698a56bb8a5SSatoshi Asami 	int error = 0, part, pmask;
699a56bb8a5SSatoshi Asami 
700ddbf51afSPoul-Henning Kamp 	if (dev->si_drv1 == dev)
701ddbf51afSPoul-Henning Kamp 		return (0);
702a56bb8a5SSatoshi Asami #ifdef DEBUG
703a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
7046a5a4d0aSAndrew Gallatin 		printf("ccdopen(%p, %x)\n", dev, flags);
705a56bb8a5SSatoshi Asami #endif
70601706d20SPoul-Henning Kamp 
70701706d20SPoul-Henning Kamp 	cs = IS_ALLOCATED(unit) ? ccdfind(unit) : ccdnew(unit);
708a56bb8a5SSatoshi Asami 
709b4e36adfSMatthew Dillon 	if ((error = ccdlock(cs)) != 0)
710a56bb8a5SSatoshi Asami 		return (error);
711a56bb8a5SSatoshi Asami 
7126cc5a722SPoul-Henning Kamp 	lp = &cs->sc_label;
713a56bb8a5SSatoshi Asami 
714d8594dfbSSatoshi Asami 	part = ccdpart(dev);
715a56bb8a5SSatoshi Asami 	pmask = (1 << part);
716a56bb8a5SSatoshi Asami 
717a56bb8a5SSatoshi Asami 	/*
718a56bb8a5SSatoshi Asami 	 * If we're initialized, check to see if there are any other
719a56bb8a5SSatoshi Asami 	 * open partitions.  If not, then it's safe to update
720a56bb8a5SSatoshi Asami 	 * the in-core disklabel.
721a56bb8a5SSatoshi Asami 	 */
72201706d20SPoul-Henning Kamp 	if (IS_INITED(cs) && (cs->sc_openmask == 0))
723a56bb8a5SSatoshi Asami 		ccdgetdisklabel(dev);
724a56bb8a5SSatoshi Asami 
725a56bb8a5SSatoshi Asami 	/* Check that the partition exists. */
7267d15435cSJordan K. Hubbard 	if (part != RAW_PART && ((part >= lp->d_npartitions) ||
727a56bb8a5SSatoshi Asami 	    (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
728a56bb8a5SSatoshi Asami 		error = ENXIO;
729a56bb8a5SSatoshi Asami 		goto done;
730a56bb8a5SSatoshi Asami 	}
731a56bb8a5SSatoshi Asami 
732af8862e4SPoul-Henning Kamp 	cs->sc_openmask |= pmask;
733a56bb8a5SSatoshi Asami  done:
734a56bb8a5SSatoshi Asami 	ccdunlock(cs);
735a56bb8a5SSatoshi Asami 	return (0);
736a56bb8a5SSatoshi Asami }
737a56bb8a5SSatoshi Asami 
738a56bb8a5SSatoshi Asami /* ARGSUSED */
739e2738b4fSPoul-Henning Kamp static int
740b40ce416SJulian Elischer ccdclose(dev_t dev, int flags, int fmt, struct thread *td)
741a56bb8a5SSatoshi Asami {
742a56bb8a5SSatoshi Asami 	int unit = ccdunit(dev);
74301706d20SPoul-Henning Kamp 	struct ccd_s *cs;
744a56bb8a5SSatoshi Asami 	int error = 0, part;
745a56bb8a5SSatoshi Asami 
746ddbf51afSPoul-Henning Kamp 	if (dev->si_drv1 == dev)
747ddbf51afSPoul-Henning Kamp 		return (0);
748a56bb8a5SSatoshi Asami #ifdef DEBUG
749a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
7506a5a4d0aSAndrew Gallatin 		printf("ccdclose(%p, %x)\n", dev, flags);
751a56bb8a5SSatoshi Asami #endif
752a56bb8a5SSatoshi Asami 
75301706d20SPoul-Henning Kamp 	if (!IS_ALLOCATED(unit))
754a56bb8a5SSatoshi Asami 		return (ENXIO);
75501706d20SPoul-Henning Kamp 	cs = ccdfind(unit);
756a56bb8a5SSatoshi Asami 
757b4e36adfSMatthew Dillon 	if ((error = ccdlock(cs)) != 0)
758a56bb8a5SSatoshi Asami 		return (error);
759a56bb8a5SSatoshi Asami 
760d8594dfbSSatoshi Asami 	part = ccdpart(dev);
761a56bb8a5SSatoshi Asami 
762a56bb8a5SSatoshi Asami 	/* ...that much closer to allowing unconfiguration... */
763af8862e4SPoul-Henning Kamp 	cs->sc_openmask &= ~(1 << part);
76401706d20SPoul-Henning Kamp 	/* collect "garbage" if possible */
76501706d20SPoul-Henning Kamp 	if (!IS_INITED(cs) && (cs->sc_flags & CCDF_WANTED) == 0)
766b40ce416SJulian Elischer 		ccddestroy(cs, td->td_proc);
76701706d20SPoul-Henning Kamp 	else
768a56bb8a5SSatoshi Asami 		ccdunlock(cs);
769a56bb8a5SSatoshi Asami 	return (0);
770a56bb8a5SSatoshi Asami }
771a56bb8a5SSatoshi Asami 
772e2738b4fSPoul-Henning Kamp static void
77301706d20SPoul-Henning Kamp ccdstrategy(struct bio *bp)
774a56bb8a5SSatoshi Asami {
7759d7f7369SPoul-Henning Kamp 	int unit = ccdunit(bp->bio_dev);
77601706d20SPoul-Henning Kamp 	struct ccd_s *cs = ccdfind(unit);
7771464240eSMatthew Dillon 	int s;
778a56bb8a5SSatoshi Asami 	int wlabel;
779a56bb8a5SSatoshi Asami 	struct disklabel *lp;
780a56bb8a5SSatoshi Asami 
781ddbf51afSPoul-Henning Kamp 	if (bp->bio_dev->si_drv1 == bp->bio_dev) {
782ddbf51afSPoul-Henning Kamp 		biofinish(bp, NULL, ENXIO);
783ddbf51afSPoul-Henning Kamp 		return;
784ddbf51afSPoul-Henning Kamp 	}
785a56bb8a5SSatoshi Asami #ifdef DEBUG
786a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
7876a5a4d0aSAndrew Gallatin 		printf("ccdstrategy(%p): unit %d\n", bp, unit);
788a56bb8a5SSatoshi Asami #endif
78901706d20SPoul-Henning Kamp 	if (!IS_INITED(cs)) {
790724682d2SPoul-Henning Kamp 		biofinish(bp, NULL, ENXIO);
791724682d2SPoul-Henning Kamp 		return;
792a56bb8a5SSatoshi Asami 	}
793a56bb8a5SSatoshi Asami 
794a56bb8a5SSatoshi Asami 	/* If it's a nil transfer, wake up the top half now. */
795724682d2SPoul-Henning Kamp 	if (bp->bio_bcount == 0) {
796724682d2SPoul-Henning Kamp 		biodone(bp);
797724682d2SPoul-Henning Kamp 		return;
798724682d2SPoul-Henning Kamp 	}
799a56bb8a5SSatoshi Asami 
8006cc5a722SPoul-Henning Kamp 	lp = &cs->sc_label;
801a56bb8a5SSatoshi Asami 
802a56bb8a5SSatoshi Asami 	/*
803a56bb8a5SSatoshi Asami 	 * Do bounds checking and adjust transfer.  If there's an
804a56bb8a5SSatoshi Asami 	 * error, the bounds check will flag that for us.
805a56bb8a5SSatoshi Asami 	 */
806a56bb8a5SSatoshi Asami 	wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
8079d7f7369SPoul-Henning Kamp 	if (ccdpart(bp->bio_dev) != RAW_PART) {
808724682d2SPoul-Henning Kamp 		if (bounds_check_with_label(bp, lp, wlabel) <= 0) {
809724682d2SPoul-Henning Kamp 			biodone(bp);
810724682d2SPoul-Henning Kamp 			return;
811724682d2SPoul-Henning Kamp 		}
81274427f90SMatthew Dillon 	} else {
81374427f90SMatthew Dillon 		int pbn;        /* in sc_secsize chunks */
81474427f90SMatthew Dillon 		long sz;        /* in sc_secsize chunks */
81574427f90SMatthew Dillon 
8169d7f7369SPoul-Henning Kamp 		pbn = bp->bio_blkno / (cs->sc_geom.ccg_secsize / DEV_BSIZE);
8179d7f7369SPoul-Henning Kamp 		sz = howmany(bp->bio_bcount, cs->sc_geom.ccg_secsize);
81874427f90SMatthew Dillon 
81974427f90SMatthew Dillon 		/*
82074427f90SMatthew Dillon 		 * If out of bounds return an error. If at the EOF point,
82174427f90SMatthew Dillon 		 * simply read or write less.
82274427f90SMatthew Dillon 		 */
82374427f90SMatthew Dillon 
82474427f90SMatthew Dillon 		if (pbn < 0 || pbn >= cs->sc_size) {
8259d7f7369SPoul-Henning Kamp 			bp->bio_resid = bp->bio_bcount;
826724682d2SPoul-Henning Kamp 			if (pbn != cs->sc_size)
827724682d2SPoul-Henning Kamp 				biofinish(bp, NULL, EINVAL);
828724682d2SPoul-Henning Kamp 			else
829724682d2SPoul-Henning Kamp 				biodone(bp);
830724682d2SPoul-Henning Kamp 			return;
83174427f90SMatthew Dillon 		}
83274427f90SMatthew Dillon 
83374427f90SMatthew Dillon 		/*
83474427f90SMatthew Dillon 		 * If the request crosses EOF, truncate the request.
83574427f90SMatthew Dillon 		 */
83674427f90SMatthew Dillon 		if (pbn + sz > cs->sc_size) {
8379d7f7369SPoul-Henning Kamp 			bp->bio_bcount = (cs->sc_size - pbn) *
83874427f90SMatthew Dillon 			    cs->sc_geom.ccg_secsize;
83974427f90SMatthew Dillon 		}
84074427f90SMatthew Dillon 	}
841a56bb8a5SSatoshi Asami 
8429d7f7369SPoul-Henning Kamp 	bp->bio_resid = bp->bio_bcount;
843a56bb8a5SSatoshi Asami 
844a56bb8a5SSatoshi Asami 	/*
845a56bb8a5SSatoshi Asami 	 * "Start" the unit.
846a56bb8a5SSatoshi Asami 	 */
847a56bb8a5SSatoshi Asami 	s = splbio();
848a56bb8a5SSatoshi Asami 	ccdstart(cs, bp);
849a56bb8a5SSatoshi Asami 	splx(s);
850a56bb8a5SSatoshi Asami 	return;
851a56bb8a5SSatoshi Asami }
852a56bb8a5SSatoshi Asami 
853a56bb8a5SSatoshi Asami static void
85401706d20SPoul-Henning Kamp ccdstart(struct ccd_s *cs, struct bio *bp)
855a56bb8a5SSatoshi Asami {
8561464240eSMatthew Dillon 	long bcount, rcount;
8573bc746beSSatoshi Asami 	struct ccdbuf *cbp[4];
8583bc746beSSatoshi Asami 	/* XXX! : 2 reads and 2 writes for RAID 4/5 */
859a56bb8a5SSatoshi Asami 	caddr_t addr;
860a56bb8a5SSatoshi Asami 	daddr_t bn;
861a56bb8a5SSatoshi Asami 	struct partition *pp;
862a56bb8a5SSatoshi Asami 
863a56bb8a5SSatoshi Asami #ifdef DEBUG
864a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
8656a5a4d0aSAndrew Gallatin 		printf("ccdstart(%p, %p)\n", cs, bp);
866a56bb8a5SSatoshi Asami #endif
867a56bb8a5SSatoshi Asami 
868b2dfb1f9SJustin T. Gibbs 	/* Record the transaction start  */
869b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&cs->device_stats);
870a56bb8a5SSatoshi Asami 
871a56bb8a5SSatoshi Asami 	/*
872a56bb8a5SSatoshi Asami 	 * Translate the partition-relative block number to an absolute.
873a56bb8a5SSatoshi Asami 	 */
8749d7f7369SPoul-Henning Kamp 	bn = bp->bio_blkno;
8759d7f7369SPoul-Henning Kamp 	if (ccdpart(bp->bio_dev) != RAW_PART) {
8769d7f7369SPoul-Henning Kamp 		pp = &cs->sc_label.d_partitions[ccdpart(bp->bio_dev)];
877a56bb8a5SSatoshi Asami 		bn += pp->p_offset;
878a56bb8a5SSatoshi Asami 	}
879a56bb8a5SSatoshi Asami 
880a56bb8a5SSatoshi Asami 	/*
881a56bb8a5SSatoshi Asami 	 * Allocate component buffers and fire off the requests
882a56bb8a5SSatoshi Asami 	 */
8839d7f7369SPoul-Henning Kamp 	addr = bp->bio_data;
8849d7f7369SPoul-Henning Kamp 	for (bcount = bp->bio_bcount; bcount > 0; bcount -= rcount) {
8853bc746beSSatoshi Asami 		ccdbuffer(cbp, cs, bp, bn, addr, bcount);
8869d7f7369SPoul-Henning Kamp 		rcount = cbp[0]->cb_buf.bio_bcount;
8871464240eSMatthew Dillon 
8881464240eSMatthew Dillon 		if (cs->sc_cflags & CCDF_MIRROR) {
8891464240eSMatthew Dillon 			/*
8901464240eSMatthew Dillon 			 * Mirroring.  Writes go to both disks, reads are
8911464240eSMatthew Dillon 			 * taken from whichever disk seems most appropriate.
8921464240eSMatthew Dillon 			 *
8931464240eSMatthew Dillon 			 * We attempt to localize reads to the disk whos arm
8941464240eSMatthew Dillon 			 * is nearest the read request.  We ignore seeks due
8951464240eSMatthew Dillon 			 * to writes when making this determination and we
8961464240eSMatthew Dillon 			 * also try to avoid hogging.
8971464240eSMatthew Dillon 			 */
8989d7f7369SPoul-Henning Kamp 			if (cbp[0]->cb_buf.bio_cmd == BIO_WRITE) {
899d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[0]->cb_buf);
900d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[1]->cb_buf);
9011464240eSMatthew Dillon 			} else {
9021464240eSMatthew Dillon 				int pick = cs->sc_pick;
9031464240eSMatthew Dillon 				daddr_t range = cs->sc_size / 16;
9041464240eSMatthew Dillon 
9051464240eSMatthew Dillon 				if (bn < cs->sc_blk[pick] - range ||
9061464240eSMatthew Dillon 				    bn > cs->sc_blk[pick] + range
9071464240eSMatthew Dillon 				) {
9081464240eSMatthew Dillon 					cs->sc_pick = pick = 1 - pick;
9091464240eSMatthew Dillon 				}
9101464240eSMatthew Dillon 				cs->sc_blk[pick] = bn + btodb(rcount);
911d616ee08SPoul-Henning Kamp 				BIO_STRATEGY(&cbp[pick]->cb_buf);
9121464240eSMatthew Dillon 			}
9131464240eSMatthew Dillon 		} else {
9141464240eSMatthew Dillon 			/*
9151464240eSMatthew Dillon 			 * Not mirroring
9161464240eSMatthew Dillon 			 */
917d616ee08SPoul-Henning Kamp 			BIO_STRATEGY(&cbp[0]->cb_buf);
9183bc746beSSatoshi Asami 		}
919a56bb8a5SSatoshi Asami 		bn += btodb(rcount);
920a56bb8a5SSatoshi Asami 		addr += rcount;
921a56bb8a5SSatoshi Asami 	}
922a56bb8a5SSatoshi Asami }
923a56bb8a5SSatoshi Asami 
924a56bb8a5SSatoshi Asami /*
925a56bb8a5SSatoshi Asami  * Build a component buffer header.
926a56bb8a5SSatoshi Asami  */
927e2738b4fSPoul-Henning Kamp static void
92801706d20SPoul-Henning Kamp ccdbuffer(struct ccdbuf **cb, struct ccd_s *cs, struct bio *bp, daddr_t bn, caddr_t addr, long bcount)
929a56bb8a5SSatoshi Asami {
9301464240eSMatthew Dillon 	struct ccdcinfo *ci, *ci2 = NULL;	/* XXX */
9311464240eSMatthew Dillon 	struct ccdbuf *cbp;
9321464240eSMatthew Dillon 	daddr_t cbn, cboff;
9331464240eSMatthew Dillon 	off_t cbc;
934a56bb8a5SSatoshi Asami 
935a56bb8a5SSatoshi Asami #ifdef DEBUG
936a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_IO)
937ff8cc2ebSBruce Evans 		printf("ccdbuffer(%p, %p, %lld, %p, %ld)\n",
938ff8cc2ebSBruce Evans 		    (void *)cs, (void *)bp, (long long)bn, (void *)addr,
939ff8cc2ebSBruce Evans 		    bcount);
940a56bb8a5SSatoshi Asami #endif
941a56bb8a5SSatoshi Asami 	/*
942a56bb8a5SSatoshi Asami 	 * Determine which component bn falls in.
943a56bb8a5SSatoshi Asami 	 */
944a56bb8a5SSatoshi Asami 	cbn = bn;
945a56bb8a5SSatoshi Asami 	cboff = 0;
946a56bb8a5SSatoshi Asami 
947a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0) {
9481464240eSMatthew Dillon 		/*
9491464240eSMatthew Dillon 		 * Serially concatenated and neither a mirror nor a parity
9501464240eSMatthew Dillon 		 * config.  This is a special case.
9511464240eSMatthew Dillon 		 */
9521464240eSMatthew Dillon 		daddr_t sblk;
953a56bb8a5SSatoshi Asami 
954a56bb8a5SSatoshi Asami 		sblk = 0;
955a56bb8a5SSatoshi Asami 		for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
956a56bb8a5SSatoshi Asami 			sblk += ci->ci_size;
957a56bb8a5SSatoshi Asami 		cbn -= sblk;
9581464240eSMatthew Dillon 	} else {
9591464240eSMatthew Dillon 		struct ccdiinfo *ii;
960a56bb8a5SSatoshi Asami 		int ccdisk, off;
961a56bb8a5SSatoshi Asami 
9621464240eSMatthew Dillon 		/*
9631464240eSMatthew Dillon 		 * Calculate cbn, the logical superblock (sc_ileave chunks),
9641464240eSMatthew Dillon 		 * and cboff, a normal block offset (DEV_BSIZE chunks) relative
9651464240eSMatthew Dillon 		 * to cbn.
9661464240eSMatthew Dillon 		 */
9671464240eSMatthew Dillon 		cboff = cbn % cs->sc_ileave;	/* DEV_BSIZE gran */
9681464240eSMatthew Dillon 		cbn = cbn / cs->sc_ileave;	/* DEV_BSIZE * ileave gran */
9691464240eSMatthew Dillon 
9701464240eSMatthew Dillon 		/*
9711464240eSMatthew Dillon 		 * Figure out which interleave table to use.
9721464240eSMatthew Dillon 		 */
9731464240eSMatthew Dillon 		for (ii = cs->sc_itable; ii->ii_ndisk; ii++) {
974a56bb8a5SSatoshi Asami 			if (ii->ii_startblk > cbn)
975a56bb8a5SSatoshi Asami 				break;
9761464240eSMatthew Dillon 		}
977a56bb8a5SSatoshi Asami 		ii--;
9781464240eSMatthew Dillon 
9791464240eSMatthew Dillon 		/*
9801464240eSMatthew Dillon 		 * off is the logical superblock relative to the beginning
9811464240eSMatthew Dillon 		 * of this interleave block.
9821464240eSMatthew Dillon 		 */
983a56bb8a5SSatoshi Asami 		off = cbn - ii->ii_startblk;
9841464240eSMatthew Dillon 
9851464240eSMatthew Dillon 		/*
9861464240eSMatthew Dillon 		 * We must calculate which disk component to use (ccdisk),
9871464240eSMatthew Dillon 		 * and recalculate cbn to be the superblock relative to
9881464240eSMatthew Dillon 		 * the beginning of the component.  This is typically done by
9891464240eSMatthew Dillon 		 * adding 'off' and ii->ii_startoff together.  However, 'off'
9901464240eSMatthew Dillon 		 * must typically be divided by the number of components in
9911464240eSMatthew Dillon 		 * this interleave array to be properly convert it from a
9921464240eSMatthew Dillon 		 * CCD-relative logical superblock number to a
9931464240eSMatthew Dillon 		 * component-relative superblock number.
9941464240eSMatthew Dillon 		 */
995a56bb8a5SSatoshi Asami 		if (ii->ii_ndisk == 1) {
9961464240eSMatthew Dillon 			/*
9971464240eSMatthew Dillon 			 * When we have just one disk, it can't be a mirror
9981464240eSMatthew Dillon 			 * or a parity config.
9991464240eSMatthew Dillon 			 */
1000a56bb8a5SSatoshi Asami 			ccdisk = ii->ii_index[0];
1001a56bb8a5SSatoshi Asami 			cbn = ii->ii_startoff + off;
1002a56bb8a5SSatoshi Asami 		} else {
100309b59204SSatoshi Asami 			if (cs->sc_cflags & CCDF_MIRROR) {
10041464240eSMatthew Dillon 				/*
10051464240eSMatthew Dillon 				 * We have forced a uniform mapping, resulting
10061464240eSMatthew Dillon 				 * in a single interleave array.  We double
10071464240eSMatthew Dillon 				 * up on the first half of the available
10081464240eSMatthew Dillon 				 * components and our mirror is in the second
10091464240eSMatthew Dillon 				 * half.  This only works with a single
10101464240eSMatthew Dillon 				 * interleave array because doubling up
10111464240eSMatthew Dillon 				 * doubles the number of sectors, so there
10121464240eSMatthew Dillon 				 * cannot be another interleave array because
10131464240eSMatthew Dillon 				 * the next interleave array's calculations
10141464240eSMatthew Dillon 				 * would be off.
10151464240eSMatthew Dillon 				 */
10161464240eSMatthew Dillon 				int ndisk2 = ii->ii_ndisk / 2;
10171464240eSMatthew Dillon 				ccdisk = ii->ii_index[off % ndisk2];
10181464240eSMatthew Dillon 				cbn = ii->ii_startoff + off / ndisk2;
10191464240eSMatthew Dillon 				ci2 = &cs->sc_cinfo[ccdisk + ndisk2];
10201464240eSMatthew Dillon 			} else {
1021a56bb8a5SSatoshi Asami 				ccdisk = ii->ii_index[off % ii->ii_ndisk];
1022a56bb8a5SSatoshi Asami 				cbn = ii->ii_startoff + off / ii->ii_ndisk;
1023a56bb8a5SSatoshi Asami 			}
10247ecb65faSSatoshi Asami 		}
10251464240eSMatthew Dillon 
1026a56bb8a5SSatoshi Asami 		ci = &cs->sc_cinfo[ccdisk];
10271464240eSMatthew Dillon 
10281464240eSMatthew Dillon 		/*
10291464240eSMatthew Dillon 		 * Convert cbn from a superblock to a normal block so it
10301464240eSMatthew Dillon 		 * can be used to calculate (along with cboff) the normal
10311464240eSMatthew Dillon 		 * block index into this particular disk.
10321464240eSMatthew Dillon 		 */
10331464240eSMatthew Dillon 		cbn *= cs->sc_ileave;
1034a56bb8a5SSatoshi Asami 	}
1035a56bb8a5SSatoshi Asami 
1036a56bb8a5SSatoshi Asami 	/*
1037a56bb8a5SSatoshi Asami 	 * Fill in the component buf structure.
1038a56bb8a5SSatoshi Asami 	 */
10391464240eSMatthew Dillon 	cbp = getccdbuf(NULL);
10409d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_cmd = bp->bio_cmd;
10419d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_done = ccdiodone;
10429d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_dev = ci->ci_dev;		/* XXX */
10439d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_blkno = cbn + cboff + CCD_OFFSET;
10449d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_offset = dbtob(cbn + cboff + CCD_OFFSET);
10459d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_data = addr;
1046a56bb8a5SSatoshi Asami 	if (cs->sc_ileave == 0)
104740969e38SDavid Greenman               cbc = dbtob((off_t)(ci->ci_size - cbn));
1048a56bb8a5SSatoshi Asami 	else
104940969e38SDavid Greenman               cbc = dbtob((off_t)(cs->sc_ileave - cboff));
10509d7f7369SPoul-Henning Kamp 	cbp->cb_buf.bio_bcount = (cbc < bcount) ? cbc : bcount;
10519d7f7369SPoul-Henning Kamp  	cbp->cb_buf.bio_caller1 = (void*)cbp->cb_buf.bio_bcount;
1052c0b89506SJohn Dyson 
1053a56bb8a5SSatoshi Asami 	/*
1054a56bb8a5SSatoshi Asami 	 * context for ccdiodone
1055a56bb8a5SSatoshi Asami 	 */
1056a56bb8a5SSatoshi Asami 	cbp->cb_obp = bp;
105701706d20SPoul-Henning Kamp 	cbp->cb_unit = cs->sc_unit;
1058a56bb8a5SSatoshi Asami 	cbp->cb_comp = ci - cs->sc_cinfo;
1059a56bb8a5SSatoshi Asami 
1060a56bb8a5SSatoshi Asami #ifdef DEBUG
1061a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_IO)
10622dfbcdd4SMaxime Henrion 		printf(" dev %p(u%ld): cbp %p bn %jd addr %p bcnt %ld\n",
10636a5a4d0aSAndrew Gallatin 		       ci->ci_dev, (unsigned long)(ci-cs->sc_cinfo), cbp,
10642dfbcdd4SMaxime Henrion 		       (intmax_t)cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data,
10656a5a4d0aSAndrew Gallatin 		       cbp->cb_buf.bio_bcount);
1066a56bb8a5SSatoshi Asami #endif
10673bc746beSSatoshi Asami 	cb[0] = cbp;
10681464240eSMatthew Dillon 
10691464240eSMatthew Dillon 	/*
10701464240eSMatthew Dillon 	 * Note: both I/O's setup when reading from mirror, but only one
10711464240eSMatthew Dillon 	 * will be executed.
10721464240eSMatthew Dillon 	 */
10731464240eSMatthew Dillon 	if (cs->sc_cflags & CCDF_MIRROR) {
10741464240eSMatthew Dillon 		/* mirror, setup second I/O */
10751464240eSMatthew Dillon 		cbp = getccdbuf(cb[0]);
10769d7f7369SPoul-Henning Kamp 		cbp->cb_buf.bio_dev = ci2->ci_dev;
10773bc746beSSatoshi Asami 		cbp->cb_comp = ci2 - cs->sc_cinfo;
10783bc746beSSatoshi Asami 		cb[1] = cbp;
1079e7322872SSatoshi Asami 		/* link together the ccdbuf's and clear "mirror done" flag */
1080e7322872SSatoshi Asami 		cb[0]->cb_mirror = cb[1];
1081e7322872SSatoshi Asami 		cb[1]->cb_mirror = cb[0];
1082e7322872SSatoshi Asami 		cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
1083e7322872SSatoshi Asami 		cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
10843bc746beSSatoshi Asami 	}
1085a56bb8a5SSatoshi Asami }
1086a56bb8a5SSatoshi Asami 
1087a56bb8a5SSatoshi Asami static void
108801706d20SPoul-Henning Kamp ccdintr(struct ccd_s *cs, struct bio *bp)
1089a56bb8a5SSatoshi Asami {
1090a56bb8a5SSatoshi Asami #ifdef DEBUG
1091a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
10926a5a4d0aSAndrew Gallatin 		printf("ccdintr(%p, %p)\n", cs, bp);
1093a56bb8a5SSatoshi Asami #endif
1094a56bb8a5SSatoshi Asami 	/*
1095a56bb8a5SSatoshi Asami 	 * Request is done for better or worse, wakeup the top half.
1096a56bb8a5SSatoshi Asami 	 */
10979d7f7369SPoul-Henning Kamp 	if (bp->bio_flags & BIO_ERROR)
10989d7f7369SPoul-Henning Kamp 		bp->bio_resid = bp->bio_bcount;
1099a468031cSPoul-Henning Kamp 	biofinish(bp, &cs->device_stats, 0);
1100a56bb8a5SSatoshi Asami }
1101a56bb8a5SSatoshi Asami 
1102a56bb8a5SSatoshi Asami /*
1103a56bb8a5SSatoshi Asami  * Called at interrupt time.
1104a56bb8a5SSatoshi Asami  * Mark the component as done and if all components are done,
1105a56bb8a5SSatoshi Asami  * take a ccd interrupt.
1106a56bb8a5SSatoshi Asami  */
1107e2738b4fSPoul-Henning Kamp static void
110801706d20SPoul-Henning Kamp ccdiodone(struct bio *ibp)
1109a56bb8a5SSatoshi Asami {
111021144e3bSPoul-Henning Kamp 	struct ccdbuf *cbp = (struct ccdbuf *)ibp;
11119d7f7369SPoul-Henning Kamp 	struct bio *bp = cbp->cb_obp;
11121464240eSMatthew Dillon 	int unit = cbp->cb_unit;
1113a56bb8a5SSatoshi Asami 	int count, s;
1114a56bb8a5SSatoshi Asami 
1115a56bb8a5SSatoshi Asami 	s = splbio();
1116a56bb8a5SSatoshi Asami #ifdef DEBUG
1117a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_FOLLOW)
11186a5a4d0aSAndrew Gallatin 		printf("ccdiodone(%p)\n", cbp);
1119a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_IO) {
11206a5a4d0aSAndrew Gallatin 		printf("ccdiodone: bp %p bcount %ld resid %ld\n",
11219d7f7369SPoul-Henning Kamp 		       bp, bp->bio_bcount, bp->bio_resid);
11222dfbcdd4SMaxime Henrion 		printf(" dev %p(u%d), cbp %p bn %jd addr %p bcnt %ld\n",
11239d7f7369SPoul-Henning Kamp 		       cbp->cb_buf.bio_dev, cbp->cb_comp, cbp,
11242dfbcdd4SMaxime Henrion 		       (intmax_t)cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data,
11259d7f7369SPoul-Henning Kamp 		       cbp->cb_buf.bio_bcount);
1126a56bb8a5SSatoshi Asami 	}
1127a56bb8a5SSatoshi Asami #endif
11281464240eSMatthew Dillon 	/*
11291464240eSMatthew Dillon 	 * If an error occured, report it.  If this is a mirrored
11301464240eSMatthew Dillon 	 * configuration and the first of two possible reads, do not
11311464240eSMatthew Dillon 	 * set the error in the bp yet because the second read may
11321464240eSMatthew Dillon 	 * succeed.
11331464240eSMatthew Dillon 	 */
1134a56bb8a5SSatoshi Asami 
11359d7f7369SPoul-Henning Kamp 	if (cbp->cb_buf.bio_flags & BIO_ERROR) {
11361464240eSMatthew Dillon 		const char *msg = "";
11371464240eSMatthew Dillon 
113801706d20SPoul-Henning Kamp 		if ((ccdfind(unit)->sc_cflags & CCDF_MIRROR) &&
11399d7f7369SPoul-Henning Kamp 		    (cbp->cb_buf.bio_cmd == BIO_READ) &&
11401464240eSMatthew Dillon 		    (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
11411464240eSMatthew Dillon 			/*
11421464240eSMatthew Dillon 			 * We will try our read on the other disk down
11431464240eSMatthew Dillon 			 * below, also reverse the default pick so if we
11441464240eSMatthew Dillon 			 * are doing a scan we do not keep hitting the
11451464240eSMatthew Dillon 			 * bad disk first.
11461464240eSMatthew Dillon 			 */
114701706d20SPoul-Henning Kamp 			struct ccd_s *cs = ccdfind(unit);
11481464240eSMatthew Dillon 
11491464240eSMatthew Dillon 			msg = ", trying other disk";
11501464240eSMatthew Dillon 			cs->sc_pick = 1 - cs->sc_pick;
11519d7f7369SPoul-Henning Kamp 			cs->sc_blk[cs->sc_pick] = bp->bio_blkno;
11521464240eSMatthew Dillon 		} else {
11539d7f7369SPoul-Henning Kamp 			bp->bio_flags |= BIO_ERROR;
11549d7f7369SPoul-Henning Kamp 			bp->bio_error = cbp->cb_buf.bio_error ?
11559d7f7369SPoul-Henning Kamp 			    cbp->cb_buf.bio_error : EIO;
11561464240eSMatthew Dillon 		}
115792faa7b5SMaxime Henrion 		printf("ccd%d: error %d on component %d block %jd "
1158f368af93SMaxime Henrion 		    "(ccd block %jd)%s\n", unit, bp->bio_error, cbp->cb_comp,
115992faa7b5SMaxime Henrion 		    (intmax_t)cbp->cb_buf.bio_blkno, (intmax_t)bp->bio_blkno,
116092faa7b5SMaxime Henrion 		    msg);
1161a56bb8a5SSatoshi Asami 	}
1162e7322872SSatoshi Asami 
11631464240eSMatthew Dillon 	/*
11641464240eSMatthew Dillon 	 * Process mirror.  If we are writing, I/O has been initiated on both
11651464240eSMatthew Dillon 	 * buffers and we fall through only after both are finished.
11661464240eSMatthew Dillon 	 *
11671464240eSMatthew Dillon 	 * If we are reading only one I/O is initiated at a time.  If an
11681464240eSMatthew Dillon 	 * error occurs we initiate the second I/O and return, otherwise
11691464240eSMatthew Dillon 	 * we free the second I/O without initiating it.
11701464240eSMatthew Dillon 	 */
11711464240eSMatthew Dillon 
117201706d20SPoul-Henning Kamp 	if (ccdfind(unit)->sc_cflags & CCDF_MIRROR) {
11739d7f7369SPoul-Henning Kamp 		if (cbp->cb_buf.bio_cmd == BIO_WRITE) {
11741464240eSMatthew Dillon 			/*
11751464240eSMatthew Dillon 			 * When writing, handshake with the second buffer
11761464240eSMatthew Dillon 			 * to determine when both are done.  If both are not
11771464240eSMatthew Dillon 			 * done, return here.
11781464240eSMatthew Dillon 			 */
1179e7322872SSatoshi Asami 			if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1180e7322872SSatoshi Asami 				cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
1181e7322872SSatoshi Asami 				putccdbuf(cbp);
1182e7322872SSatoshi Asami 				splx(s);
1183e7322872SSatoshi Asami 				return;
1184e7322872SSatoshi Asami 			}
11851464240eSMatthew Dillon 		} else {
11861464240eSMatthew Dillon 			/*
11871464240eSMatthew Dillon 			 * When reading, either dispose of the second buffer
11881464240eSMatthew Dillon 			 * or initiate I/O on the second buffer if an error
11891464240eSMatthew Dillon 			 * occured with this one.
11901464240eSMatthew Dillon 			 */
11911464240eSMatthew Dillon 			if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
11929d7f7369SPoul-Henning Kamp 				if (cbp->cb_buf.bio_flags & BIO_ERROR) {
11931464240eSMatthew Dillon 					cbp->cb_mirror->cb_pflags |=
11941464240eSMatthew Dillon 					    CCDPF_MIRROR_DONE;
1195d616ee08SPoul-Henning Kamp 					BIO_STRATEGY(&cbp->cb_mirror->cb_buf);
11961464240eSMatthew Dillon 					putccdbuf(cbp);
11971464240eSMatthew Dillon 					splx(s);
11981464240eSMatthew Dillon 					return;
11991464240eSMatthew Dillon 				} else {
12001464240eSMatthew Dillon 					putccdbuf(cbp->cb_mirror);
12011464240eSMatthew Dillon 					/* fall through */
12021464240eSMatthew Dillon 				}
12031464240eSMatthew Dillon 			}
12041464240eSMatthew Dillon 		}
12051464240eSMatthew Dillon 	}
1206e7322872SSatoshi Asami 
120725d1a00bSMatthew Dillon 	/*
12089d7f7369SPoul-Henning Kamp 	 * use bio_caller1 to determine how big the original request was rather
12099d7f7369SPoul-Henning Kamp 	 * then bio_bcount, because bio_bcount may have been truncated for EOF.
121025d1a00bSMatthew Dillon 	 *
121125d1a00bSMatthew Dillon 	 * XXX We check for an error, but we do not test the resid for an
121225d1a00bSMatthew Dillon 	 * aligned EOF condition.  This may result in character & block
121325d1a00bSMatthew Dillon 	 * device access not recognizing EOF properly when read or written
121425d1a00bSMatthew Dillon 	 * sequentially, but will not effect filesystems.
121525d1a00bSMatthew Dillon 	 */
12169d7f7369SPoul-Henning Kamp 	count = (long)cbp->cb_buf.bio_caller1;
1217a56bb8a5SSatoshi Asami 	putccdbuf(cbp);
1218a56bb8a5SSatoshi Asami 
1219a56bb8a5SSatoshi Asami 	/*
1220a56bb8a5SSatoshi Asami 	 * If all done, "interrupt".
1221a56bb8a5SSatoshi Asami 	 */
12229d7f7369SPoul-Henning Kamp 	bp->bio_resid -= count;
12239d7f7369SPoul-Henning Kamp 	if (bp->bio_resid < 0)
1224a56bb8a5SSatoshi Asami 		panic("ccdiodone: count");
12259d7f7369SPoul-Henning Kamp 	if (bp->bio_resid == 0)
122601706d20SPoul-Henning Kamp 		ccdintr(ccdfind(unit), bp);
1227a56bb8a5SSatoshi Asami 	splx(s);
1228a56bb8a5SSatoshi Asami }
1229a56bb8a5SSatoshi Asami 
1230e2738b4fSPoul-Henning Kamp static int
1231b40ce416SJulian Elischer ccdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
1232a56bb8a5SSatoshi Asami {
1233ddbf51afSPoul-Henning Kamp 	struct ccd_ioctl *ccio;
1234ddbf51afSPoul-Henning Kamp 	u_int unit;
1235ddbf51afSPoul-Henning Kamp 	dev_t dev2;
1236ddbf51afSPoul-Henning Kamp 	int error;
1237ddbf51afSPoul-Henning Kamp 
1238ddbf51afSPoul-Henning Kamp 	if (dev->si_drv1 != dev) {
1239ddbf51afSPoul-Henning Kamp 		switch (cmd) {
1240ddbf51afSPoul-Henning Kamp 		case CCDIOCSET:
1241ddbf51afSPoul-Henning Kamp 		case CCDIOCCLR:
1242ddbf51afSPoul-Henning Kamp 		case CCDCONFINFO:
1243ddbf51afSPoul-Henning Kamp 		case CCDCPPINFO:
1244ddbf51afSPoul-Henning Kamp 			printf("*** WARNING: upgrade your ccdconfig(8) binary\n");
1245ddbf51afSPoul-Henning Kamp 			printf("*** WARNING: continuing in 30 seconds\n");
1246ddbf51afSPoul-Henning Kamp 			tsleep(dev, PRIBIO, "ccdbug", hz * 30);
1247ddbf51afSPoul-Henning Kamp 			break;
1248ddbf51afSPoul-Henning Kamp 		}
1249ddbf51afSPoul-Henning Kamp 		return ccdioctltoo(dev, cmd, data, flag, td);
1250ddbf51afSPoul-Henning Kamp 	}
1251ddbf51afSPoul-Henning Kamp 	switch (cmd) {
1252ddbf51afSPoul-Henning Kamp 	case CCDIOCSET:
1253ddbf51afSPoul-Henning Kamp 	case CCDIOCCLR:
1254ddbf51afSPoul-Henning Kamp 		ccio = (struct ccd_ioctl *)data;
1255ddbf51afSPoul-Henning Kamp 		unit = ccio->ccio_size;
1256ddbf51afSPoul-Henning Kamp 		dev2 = makedev(CDEV_MAJOR, unit * 8 + 2);
1257ddbf51afSPoul-Henning Kamp 		if (!(dev2->si_flags & SI_NAMED)) {
1258ddbf51afSPoul-Henning Kamp 			dev2 = make_dev(&ccd_cdevsw, unit * 8 + 2,
1259ddbf51afSPoul-Henning Kamp 				UID_ROOT, GID_OPERATOR, 0640, "ccd%dc", unit);
1260ddbf51afSPoul-Henning Kamp 			ccdnew(unit);
1261ddbf51afSPoul-Henning Kamp 		}
1262ddbf51afSPoul-Henning Kamp 		return (ccdioctltoo(dev2, cmd, data, flag, td));
1263ddbf51afSPoul-Henning Kamp 	case CCDCONFINFO:
1264ddbf51afSPoul-Henning Kamp 		{
1265ddbf51afSPoul-Henning Kamp 		int ninit = 0;
1266ddbf51afSPoul-Henning Kamp 		struct ccdconf *conf = (struct ccdconf *)data;
1267ddbf51afSPoul-Henning Kamp 		struct ccd_s *tmpcs;
1268ddbf51afSPoul-Henning Kamp 		struct ccd_s *ubuf = conf->buffer;
1269ddbf51afSPoul-Henning Kamp 
1270ddbf51afSPoul-Henning Kamp 		/* XXX: LOCK(unique unit numbers) */
1271ddbf51afSPoul-Henning Kamp 		LIST_FOREACH(tmpcs, &ccd_softc_list, list)
1272ddbf51afSPoul-Henning Kamp 			if (IS_INITED(tmpcs))
1273ddbf51afSPoul-Henning Kamp 				ninit++;
1274ddbf51afSPoul-Henning Kamp 
1275ddbf51afSPoul-Henning Kamp 		if (conf->size == 0) {
1276ddbf51afSPoul-Henning Kamp 			conf->size = sizeof(struct ccd_s) * ninit;
1277ddbf51afSPoul-Henning Kamp 			return (0);
1278ddbf51afSPoul-Henning Kamp 		} else if ((conf->size / sizeof(struct ccd_s) != ninit) ||
1279ddbf51afSPoul-Henning Kamp 		    (conf->size % sizeof(struct ccd_s) != 0)) {
1280ddbf51afSPoul-Henning Kamp 			/* XXX: UNLOCK(unique unit numbers) */
1281ddbf51afSPoul-Henning Kamp 			return (EINVAL);
1282ddbf51afSPoul-Henning Kamp 		}
1283ddbf51afSPoul-Henning Kamp 
1284ddbf51afSPoul-Henning Kamp 		ubuf += ninit;
1285ddbf51afSPoul-Henning Kamp 		LIST_FOREACH(tmpcs, &ccd_softc_list, list) {
1286ddbf51afSPoul-Henning Kamp 			if (!IS_INITED(tmpcs))
1287ddbf51afSPoul-Henning Kamp 				continue;
1288ddbf51afSPoul-Henning Kamp 			error = copyout(tmpcs, --ubuf,
1289ddbf51afSPoul-Henning Kamp 			    sizeof(struct ccd_s));
1290ddbf51afSPoul-Henning Kamp 			if (error != 0)
1291ddbf51afSPoul-Henning Kamp 				/* XXX: UNLOCK(unique unit numbers) */
1292ddbf51afSPoul-Henning Kamp 				return (error);
1293ddbf51afSPoul-Henning Kamp 		}
1294ddbf51afSPoul-Henning Kamp 		/* XXX: UNLOCK(unique unit numbers) */
1295ddbf51afSPoul-Henning Kamp 		return (0);
1296ddbf51afSPoul-Henning Kamp 		}
1297ddbf51afSPoul-Henning Kamp 
1298ddbf51afSPoul-Henning Kamp 	case CCDCPPINFO:
1299ddbf51afSPoul-Henning Kamp 		{
1300ddbf51afSPoul-Henning Kamp 		struct ccdcpps *cpps = (struct ccdcpps *)data;
1301ddbf51afSPoul-Henning Kamp 		char *ubuf = cpps->buffer;
1302ddbf51afSPoul-Henning Kamp 
1303ddbf51afSPoul-Henning Kamp 
1304ddbf51afSPoul-Henning Kamp 		error = copyin(ubuf, &unit, sizeof (unit));
1305ddbf51afSPoul-Henning Kamp 		if (error)
1306ddbf51afSPoul-Henning Kamp 			return (error);
1307ddbf51afSPoul-Henning Kamp 
1308ddbf51afSPoul-Henning Kamp 		if (!IS_ALLOCATED(unit))
1309ddbf51afSPoul-Henning Kamp 			return (ENXIO);
1310ddbf51afSPoul-Henning Kamp 		dev2 = makedev(CDEV_MAJOR, unit * 8 + 2);
1311ddbf51afSPoul-Henning Kamp 		return (ccdioctltoo(dev2, cmd, data, flag, td));
1312ddbf51afSPoul-Henning Kamp 		}
1313ddbf51afSPoul-Henning Kamp 
1314ddbf51afSPoul-Henning Kamp 	default:
1315ddbf51afSPoul-Henning Kamp 		return (ENXIO);
1316ddbf51afSPoul-Henning Kamp 	}
1317ddbf51afSPoul-Henning Kamp }
1318ddbf51afSPoul-Henning Kamp 
1319ddbf51afSPoul-Henning Kamp static int
1320ddbf51afSPoul-Henning Kamp ccdioctltoo(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
1321ddbf51afSPoul-Henning Kamp {
1322ddbf51afSPoul-Henning Kamp 	int unit;
1323a56bb8a5SSatoshi Asami 	int i, j, lookedup = 0, error = 0;
1324a56bb8a5SSatoshi Asami 	int part, pmask, s;
132501706d20SPoul-Henning Kamp 	struct ccd_s *cs;
1326a56bb8a5SSatoshi Asami 	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
1327a56bb8a5SSatoshi Asami 	char **cpp;
1328a56bb8a5SSatoshi Asami 	struct vnode **vpp;
1329a56bb8a5SSatoshi Asami 
1330ddbf51afSPoul-Henning Kamp 	unit = ccdunit(dev);
133101706d20SPoul-Henning Kamp 	if (!IS_ALLOCATED(unit))
1332a56bb8a5SSatoshi Asami 		return (ENXIO);
133301706d20SPoul-Henning Kamp 	cs = ccdfind(unit);
1334a56bb8a5SSatoshi Asami 
1335a56bb8a5SSatoshi Asami 	switch (cmd) {
1336a56bb8a5SSatoshi Asami 	case CCDIOCSET:
133701706d20SPoul-Henning Kamp 		if (IS_INITED(cs))
1338a56bb8a5SSatoshi Asami 			return (EBUSY);
1339a56bb8a5SSatoshi Asami 
1340a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1341a56bb8a5SSatoshi Asami 			return (EBADF);
1342a56bb8a5SSatoshi Asami 
1343b4e36adfSMatthew Dillon 		if ((error = ccdlock(cs)) != 0)
1344a56bb8a5SSatoshi Asami 			return (error);
1345a56bb8a5SSatoshi Asami 
1346bf61e266SKris Kennaway 		if (ccio->ccio_ndisks > CCD_MAXNDISKS)
1347bf61e266SKris Kennaway 			return (EINVAL);
1348bf61e266SKris Kennaway 
1349a56bb8a5SSatoshi Asami 		/* Fill in some important bits. */
135001706d20SPoul-Henning Kamp 		cs->sc_ileave = ccio->ccio_ileave;
1351f05f44f0SPoul-Henning Kamp 		if (cs->sc_ileave == 0 && (ccio->ccio_flags & CCDF_MIRROR)) {
1352f05f44f0SPoul-Henning Kamp 			printf("ccd%d: disabling mirror, interleave is 0\n",
1353f05f44f0SPoul-Henning Kamp 			    unit);
1354f05f44f0SPoul-Henning Kamp 			ccio->ccio_flags &= ~(CCDF_MIRROR);
1355b8e29b55SSatoshi Asami 		}
135609b59204SSatoshi Asami 		if ((ccio->ccio_flags & CCDF_MIRROR) &&
13577ecb65faSSatoshi Asami 		    !(ccio->ccio_flags & CCDF_UNIFORM)) {
135809b59204SSatoshi Asami 			printf("ccd%d: mirror/parity forces uniform flag\n",
135909b59204SSatoshi Asami 			       unit);
13607ecb65faSSatoshi Asami 			ccio->ccio_flags |= CCDF_UNIFORM;
13617ecb65faSSatoshi Asami 		}
136201706d20SPoul-Henning Kamp 		cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK;
1363a56bb8a5SSatoshi Asami 
1364a56bb8a5SSatoshi Asami 		/*
1365a56bb8a5SSatoshi Asami 		 * Allocate space for and copy in the array of
1366a56bb8a5SSatoshi Asami 		 * componet pathnames and device numbers.
1367a56bb8a5SSatoshi Asami 		 */
1368a56bb8a5SSatoshi Asami 		cpp = malloc(ccio->ccio_ndisks * sizeof(char *),
1369a56bb8a5SSatoshi Asami 		    M_DEVBUF, M_WAITOK);
1370a56bb8a5SSatoshi Asami 		vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *),
1371a56bb8a5SSatoshi Asami 		    M_DEVBUF, M_WAITOK);
1372a56bb8a5SSatoshi Asami 
1373a56bb8a5SSatoshi Asami 		error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
1374a56bb8a5SSatoshi Asami 		    ccio->ccio_ndisks * sizeof(char **));
1375a56bb8a5SSatoshi Asami 		if (error) {
1376a56bb8a5SSatoshi Asami 			free(vpp, M_DEVBUF);
1377a56bb8a5SSatoshi Asami 			free(cpp, M_DEVBUF);
1378a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1379a56bb8a5SSatoshi Asami 			return (error);
1380a56bb8a5SSatoshi Asami 		}
1381a56bb8a5SSatoshi Asami 
1382a56bb8a5SSatoshi Asami #ifdef DEBUG
1383a56bb8a5SSatoshi Asami 		if (ccddebug & CCDB_INIT)
1384a56bb8a5SSatoshi Asami 			for (i = 0; i < ccio->ccio_ndisks; ++i)
13856a5a4d0aSAndrew Gallatin 				printf("ccdioctl: component %d: %p\n",
1386a56bb8a5SSatoshi Asami 				    i, cpp[i]);
1387a56bb8a5SSatoshi Asami #endif
1388a56bb8a5SSatoshi Asami 
1389a56bb8a5SSatoshi Asami 		for (i = 0; i < ccio->ccio_ndisks; ++i) {
1390a56bb8a5SSatoshi Asami #ifdef DEBUG
1391a56bb8a5SSatoshi Asami 			if (ccddebug & CCDB_INIT)
1392a56bb8a5SSatoshi Asami 				printf("ccdioctl: lookedup = %d\n", lookedup);
1393a56bb8a5SSatoshi Asami #endif
1394b40ce416SJulian Elischer 			if ((error = ccdlookup(cpp[i], td, &vpp[i])) != 0) {
1395a56bb8a5SSatoshi Asami 				for (j = 0; j < lookedup; ++j)
1396a56bb8a5SSatoshi Asami 					(void)vn_close(vpp[j], FREAD|FWRITE,
1397a854ed98SJohn Baldwin 					    td->td_ucred, td);
1398a56bb8a5SSatoshi Asami 				free(vpp, M_DEVBUF);
1399a56bb8a5SSatoshi Asami 				free(cpp, M_DEVBUF);
1400a56bb8a5SSatoshi Asami 				ccdunlock(cs);
1401a56bb8a5SSatoshi Asami 				return (error);
1402a56bb8a5SSatoshi Asami 			}
1403a56bb8a5SSatoshi Asami 			++lookedup;
1404a56bb8a5SSatoshi Asami 		}
140501706d20SPoul-Henning Kamp 		cs->sc_vpp = vpp;
140601706d20SPoul-Henning Kamp 		cs->sc_nccdisks = ccio->ccio_ndisks;
1407a56bb8a5SSatoshi Asami 
1408a56bb8a5SSatoshi Asami 		/*
1409a56bb8a5SSatoshi Asami 		 * Initialize the ccd.  Fills in the softc for us.
1410a56bb8a5SSatoshi Asami 		 */
1411b40ce416SJulian Elischer 		if ((error = ccdinit(cs, cpp, td)) != 0) {
1412a56bb8a5SSatoshi Asami 			for (j = 0; j < lookedup; ++j)
1413ba88dfc7SJohn Baldwin 				(void)vn_close(vpp[j], FREAD|FWRITE,
1414a854ed98SJohn Baldwin 				    td->td_ucred, td);
141501706d20SPoul-Henning Kamp 			/*
141601706d20SPoul-Henning Kamp 			 * We can't ccddestroy() cs just yet, because nothing
141701706d20SPoul-Henning Kamp 			 * prevents user-level app to do another ioctl()
141801706d20SPoul-Henning Kamp 			 * without closing the device first, therefore
141901706d20SPoul-Henning Kamp 			 * declare unit null and void and let ccdclose()
142001706d20SPoul-Henning Kamp 			 * destroy it when it is safe to do so.
142101706d20SPoul-Henning Kamp 			 */
142201706d20SPoul-Henning Kamp 			cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED);
1423a56bb8a5SSatoshi Asami 			free(vpp, M_DEVBUF);
1424a56bb8a5SSatoshi Asami 			free(cpp, M_DEVBUF);
1425a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1426a56bb8a5SSatoshi Asami 			return (error);
1427a56bb8a5SSatoshi Asami 		}
1428a56bb8a5SSatoshi Asami 
1429a56bb8a5SSatoshi Asami 		/*
1430a56bb8a5SSatoshi Asami 		 * The ccd has been successfully initialized, so
1431a56bb8a5SSatoshi Asami 		 * we can place it into the array and read the disklabel.
1432a56bb8a5SSatoshi Asami 		 */
1433a56bb8a5SSatoshi Asami 		ccio->ccio_unit = unit;
1434a56bb8a5SSatoshi Asami 		ccio->ccio_size = cs->sc_size;
1435a56bb8a5SSatoshi Asami 		ccdgetdisklabel(dev);
1436a56bb8a5SSatoshi Asami 
1437a56bb8a5SSatoshi Asami 		ccdunlock(cs);
1438a56bb8a5SSatoshi Asami 
1439a56bb8a5SSatoshi Asami 		break;
1440a56bb8a5SSatoshi Asami 
1441a56bb8a5SSatoshi Asami 	case CCDIOCCLR:
144201706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
1443a56bb8a5SSatoshi Asami 			return (ENXIO);
1444a56bb8a5SSatoshi Asami 
1445a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1446a56bb8a5SSatoshi Asami 			return (EBADF);
1447a56bb8a5SSatoshi Asami 
1448b4e36adfSMatthew Dillon 		if ((error = ccdlock(cs)) != 0)
1449a56bb8a5SSatoshi Asami 			return (error);
1450a56bb8a5SSatoshi Asami 
1451af8862e4SPoul-Henning Kamp 		/* Don't unconfigure if any other partitions are open */
1452d8594dfbSSatoshi Asami 		part = ccdpart(dev);
1453a56bb8a5SSatoshi Asami 		pmask = (1 << part);
1454af8862e4SPoul-Henning Kamp 		if ((cs->sc_openmask & ~pmask)) {
1455a56bb8a5SSatoshi Asami 			ccdunlock(cs);
1456a56bb8a5SSatoshi Asami 			return (EBUSY);
1457a56bb8a5SSatoshi Asami 		}
1458a56bb8a5SSatoshi Asami 
145901706d20SPoul-Henning Kamp 		/* Declare unit null and void (reset all flags) */
146001706d20SPoul-Henning Kamp 		cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED);
1461a56bb8a5SSatoshi Asami 
1462a56bb8a5SSatoshi Asami 		/* Close the components and free their pathnames. */
1463a56bb8a5SSatoshi Asami 		for (i = 0; i < cs->sc_nccdisks; ++i) {
1464a56bb8a5SSatoshi Asami 			/*
1465a56bb8a5SSatoshi Asami 			 * XXX: this close could potentially fail and
1466a56bb8a5SSatoshi Asami 			 * cause Bad Things.  Maybe we need to force
1467a56bb8a5SSatoshi Asami 			 * the close to happen?
1468a56bb8a5SSatoshi Asami 			 */
1469a56bb8a5SSatoshi Asami #ifdef DEBUG
1470a56bb8a5SSatoshi Asami 			if (ccddebug & CCDB_VNODE)
1471a56bb8a5SSatoshi Asami 				vprint("CCDIOCCLR: vnode info",
1472a56bb8a5SSatoshi Asami 				    cs->sc_cinfo[i].ci_vp);
1473a56bb8a5SSatoshi Asami #endif
1474a56bb8a5SSatoshi Asami 			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1475a854ed98SJohn Baldwin 			    td->td_ucred, td);
1476a56bb8a5SSatoshi Asami 			free(cs->sc_cinfo[i].ci_path, M_DEVBUF);
1477a56bb8a5SSatoshi Asami 		}
1478a56bb8a5SSatoshi Asami 
1479a56bb8a5SSatoshi Asami 		/* Free interleave index. */
1480a56bb8a5SSatoshi Asami 		for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
1481a56bb8a5SSatoshi Asami 			free(cs->sc_itable[i].ii_index, M_DEVBUF);
1482a56bb8a5SSatoshi Asami 
1483a56bb8a5SSatoshi Asami 		/* Free component info and interleave table. */
1484a56bb8a5SSatoshi Asami 		free(cs->sc_cinfo, M_DEVBUF);
1485a56bb8a5SSatoshi Asami 		free(cs->sc_itable, M_DEVBUF);
148601706d20SPoul-Henning Kamp 		free(cs->sc_vpp, M_DEVBUF);
1487a56bb8a5SSatoshi Asami 
148801706d20SPoul-Henning Kamp 		/* And remove the devstat entry. */
148921c3b31eSMatt Jacob 		devstat_remove_entry(&cs->device_stats);
149021c3b31eSMatt Jacob 
1491a56bb8a5SSatoshi Asami 		/* This must be atomic. */
1492a56bb8a5SSatoshi Asami 		s = splhigh();
1493a56bb8a5SSatoshi Asami 		ccdunlock(cs);
1494a56bb8a5SSatoshi Asami 		splx(s);
1495a56bb8a5SSatoshi Asami 
1496a56bb8a5SSatoshi Asami 		break;
1497a56bb8a5SSatoshi Asami 
149801706d20SPoul-Henning Kamp 	case CCDCONFINFO:
149901706d20SPoul-Henning Kamp 		{
150001706d20SPoul-Henning Kamp 			int ninit = 0;
150101706d20SPoul-Henning Kamp 			struct ccdconf *conf = (struct ccdconf *)data;
150201706d20SPoul-Henning Kamp 			struct ccd_s *tmpcs;
150301706d20SPoul-Henning Kamp 			struct ccd_s *ubuf = conf->buffer;
150401706d20SPoul-Henning Kamp 
150501706d20SPoul-Henning Kamp 			/* XXX: LOCK(unique unit numbers) */
150601706d20SPoul-Henning Kamp 			LIST_FOREACH(tmpcs, &ccd_softc_list, list)
150701706d20SPoul-Henning Kamp 				if (IS_INITED(tmpcs))
150801706d20SPoul-Henning Kamp 					ninit++;
150901706d20SPoul-Henning Kamp 
151001706d20SPoul-Henning Kamp 			if (conf->size == 0) {
151101706d20SPoul-Henning Kamp 				conf->size = sizeof(struct ccd_s) * ninit;
151201706d20SPoul-Henning Kamp 				break;
151301706d20SPoul-Henning Kamp 			} else if ((conf->size / sizeof(struct ccd_s) != ninit) ||
151401706d20SPoul-Henning Kamp 			    (conf->size % sizeof(struct ccd_s) != 0)) {
151501706d20SPoul-Henning Kamp 				/* XXX: UNLOCK(unique unit numbers) */
151601706d20SPoul-Henning Kamp 				return (EINVAL);
151701706d20SPoul-Henning Kamp 			}
151801706d20SPoul-Henning Kamp 
151901706d20SPoul-Henning Kamp 			ubuf += ninit;
152001706d20SPoul-Henning Kamp 			LIST_FOREACH(tmpcs, &ccd_softc_list, list) {
152101706d20SPoul-Henning Kamp 				if (!IS_INITED(tmpcs))
152201706d20SPoul-Henning Kamp 					continue;
152301706d20SPoul-Henning Kamp 				error = copyout(tmpcs, --ubuf,
152401706d20SPoul-Henning Kamp 				    sizeof(struct ccd_s));
152501706d20SPoul-Henning Kamp 				if (error != 0)
152601706d20SPoul-Henning Kamp 					/* XXX: UNLOCK(unique unit numbers) */
152701706d20SPoul-Henning Kamp 					return (error);
152801706d20SPoul-Henning Kamp 			}
152901706d20SPoul-Henning Kamp 			/* XXX: UNLOCK(unique unit numbers) */
153001706d20SPoul-Henning Kamp 		}
153101706d20SPoul-Henning Kamp 		break;
153201706d20SPoul-Henning Kamp 
153301706d20SPoul-Henning Kamp 	case CCDCPPINFO:
153401706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
153501706d20SPoul-Henning Kamp 			return (ENXIO);
153601706d20SPoul-Henning Kamp 
153701706d20SPoul-Henning Kamp 		{
153801706d20SPoul-Henning Kamp 			int len = 0;
153901706d20SPoul-Henning Kamp 			struct ccdcpps *cpps = (struct ccdcpps *)data;
154001706d20SPoul-Henning Kamp 			char *ubuf = cpps->buffer;
154101706d20SPoul-Henning Kamp 
154201706d20SPoul-Henning Kamp 
154301706d20SPoul-Henning Kamp 			for (i = 0; i < cs->sc_nccdisks; ++i)
154401706d20SPoul-Henning Kamp 				len += cs->sc_cinfo[i].ci_pathlen;
154501706d20SPoul-Henning Kamp 
154601706d20SPoul-Henning Kamp 			if (cpps->size == 0) {
154701706d20SPoul-Henning Kamp 				cpps->size = len;
154801706d20SPoul-Henning Kamp 				break;
1549ddbf51afSPoul-Henning Kamp 			} else if (cpps->size < len) {
1550ddbf51afSPoul-Henning Kamp 				return (ENOMEM);
155101706d20SPoul-Henning Kamp 			}
155201706d20SPoul-Henning Kamp 
155301706d20SPoul-Henning Kamp 			for (i = 0; i < cs->sc_nccdisks; ++i) {
155401706d20SPoul-Henning Kamp 				len = cs->sc_cinfo[i].ci_pathlen;
155501706d20SPoul-Henning Kamp 				error = copyout(cs->sc_cinfo[i].ci_path, ubuf,
155601706d20SPoul-Henning Kamp 				    len);
155701706d20SPoul-Henning Kamp 				if (error != 0)
155801706d20SPoul-Henning Kamp 					return (error);
155901706d20SPoul-Henning Kamp 				ubuf += len;
156001706d20SPoul-Henning Kamp 			}
1561ddbf51afSPoul-Henning Kamp 			return(copyout("", ubuf, 1));
156201706d20SPoul-Henning Kamp 		}
156301706d20SPoul-Henning Kamp 		break;
156401706d20SPoul-Henning Kamp 
1565a56bb8a5SSatoshi Asami 	case DIOCGDINFO:
156601706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
1567a56bb8a5SSatoshi Asami 			return (ENXIO);
1568a56bb8a5SSatoshi Asami 
15696cc5a722SPoul-Henning Kamp 		*(struct disklabel *)data = cs->sc_label;
1570a56bb8a5SSatoshi Asami 		break;
1571a56bb8a5SSatoshi Asami 
1572a56bb8a5SSatoshi Asami 	case DIOCWDINFO:
1573a56bb8a5SSatoshi Asami 	case DIOCSDINFO:
157401706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
1575a56bb8a5SSatoshi Asami 			return (ENXIO);
1576a56bb8a5SSatoshi Asami 
1577a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1578a56bb8a5SSatoshi Asami 			return (EBADF);
1579a56bb8a5SSatoshi Asami 
1580b4e36adfSMatthew Dillon 		if ((error = ccdlock(cs)) != 0)
1581a56bb8a5SSatoshi Asami 			return (error);
1582a56bb8a5SSatoshi Asami 
1583a56bb8a5SSatoshi Asami 		cs->sc_flags |= CCDF_LABELLING;
1584a56bb8a5SSatoshi Asami 
15856cc5a722SPoul-Henning Kamp 		error = setdisklabel(&cs->sc_label,
1586d8594dfbSSatoshi Asami 		    (struct disklabel *)data, 0);
1587a56bb8a5SSatoshi Asami 		if (error == 0) {
1588a56bb8a5SSatoshi Asami 			if (cmd == DIOCWDINFO)
1589e2a13e8cSSatoshi Asami 				error = writedisklabel(CCDLABELDEV(dev),
159049ff4debSPoul-Henning Kamp 				    &cs->sc_label);
1591a56bb8a5SSatoshi Asami 		}
1592a56bb8a5SSatoshi Asami 
1593a56bb8a5SSatoshi Asami 		cs->sc_flags &= ~CCDF_LABELLING;
1594a56bb8a5SSatoshi Asami 
1595a56bb8a5SSatoshi Asami 		ccdunlock(cs);
1596a56bb8a5SSatoshi Asami 
1597a56bb8a5SSatoshi Asami 		if (error)
1598a56bb8a5SSatoshi Asami 			return (error);
1599a56bb8a5SSatoshi Asami 		break;
1600a56bb8a5SSatoshi Asami 
1601a56bb8a5SSatoshi Asami 	case DIOCWLABEL:
160201706d20SPoul-Henning Kamp 		if (!IS_INITED(cs))
1603a56bb8a5SSatoshi Asami 			return (ENXIO);
1604a56bb8a5SSatoshi Asami 
1605a56bb8a5SSatoshi Asami 		if ((flag & FWRITE) == 0)
1606a56bb8a5SSatoshi Asami 			return (EBADF);
1607a56bb8a5SSatoshi Asami 		if (*(int *)data != 0)
1608a56bb8a5SSatoshi Asami 			cs->sc_flags |= CCDF_WLABEL;
1609a56bb8a5SSatoshi Asami 		else
1610a56bb8a5SSatoshi Asami 			cs->sc_flags &= ~CCDF_WLABEL;
1611a56bb8a5SSatoshi Asami 		break;
1612a56bb8a5SSatoshi Asami 
1613a56bb8a5SSatoshi Asami 	default:
1614a56bb8a5SSatoshi Asami 		return (ENOTTY);
1615a56bb8a5SSatoshi Asami 	}
1616a56bb8a5SSatoshi Asami 
1617a56bb8a5SSatoshi Asami 	return (0);
1618a56bb8a5SSatoshi Asami }
1619a56bb8a5SSatoshi Asami 
1620e2738b4fSPoul-Henning Kamp static int
162101706d20SPoul-Henning Kamp ccdsize(dev_t dev)
1622a56bb8a5SSatoshi Asami {
162301706d20SPoul-Henning Kamp 	struct ccd_s *cs;
1624a56bb8a5SSatoshi Asami 	int part, size;
1625a56bb8a5SSatoshi Asami 
1626ddbf51afSPoul-Henning Kamp 	if (dev->si_drv1 == dev)
1627ddbf51afSPoul-Henning Kamp 		return (-1);
1628ddbf51afSPoul-Henning Kamp 
1629b40ce416SJulian Elischer 	if (ccdopen(dev, 0, S_IFCHR, curthread))
1630a56bb8a5SSatoshi Asami 		return (-1);
1631a56bb8a5SSatoshi Asami 
163201706d20SPoul-Henning Kamp 	cs = ccdfind(ccdunit(dev));
1633d8594dfbSSatoshi Asami 	part = ccdpart(dev);
1634a56bb8a5SSatoshi Asami 
163501706d20SPoul-Henning Kamp 	if (!IS_INITED(cs))
1636a56bb8a5SSatoshi Asami 		return (-1);
1637a56bb8a5SSatoshi Asami 
16386cc5a722SPoul-Henning Kamp 	if (cs->sc_label.d_partitions[part].p_fstype != FS_SWAP)
1639a56bb8a5SSatoshi Asami 		size = -1;
1640a56bb8a5SSatoshi Asami 	else
16416cc5a722SPoul-Henning Kamp 		size = cs->sc_label.d_partitions[part].p_size;
1642a56bb8a5SSatoshi Asami 
1643b40ce416SJulian Elischer 	if (ccdclose(dev, 0, S_IFCHR, curthread))
1644a56bb8a5SSatoshi Asami 		return (-1);
1645a56bb8a5SSatoshi Asami 
1646a56bb8a5SSatoshi Asami 	return (size);
1647a56bb8a5SSatoshi Asami }
1648a56bb8a5SSatoshi Asami 
1649a56bb8a5SSatoshi Asami /*
1650a56bb8a5SSatoshi Asami  * Lookup the provided name in the filesystem.  If the file exists,
1651a56bb8a5SSatoshi Asami  * is a valid block device, and isn't being used by anyone else,
1652a56bb8a5SSatoshi Asami  * set *vpp to the file's vnode.
1653a56bb8a5SSatoshi Asami  */
1654a56bb8a5SSatoshi Asami static int
1655b40ce416SJulian Elischer ccdlookup(char *path, struct thread *td, struct vnode **vpp)
1656a56bb8a5SSatoshi Asami {
1657a56bb8a5SSatoshi Asami 	struct nameidata nd;
1658a56bb8a5SSatoshi Asami 	struct vnode *vp;
1659e6796b67SKirk McKusick 	int error, flags;
1660a56bb8a5SSatoshi Asami 
1661b40ce416SJulian Elischer 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, td);
1662e6796b67SKirk McKusick 	flags = FREAD | FWRITE;
1663e6796b67SKirk McKusick 	if ((error = vn_open(&nd, &flags, 0)) != 0) {
1664a56bb8a5SSatoshi Asami #ifdef DEBUG
166501706d20SPoul-Henning Kamp 		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
1666a56bb8a5SSatoshi Asami 			printf("ccdlookup: vn_open error = %d\n", error);
1667a56bb8a5SSatoshi Asami #endif
1668a56bb8a5SSatoshi Asami 		return (error);
1669a56bb8a5SSatoshi Asami 	}
1670a56bb8a5SSatoshi Asami 	vp = nd.ni_vp;
1671a56bb8a5SSatoshi Asami 
167237ab0e0dSJeff Roberson 	if (vrefcnt(vp) > 1) {
1673762e6b85SEivind Eklund 		error = EBUSY;
1674762e6b85SEivind Eklund 		goto bad;
1675a56bb8a5SSatoshi Asami 	}
1676a56bb8a5SSatoshi Asami 
1677ba4ad1fcSPoul-Henning Kamp 	if (!vn_isdisk(vp, &error))
1678762e6b85SEivind Eklund 		goto bad;
1679a56bb8a5SSatoshi Asami 
1680a56bb8a5SSatoshi Asami #ifdef DEBUG
1681a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_VNODE)
1682a56bb8a5SSatoshi Asami 		vprint("ccdlookup: vnode info", vp);
1683a56bb8a5SSatoshi Asami #endif
1684a56bb8a5SSatoshi Asami 
1685b40ce416SJulian Elischer 	VOP_UNLOCK(vp, 0, td);
1686762e6b85SEivind Eklund 	NDFREE(&nd, NDF_ONLY_PNBUF);
1687a56bb8a5SSatoshi Asami 	*vpp = vp;
1688a56bb8a5SSatoshi Asami 	return (0);
1689762e6b85SEivind Eklund bad:
1690b40ce416SJulian Elischer 	VOP_UNLOCK(vp, 0, td);
1691762e6b85SEivind Eklund 	NDFREE(&nd, NDF_ONLY_PNBUF);
1692762e6b85SEivind Eklund 	/* vn_close does vrele() for vp */
1693a854ed98SJohn Baldwin 	(void)vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
1694762e6b85SEivind Eklund 	return (error);
1695a56bb8a5SSatoshi Asami }
1696a56bb8a5SSatoshi Asami 
1697a56bb8a5SSatoshi Asami /*
1698a56bb8a5SSatoshi Asami  * Read the disklabel from the ccd.  If one is not present, fake one
1699a56bb8a5SSatoshi Asami  * up.
1700a56bb8a5SSatoshi Asami  */
1701a56bb8a5SSatoshi Asami static void
170201706d20SPoul-Henning Kamp ccdgetdisklabel(dev_t dev)
1703a56bb8a5SSatoshi Asami {
1704a56bb8a5SSatoshi Asami 	int unit = ccdunit(dev);
170501706d20SPoul-Henning Kamp 	struct ccd_s *cs = ccdfind(unit);
1706a56bb8a5SSatoshi Asami 	char *errstring;
17076cc5a722SPoul-Henning Kamp 	struct disklabel *lp = &cs->sc_label;
1708a56bb8a5SSatoshi Asami 	struct ccdgeom *ccg = &cs->sc_geom;
1709a56bb8a5SSatoshi Asami 
1710a56bb8a5SSatoshi Asami 	bzero(lp, sizeof(*lp));
1711a56bb8a5SSatoshi Asami 
1712a56bb8a5SSatoshi Asami 	lp->d_secperunit = cs->sc_size;
1713a56bb8a5SSatoshi Asami 	lp->d_secsize = ccg->ccg_secsize;
1714a56bb8a5SSatoshi Asami 	lp->d_nsectors = ccg->ccg_nsectors;
1715a56bb8a5SSatoshi Asami 	lp->d_ntracks = ccg->ccg_ntracks;
1716a56bb8a5SSatoshi Asami 	lp->d_ncylinders = ccg->ccg_ncylinders;
1717a56bb8a5SSatoshi Asami 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1718a56bb8a5SSatoshi Asami 
1719a56bb8a5SSatoshi Asami 	strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
1720a56bb8a5SSatoshi Asami 	lp->d_type = DTYPE_CCD;
1721a56bb8a5SSatoshi Asami 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1722a56bb8a5SSatoshi Asami 	lp->d_rpm = 3600;
1723a56bb8a5SSatoshi Asami 	lp->d_interleave = 1;
1724a56bb8a5SSatoshi Asami 	lp->d_flags = 0;
1725a56bb8a5SSatoshi Asami 
1726a56bb8a5SSatoshi Asami 	lp->d_partitions[RAW_PART].p_offset = 0;
1727a56bb8a5SSatoshi Asami 	lp->d_partitions[RAW_PART].p_size = cs->sc_size;
1728a56bb8a5SSatoshi Asami 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1729a56bb8a5SSatoshi Asami 	lp->d_npartitions = RAW_PART + 1;
1730a56bb8a5SSatoshi Asami 
1731d8594dfbSSatoshi Asami 	lp->d_bbsize = BBSIZE;				/* XXX */
173277068a7fSPoul-Henning Kamp 	lp->d_sbsize = 0;
1733d8594dfbSSatoshi Asami 
1734a56bb8a5SSatoshi Asami 	lp->d_magic = DISKMAGIC;
1735a56bb8a5SSatoshi Asami 	lp->d_magic2 = DISKMAGIC;
17366cc5a722SPoul-Henning Kamp 	lp->d_checksum = dkcksum(&cs->sc_label);
1737a56bb8a5SSatoshi Asami 
1738a56bb8a5SSatoshi Asami 	/*
1739a56bb8a5SSatoshi Asami 	 * Call the generic disklabel extraction routine.
1740a56bb8a5SSatoshi Asami 	 */
174149ff4debSPoul-Henning Kamp 	errstring = readdisklabel(CCDLABELDEV(dev), &cs->sc_label);
174249ff4debSPoul-Henning Kamp 	if (errstring != NULL)
1743a56bb8a5SSatoshi Asami 		ccdmakedisklabel(cs);
1744a56bb8a5SSatoshi Asami 
1745a56bb8a5SSatoshi Asami #ifdef DEBUG
1746a56bb8a5SSatoshi Asami 	/* It's actually extremely common to have unlabeled ccds. */
1747a56bb8a5SSatoshi Asami 	if (ccddebug & CCDB_LABEL)
1748a56bb8a5SSatoshi Asami 		if (errstring != NULL)
1749a56bb8a5SSatoshi Asami 			printf("ccd%d: %s\n", unit, errstring);
1750a56bb8a5SSatoshi Asami #endif
1751a56bb8a5SSatoshi Asami }
1752a56bb8a5SSatoshi Asami 
1753a56bb8a5SSatoshi Asami /*
1754a56bb8a5SSatoshi Asami  * Take care of things one might want to take care of in the event
1755a56bb8a5SSatoshi Asami  * that a disklabel isn't present.
1756a56bb8a5SSatoshi Asami  */
1757a56bb8a5SSatoshi Asami static void
175801706d20SPoul-Henning Kamp ccdmakedisklabel(struct ccd_s *cs)
1759a56bb8a5SSatoshi Asami {
17606cc5a722SPoul-Henning Kamp 	struct disklabel *lp = &cs->sc_label;
1761a56bb8a5SSatoshi Asami 
1762a56bb8a5SSatoshi Asami 	/*
1763a56bb8a5SSatoshi Asami 	 * For historical reasons, if there's no disklabel present
1764a56bb8a5SSatoshi Asami 	 * the raw partition must be marked FS_BSDFFS.
1765a56bb8a5SSatoshi Asami 	 */
1766a56bb8a5SSatoshi Asami 	lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
1767a56bb8a5SSatoshi Asami 
1768a56bb8a5SSatoshi Asami 	strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1769a56bb8a5SSatoshi Asami }
1770a56bb8a5SSatoshi Asami 
1771a56bb8a5SSatoshi Asami /*
1772a56bb8a5SSatoshi Asami  * Wait interruptibly for an exclusive lock.
1773a56bb8a5SSatoshi Asami  *
1774a56bb8a5SSatoshi Asami  * XXX
1775a56bb8a5SSatoshi Asami  * Several drivers do this; it should be abstracted and made MP-safe.
1776a56bb8a5SSatoshi Asami  */
1777a56bb8a5SSatoshi Asami static int
177801706d20SPoul-Henning Kamp ccdlock(struct ccd_s *cs)
1779a56bb8a5SSatoshi Asami {
1780a56bb8a5SSatoshi Asami 	int error;
1781a56bb8a5SSatoshi Asami 
1782a56bb8a5SSatoshi Asami 	while ((cs->sc_flags & CCDF_LOCKED) != 0) {
1783a56bb8a5SSatoshi Asami 		cs->sc_flags |= CCDF_WANTED;
1784a56bb8a5SSatoshi Asami 		if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0)
1785a56bb8a5SSatoshi Asami 			return (error);
1786a56bb8a5SSatoshi Asami 	}
1787a56bb8a5SSatoshi Asami 	cs->sc_flags |= CCDF_LOCKED;
1788a56bb8a5SSatoshi Asami 	return (0);
1789a56bb8a5SSatoshi Asami }
1790a56bb8a5SSatoshi Asami 
1791a56bb8a5SSatoshi Asami /*
1792a56bb8a5SSatoshi Asami  * Unlock and wake up any waiters.
1793a56bb8a5SSatoshi Asami  */
1794a56bb8a5SSatoshi Asami static void
179501706d20SPoul-Henning Kamp ccdunlock(struct ccd_s *cs)
1796a56bb8a5SSatoshi Asami {
1797a56bb8a5SSatoshi Asami 
1798a56bb8a5SSatoshi Asami 	cs->sc_flags &= ~CCDF_LOCKED;
1799a56bb8a5SSatoshi Asami 	if ((cs->sc_flags & CCDF_WANTED) != 0) {
1800a56bb8a5SSatoshi Asami 		cs->sc_flags &= ~CCDF_WANTED;
1801a56bb8a5SSatoshi Asami 		wakeup(cs);
1802a56bb8a5SSatoshi Asami 	}
1803a56bb8a5SSatoshi Asami }
1804a56bb8a5SSatoshi Asami 
1805a56bb8a5SSatoshi Asami #ifdef DEBUG
1806a56bb8a5SSatoshi Asami static void
180701706d20SPoul-Henning Kamp printiinfo(struct ccdiinfo *ii)
1808a56bb8a5SSatoshi Asami {
18091464240eSMatthew Dillon 	int ix, i;
1810a56bb8a5SSatoshi Asami 
1811a56bb8a5SSatoshi Asami 	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
1812ff8cc2ebSBruce Evans 		printf(" itab[%d]: #dk %d sblk %lld soff %lld",
1813ff8cc2ebSBruce Evans 		    ix, ii->ii_ndisk, (long long)ii->ii_startblk,
1814ff8cc2ebSBruce Evans 		    (long long)ii->ii_startoff);
1815a56bb8a5SSatoshi Asami 		for (i = 0; i < ii->ii_ndisk; i++)
1816a56bb8a5SSatoshi Asami 			printf(" %d", ii->ii_index[i]);
1817a56bb8a5SSatoshi Asami 		printf("\n");
1818a56bb8a5SSatoshi Asami 	}
1819a56bb8a5SSatoshi Asami }
1820a56bb8a5SSatoshi Asami #endif
1821