xref: /netbsd-src/sys/dev/ccd.c (revision 49e3024a8832ef572aa35f438a80e0dbcc89377c)
1*49e3024aSmlelstv /*	$NetBSD: ccd.c,v 1.154 2014/10/11 12:36:25 mlelstv Exp $	*/
277d85de2Sthorpej 
3ee8a2c00Sthorpej /*-
4481ff5acSad  * Copyright (c) 1996, 1997, 1998, 1999, 2007, 2009 The NetBSD Foundation, Inc.
577d85de2Sthorpej  * All rights reserved.
677d85de2Sthorpej  *
7ee8a2c00Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8481ff5acSad  * by Jason R. Thorpe, and by Andrew Doran.
9ee8a2c00Sthorpej  *
1077d85de2Sthorpej  * Redistribution and use in source and binary forms, with or without
1177d85de2Sthorpej  * modification, are permitted provided that the following conditions
1277d85de2Sthorpej  * are met:
1377d85de2Sthorpej  * 1. Redistributions of source code must retain the above copyright
1477d85de2Sthorpej  *    notice, this list of conditions and the following disclaimer.
1577d85de2Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
1677d85de2Sthorpej  *    notice, this list of conditions and the following disclaimer in the
1777d85de2Sthorpej  *    documentation and/or other materials provided with the distribution.
1877d85de2Sthorpej  *
19ee8a2c00Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20ee8a2c00Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21ee8a2c00Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d6ae2cd7Sjtc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d6ae2cd7Sjtc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24ee8a2c00Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25ee8a2c00Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26ee8a2c00Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27ee8a2c00Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28ee8a2c00Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29ee8a2c00Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
3077d85de2Sthorpej  */
31cf92afd6Scgd 
326f9db1eeShpeyerl /*
339b6bd2d9Srmind  * Copyright (c) 1988 University of Utah.
343214723fShpeyerl  * Copyright (c) 1990, 1993
353214723fShpeyerl  *	The Regents of the University of California.  All rights reserved.
366f9db1eeShpeyerl  *
376f9db1eeShpeyerl  * This code is derived from software contributed to Berkeley by
386f9db1eeShpeyerl  * the Systems Programming Group of the University of Utah Computer
396f9db1eeShpeyerl  * Science Department.
406f9db1eeShpeyerl  *
416f9db1eeShpeyerl  * Redistribution and use in source and binary forms, with or without
426f9db1eeShpeyerl  * modification, are permitted provided that the following conditions
436f9db1eeShpeyerl  * are met:
446f9db1eeShpeyerl  * 1. Redistributions of source code must retain the above copyright
456f9db1eeShpeyerl  *    notice, this list of conditions and the following disclaimer.
466f9db1eeShpeyerl  * 2. Redistributions in binary form must reproduce the above copyright
476f9db1eeShpeyerl  *    notice, this list of conditions and the following disclaimer in the
486f9db1eeShpeyerl  *    documentation and/or other materials provided with the distribution.
49aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
50aad01611Sagc  *    may be used to endorse or promote products derived from this software
51aad01611Sagc  *    without specific prior written permission.
52aad01611Sagc  *
53aad01611Sagc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54aad01611Sagc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55aad01611Sagc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56aad01611Sagc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57aad01611Sagc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58aad01611Sagc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59aad01611Sagc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60aad01611Sagc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61aad01611Sagc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62aad01611Sagc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63aad01611Sagc  * SUCH DAMAGE.
64aad01611Sagc  *
65aad01611Sagc  * from: Utah $Hdr: cd.c 1.6 90/11/28$
66aad01611Sagc  *
67aad01611Sagc  *	@(#)cd.c	8.2 (Berkeley) 11/16/93
68aad01611Sagc  */
69aad01611Sagc 
70aad01611Sagc /*
716f9db1eeShpeyerl  * "Concatenated" disk driver.
7277d85de2Sthorpej  *
73481ff5acSad  * Notes on concurrency:
74481ff5acSad  *
75481ff5acSad  * => sc_dvlock serializes access to the device nodes, excluding block I/O.
76481ff5acSad  *
77481ff5acSad  * => sc_iolock serializes access to (sc_flags & CCDF_INITED), disk stats,
78481ff5acSad  *    sc_stop, sc_bufq and b_resid from master buffers.
79481ff5acSad  *
80481ff5acSad  * => a combination of CCDF_INITED, sc_inflight, and sc_iolock is used to
81481ff5acSad  *    serialize I/O and configuration changes.
82481ff5acSad  *
83481ff5acSad  * => the in-core disk label does not change while the device is open.
84481ff5acSad  *
85481ff5acSad  * On memory consumption: ccd fans out I/O requests and so needs to
86481ff5acSad  * allocate memory.  If the system is desperately low on memory, we
87481ff5acSad  * single thread I/O.
886f9db1eeShpeyerl  */
896f9db1eeShpeyerl 
902bbe2de6Slukem #include <sys/cdefs.h>
91*49e3024aSmlelstv __KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.154 2014/10/11 12:36:25 mlelstv Exp $");
927d706cf7Ssborrill 
937d706cf7Ssborrill #if defined(_KERNEL_OPT)
947d706cf7Ssborrill #include "opt_compat_netbsd.h"
957d706cf7Ssborrill #endif
962bbe2de6Slukem 
976f9db1eeShpeyerl #include <sys/param.h>
986f9db1eeShpeyerl #include <sys/systm.h>
99481ff5acSad #include <sys/kernel.h>
1003214723fShpeyerl #include <sys/proc.h>
1016f9db1eeShpeyerl #include <sys/errno.h>
1026f9db1eeShpeyerl #include <sys/buf.h>
103481ff5acSad #include <sys/kmem.h>
104dd5ae858Sthorpej #include <sys/pool.h>
105b4a3a8f3Sjruoho #include <sys/module.h>
10677d85de2Sthorpej #include <sys/namei.h>
1073214723fShpeyerl #include <sys/stat.h>
1083214723fShpeyerl #include <sys/ioctl.h>
1093214723fShpeyerl #include <sys/disklabel.h>
11077d85de2Sthorpej #include <sys/device.h>
11177d85de2Sthorpej #include <sys/disk.h>
11277d85de2Sthorpej #include <sys/syslog.h>
1133214723fShpeyerl #include <sys/fcntl.h>
11477d85de2Sthorpej #include <sys/vnode.h>
115e1930da2Schristos #include <sys/conf.h>
1169abeea58Sad #include <sys/mutex.h>
117d272bb00Sthorpej #include <sys/queue.h>
1182867b68bSelad #include <sys/kauth.h>
119481ff5acSad #include <sys/kthread.h>
120481ff5acSad #include <sys/bufq.h>
121b08b65c6Schristos #include <sys/sysctl.h>
1226f9db1eeShpeyerl 
12319b39d64Suebayasi #include <uvm/uvm_extern.h>
12419b39d64Suebayasi 
1256f9db1eeShpeyerl #include <dev/ccdvar.h>
1261a789da3Schristos #include <dev/dkvar.h>
1276f9db1eeShpeyerl 
128292a0c7fShannken #include <miscfs/specfs/specdev.h> /* for v_rdev */
129292a0c7fShannken 
13077d85de2Sthorpej #if defined(CCDDEBUG) && !defined(DEBUG)
13177d85de2Sthorpej #define DEBUG
13277d85de2Sthorpej #endif
13377d85de2Sthorpej 
1346f9db1eeShpeyerl #ifdef DEBUG
1353214723fShpeyerl #define CCDB_FOLLOW	0x01
1363214723fShpeyerl #define CCDB_INIT	0x02
1373214723fShpeyerl #define CCDB_IO		0x04
13877d85de2Sthorpej #define CCDB_LABEL	0x08
13977d85de2Sthorpej #define CCDB_VNODE	0x10
140732dd94aSthorpej int ccddebug = 0x00;
1416f9db1eeShpeyerl #endif
1426f9db1eeShpeyerl 
143f694af67Scgd #define	ccdunit(x)	DISKUNIT(x)
1446f9db1eeShpeyerl 
145f694af67Scgd struct ccdbuf {
146f694af67Scgd 	struct buf	cb_buf;		/* new I/O buf */
147f694af67Scgd 	struct buf	*cb_obp;	/* ptr. to original I/O buf */
148c6aa25bfSthorpej 	struct ccd_softc *cb_sc;	/* pointer to ccd softc */
149f694af67Scgd 	int		cb_comp;	/* target component */
150d272bb00Sthorpej 	SIMPLEQ_ENTRY(ccdbuf) cb_q;	/* fifo of component buffers */
151f694af67Scgd };
1526f9db1eeShpeyerl 
153dd5ae858Sthorpej /* component buffer pool */
154481ff5acSad static pool_cache_t ccd_cache;
155dd5ae858Sthorpej 
156481ff5acSad #define	CCD_GETBUF()		pool_cache_get(ccd_cache, PR_WAITOK)
157481ff5acSad #define	CCD_PUTBUF(cbp)		pool_cache_put(ccd_cache, cbp)
1586f9db1eeShpeyerl 
15977d85de2Sthorpej #define CCDLABELDEV(dev)	\
16077d85de2Sthorpej 	(MAKEDISKDEV(major((dev)), ccdunit((dev)), RAW_PART))
1616f9db1eeShpeyerl 
16277d85de2Sthorpej /* called by main() at boot time */
163b8d38500Sthorpej void	ccdattach(int);
16477d85de2Sthorpej 
16577d85de2Sthorpej /* called by biodone() at interrupt time */
166b8d38500Sthorpej static void	ccdiodone(struct buf *);
167f694af67Scgd 
168b8d38500Sthorpej static void	ccdinterleave(struct ccd_softc *);
169b8d38500Sthorpej static int	ccdinit(struct ccd_softc *, char **, struct vnode **,
17095e1ffb1Schristos 		    struct lwp *);
171b8d38500Sthorpej static struct ccdbuf *ccdbuffer(struct ccd_softc *, struct buf *,
17253524e44Schristos 		    daddr_t, void *, long);
173b8d38500Sthorpej static void	ccdgetdefaultlabel(struct ccd_softc *, struct disklabel *);
174b8d38500Sthorpej static void	ccdgetdisklabel(dev_t);
175b8d38500Sthorpej static void	ccdmakedisklabel(struct ccd_softc *);
176481ff5acSad static void	ccdstart(struct ccd_softc *);
177481ff5acSad static void	ccdthread(void *);
1783214723fShpeyerl 
179b8d38500Sthorpej static dev_type_open(ccdopen);
180b8d38500Sthorpej static dev_type_close(ccdclose);
181b8d38500Sthorpej static dev_type_read(ccdread);
182b8d38500Sthorpej static dev_type_write(ccdwrite);
183b8d38500Sthorpej static dev_type_ioctl(ccdioctl);
184b8d38500Sthorpej static dev_type_strategy(ccdstrategy);
185b8d38500Sthorpej static dev_type_size(ccdsize);
18677a6b82bSgehenna 
18777a6b82bSgehenna const struct bdevsw ccd_bdevsw = {
188481ff5acSad 	.d_open = ccdopen,
189481ff5acSad 	.d_close = ccdclose,
190481ff5acSad 	.d_strategy = ccdstrategy,
191481ff5acSad 	.d_ioctl = ccdioctl,
192481ff5acSad 	.d_dump = nodump,
193481ff5acSad 	.d_psize = ccdsize,
1948c70ef39Sdholland 	.d_discard = nodiscard,
195481ff5acSad 	.d_flag = D_DISK | D_MPSAFE
19677a6b82bSgehenna };
19777a6b82bSgehenna 
19877a6b82bSgehenna const struct cdevsw ccd_cdevsw = {
199481ff5acSad 	.d_open = ccdopen,
200481ff5acSad 	.d_close = ccdclose,
201481ff5acSad 	.d_read = ccdread,
202481ff5acSad 	.d_write = ccdwrite,
203481ff5acSad 	.d_ioctl = ccdioctl,
204481ff5acSad 	.d_stop = nostop,
205481ff5acSad 	.d_tty = notty,
206481ff5acSad 	.d_poll = nopoll,
207481ff5acSad 	.d_mmap = nommap,
208481ff5acSad 	.d_kqfilter = nokqfilter,
209f9228f42Sdholland 	.d_discard = nodiscard,
210481ff5acSad 	.d_flag = D_DISK | D_MPSAFE
21177a6b82bSgehenna };
21277a6b82bSgehenna 
21377d85de2Sthorpej #ifdef DEBUG
214b8d38500Sthorpej static	void printiinfo(struct ccdiinfo *);
21577d85de2Sthorpej #endif
21677d85de2Sthorpej 
217b08b65c6Schristos static LIST_HEAD(, ccd_softc) ccds = LIST_HEAD_INITIALIZER(ccds);
218b08b65c6Schristos static kmutex_t ccd_lock;
219b08b65c6Schristos 
220b08b65c6Schristos static struct ccd_softc *
221b08b65c6Schristos ccdcreate(int unit) {
222b08b65c6Schristos 	struct ccd_softc *sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
223b08b65c6Schristos 	if (sc == NULL) {
224b08b65c6Schristos #ifdef DIAGNOSTIC
225b08b65c6Schristos 		printf("%s: out of memory\n", __func__);
226b08b65c6Schristos #endif
227b08b65c6Schristos 		return NULL;
228b08b65c6Schristos 	}
229b08b65c6Schristos 	/* Initialize per-softc structures. */
230b08b65c6Schristos 	snprintf(sc->sc_xname, sizeof(sc->sc_xname), "ccd%d", unit);
231b08b65c6Schristos 	mutex_init(&sc->sc_dvlock, MUTEX_DEFAULT, IPL_NONE);
232b08b65c6Schristos 	sc->sc_iolock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
233b08b65c6Schristos 	cv_init(&sc->sc_stop, "ccdstop");
234b08b65c6Schristos 	cv_init(&sc->sc_push, "ccdthr");
235b08b65c6Schristos 	disk_init(&sc->sc_dkdev, sc->sc_xname, NULL); /* XXX */
236b08b65c6Schristos 	return sc;
237b08b65c6Schristos }
238b08b65c6Schristos 
239b08b65c6Schristos static void
240b08b65c6Schristos ccddestroy(struct ccd_softc *sc) {
241b08b65c6Schristos 	mutex_obj_free(sc->sc_iolock);
24255252202Sjoerg 	mutex_exit(&sc->sc_dvlock);
243b08b65c6Schristos 	mutex_destroy(&sc->sc_dvlock);
244b08b65c6Schristos 	cv_destroy(&sc->sc_stop);
245b08b65c6Schristos 	cv_destroy(&sc->sc_push);
246b08b65c6Schristos 	disk_destroy(&sc->sc_dkdev);
247b08b65c6Schristos 	kmem_free(sc, sizeof(*sc));
248b08b65c6Schristos }
249b08b65c6Schristos 
250b08b65c6Schristos static struct ccd_softc *
251b08b65c6Schristos ccdget(int unit) {
252b08b65c6Schristos 	struct ccd_softc *sc;
253b08b65c6Schristos 	if (unit < 0) {
254b08b65c6Schristos #ifdef DIAGNOSTIC
255b08b65c6Schristos 		panic("%s: unit %d!", __func__, unit);
256b08b65c6Schristos #endif
257b08b65c6Schristos 		return NULL;
258b08b65c6Schristos 	}
259b08b65c6Schristos 	mutex_enter(&ccd_lock);
260b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link) {
261b08b65c6Schristos 		if (sc->sc_unit == unit) {
262b08b65c6Schristos 			mutex_exit(&ccd_lock);
263b08b65c6Schristos 			return sc;
264b08b65c6Schristos 		}
265b08b65c6Schristos 	}
266b08b65c6Schristos 	mutex_exit(&ccd_lock);
267b08b65c6Schristos 	if ((sc = ccdcreate(unit)) == NULL)
268b08b65c6Schristos 		return NULL;
269b08b65c6Schristos 	mutex_enter(&ccd_lock);
270b08b65c6Schristos 	LIST_INSERT_HEAD(&ccds, sc, sc_link);
271b08b65c6Schristos 	mutex_exit(&ccd_lock);
272b08b65c6Schristos 	return sc;
273b08b65c6Schristos }
274b08b65c6Schristos 
275b08b65c6Schristos static void
276b08b65c6Schristos ccdput(struct ccd_softc *sc) {
277b08b65c6Schristos 	mutex_enter(&ccd_lock);
278b08b65c6Schristos 	LIST_REMOVE(sc, sc_link);
279b08b65c6Schristos 	mutex_exit(&ccd_lock);
280b08b65c6Schristos 	ccddestroy(sc);
281b08b65c6Schristos }
2826f9db1eeShpeyerl 
2836f9db1eeShpeyerl /*
28477d85de2Sthorpej  * Called by main() during pseudo-device attachment.  All we need
28577d85de2Sthorpej  * to do is allocate enough space for devices to be configured later.
2866f9db1eeShpeyerl  */
2876f9db1eeShpeyerl void
288b8d38500Sthorpej ccdattach(int num)
2896f9db1eeShpeyerl {
290b08b65c6Schristos 	mutex_init(&ccd_lock, MUTEX_DEFAULT, IPL_NONE);
2910272b2abSthorpej 
292dd5ae858Sthorpej 	/* Initialize the component buffer pool. */
293481ff5acSad 	ccd_cache = pool_cache_init(sizeof(struct ccdbuf), 0,
294481ff5acSad 	    0, 0, "ccdbuf", NULL, IPL_BIO, NULL, NULL, NULL);
2956f9db1eeShpeyerl }
2966f9db1eeShpeyerl 
29777d85de2Sthorpej static int
298b8d38500Sthorpej ccdinit(struct ccd_softc *cs, char **cpaths, struct vnode **vpp,
29995e1ffb1Schristos     struct lwp *l)
3006f9db1eeShpeyerl {
301169ac5b3Saugustss 	struct ccdcinfo *ci = NULL;
302169ac5b3Saugustss 	int ix;
30377d85de2Sthorpej 	struct ccdgeom *ccg = &cs->sc_geom;
30492d441e0Schristos 	char *tmppath;
305aa8b5ebdSenami 	int error, path_alloced;
306e839ec30Schristos 	uint64_t psize, minsize;
307e839ec30Schristos 	unsigned secsize, maxsecsize;
3086f9db1eeShpeyerl 
3096f9db1eeShpeyerl #ifdef DEBUG
3103214723fShpeyerl 	if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
311c6aa25bfSthorpej 		printf("%s: ccdinit\n", cs->sc_xname);
3126f9db1eeShpeyerl #endif
31377d85de2Sthorpej 
31477d85de2Sthorpej 	/* Allocate space for the component info. */
315481ff5acSad 	cs->sc_cinfo = kmem_alloc(cs->sc_nccdisks * sizeof(*cs->sc_cinfo),
316481ff5acSad 	    KM_SLEEP);
317481ff5acSad 	tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
31892d441e0Schristos 
3190272b2abSthorpej 	cs->sc_size = 0;
3200272b2abSthorpej 
3216f9db1eeShpeyerl 	/*
3226f9db1eeShpeyerl 	 * Verify that each component piece exists and record
3236f9db1eeShpeyerl 	 * relevant information about it.
3246f9db1eeShpeyerl 	 */
32577d85de2Sthorpej 	maxsecsize = 0;
3266f9db1eeShpeyerl 	minsize = 0;
327aa8b5ebdSenami 	for (ix = 0, path_alloced = 0; ix < cs->sc_nccdisks; ix++) {
3286f9db1eeShpeyerl 		ci = &cs->sc_cinfo[ix];
3290272b2abSthorpej 		ci->ci_vp = vpp[ix];
330a44a2765Scgd 
33177d85de2Sthorpej 		/*
33277d85de2Sthorpej 		 * Copy in the pathname of the component.
33377d85de2Sthorpej 		 */
33425862469Sjoerg 		memset(tmppath, 0, MAXPATHLEN);	/* sanity */
335d318abdbSchristos 		error = copyinstr(cpaths[ix], tmppath,
336d318abdbSchristos 		    MAXPATHLEN, &ci->ci_pathlen);
337481ff5acSad 		if (ci->ci_pathlen == 0)
338481ff5acSad 			error = EINVAL;
339d318abdbSchristos 		if (error) {
34077d85de2Sthorpej #ifdef DEBUG
34177d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
34286373f8cSchristos 				printf("%s: can't copy path, error = %d\n",
3435b39541eSthorpej 				    cs->sc_xname, error);
34477d85de2Sthorpej #endif
345aa8b5ebdSenami 			goto out;
34677d85de2Sthorpej 		}
347481ff5acSad 		ci->ci_path = kmem_alloc(ci->ci_pathlen, KM_SLEEP);
348c8b4ac1bSthorpej 		memcpy(ci->ci_path, tmppath, ci->ci_pathlen);
349aa8b5ebdSenami 		path_alloced++;
35077d85de2Sthorpej 
35177d85de2Sthorpej 		/*
35277d85de2Sthorpej 		 * XXX: Cache the component's dev_t.
35377d85de2Sthorpej 		 */
354292a0c7fShannken 		ci->ci_dev = vpp[ix]->v_rdev;
35577d85de2Sthorpej 
35677d85de2Sthorpej 		/*
35777d85de2Sthorpej 		 * Get partition information for the component.
35877d85de2Sthorpej 		 */
359e839ec30Schristos 		error = getdisksize(vpp[ix], &psize, &secsize);
360d318abdbSchristos 		if (error) {
36177d85de2Sthorpej #ifdef DEBUG
36277d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
363e839ec30Schristos 				 printf("%s: %s: disksize failed, error = %d\n",
3645b39541eSthorpej 				     cs->sc_xname, ci->ci_path, error);
36577d85de2Sthorpej #endif
366aa8b5ebdSenami 			goto out;
36777d85de2Sthorpej 		}
36877d85de2Sthorpej 
36977d85de2Sthorpej 		/*
37077d85de2Sthorpej 		 * Calculate the size, truncating to an interleave
37177d85de2Sthorpej 		 * boundary if necessary.
37277d85de2Sthorpej 		 */
373e839ec30Schristos 		maxsecsize = secsize > maxsecsize ? secsize : maxsecsize;
3746f9db1eeShpeyerl 		if (cs->sc_ileave > 1)
375e839ec30Schristos 			psize -= psize % cs->sc_ileave;
3763214723fShpeyerl 
377e839ec30Schristos 		if (psize == 0) {
37877d85de2Sthorpej #ifdef DEBUG
37977d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
38086373f8cSchristos 				printf("%s: %s: size == 0\n",
3815b39541eSthorpej 				    cs->sc_xname, ci->ci_path);
3823214723fShpeyerl #endif
383aa8b5ebdSenami 			error = ENODEV;
384aa8b5ebdSenami 			goto out;
38577d85de2Sthorpej 		}
38677d85de2Sthorpej 
387e839ec30Schristos 		if (minsize == 0 || psize < minsize)
388e839ec30Schristos 			minsize = psize;
389e839ec30Schristos 		ci->ci_size = psize;
390e839ec30Schristos 		cs->sc_size += psize;
3916f9db1eeShpeyerl 	}
39277d85de2Sthorpej 
39377d85de2Sthorpej 	/*
39477d85de2Sthorpej 	 * Don't allow the interleave to be smaller than
39577d85de2Sthorpej 	 * the biggest component sector.
39677d85de2Sthorpej 	 */
39777d85de2Sthorpej 	if ((cs->sc_ileave > 0) &&
39877d85de2Sthorpej 	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
39977d85de2Sthorpej #ifdef DEBUG
40077d85de2Sthorpej 		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
40186373f8cSchristos 			printf("%s: interleave must be at least %d\n",
4025b39541eSthorpej 			    cs->sc_xname, (maxsecsize / DEV_BSIZE));
40377d85de2Sthorpej #endif
404aa8b5ebdSenami 		error = EINVAL;
405aa8b5ebdSenami 		goto out;
40677d85de2Sthorpej 	}
40777d85de2Sthorpej 
4086f9db1eeShpeyerl 	/*
4096f9db1eeShpeyerl 	 * If uniform interleave is desired set all sizes to that of
4106f9db1eeShpeyerl 	 * the smallest component.
4116f9db1eeShpeyerl 	 */
4120272b2abSthorpej 	if (cs->sc_flags & CCDF_UNIFORM) {
4136f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
4146f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
4156f9db1eeShpeyerl 			ci->ci_size = minsize;
416732dd94aSthorpej 
4176f9db1eeShpeyerl 		cs->sc_size = cs->sc_nccdisks * minsize;
4186f9db1eeShpeyerl 	}
41977d85de2Sthorpej 
4206f9db1eeShpeyerl 	/*
42177d85de2Sthorpej 	 * Construct the interleave table.
4226f9db1eeShpeyerl 	 */
4230272b2abSthorpej 	ccdinterleave(cs);
42477d85de2Sthorpej 
42577d85de2Sthorpej 	/*
42677d85de2Sthorpej 	 * Create pseudo-geometry based on 1MB cylinders.  It's
42777d85de2Sthorpej 	 * pretty close.
42877d85de2Sthorpej 	 */
42977d85de2Sthorpej 	ccg->ccg_secsize = DEV_BSIZE;
43089d4987eSthorpej 	ccg->ccg_ntracks = 1;
43177d85de2Sthorpej 	ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize);
43277d85de2Sthorpej 	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
43377d85de2Sthorpej 
4347d706cf7Ssborrill 	if (cs->sc_ileave > 0)
4357d706cf7Ssborrill 	        aprint_normal("%s: Interleaving %d component%s "
4367d706cf7Ssborrill 	            "(%d block interleave)\n", cs->sc_xname,
4377d706cf7Ssborrill         	    cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""),
4387d706cf7Ssborrill         	    cs->sc_ileave);
4397d706cf7Ssborrill 	else
4407d706cf7Ssborrill 	        aprint_normal("%s: Concatenating %d component%s\n",
4417d706cf7Ssborrill 	            cs->sc_xname,
4427d706cf7Ssborrill         	    cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""));
4437d706cf7Ssborrill 	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
4447d706cf7Ssborrill 		ci = &cs->sc_cinfo[ix];
4457d706cf7Ssborrill 		aprint_normal("%s: %s (%ju blocks)\n", cs->sc_xname,
4467d706cf7Ssborrill 		    ci->ci_path, (uintmax_t)ci->ci_size);
4477d706cf7Ssborrill 	}
4487d706cf7Ssborrill 	aprint_normal("%s: total %ju blocks\n", cs->sc_xname, cs->sc_size);
4497d706cf7Ssborrill 
450481ff5acSad 	/*
451481ff5acSad 	 * Create thread to handle deferred I/O.
452481ff5acSad 	 */
453481ff5acSad 	cs->sc_zap = false;
454481ff5acSad 	error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, ccdthread,
455481ff5acSad 	    cs, &cs->sc_thread, "%s", cs->sc_xname);
456481ff5acSad 	if (error) {
457481ff5acSad 		printf("ccdinit: can't create thread: %d\n", error);
458481ff5acSad 		goto out;
459481ff5acSad 	}
460481ff5acSad 
461481ff5acSad 	/*
462481ff5acSad 	 * Only now that everything is set up can we enable the device.
463481ff5acSad 	 */
464481ff5acSad 	mutex_enter(cs->sc_iolock);
46577d85de2Sthorpej 	cs->sc_flags |= CCDF_INITED;
466481ff5acSad 	mutex_exit(cs->sc_iolock);
467481ff5acSad 	kmem_free(tmppath, MAXPATHLEN);
46877d85de2Sthorpej 	return (0);
469aa8b5ebdSenami 
470aa8b5ebdSenami  out:
471481ff5acSad 	for (ix = 0; ix < path_alloced; ix++) {
472481ff5acSad 		kmem_free(cs->sc_cinfo[ix].ci_path,
473481ff5acSad 		    cs->sc_cinfo[ix].ci_pathlen);
474481ff5acSad 	}
475481ff5acSad 	kmem_free(cs->sc_cinfo, cs->sc_nccdisks * sizeof(struct ccdcinfo));
476481ff5acSad 	kmem_free(tmppath, MAXPATHLEN);
477aa8b5ebdSenami 	return (error);
4786f9db1eeShpeyerl }
4796f9db1eeShpeyerl 
48077d85de2Sthorpej static void
481b8d38500Sthorpej ccdinterleave(struct ccd_softc *cs)
4826f9db1eeShpeyerl {
483169ac5b3Saugustss 	struct ccdcinfo *ci, *smallci;
484169ac5b3Saugustss 	struct ccdiinfo *ii;
485169ac5b3Saugustss 	daddr_t bn, lbn;
486169ac5b3Saugustss 	int ix;
4876f9db1eeShpeyerl 	u_long size;
4886f9db1eeShpeyerl 
4896f9db1eeShpeyerl #ifdef DEBUG
4903214723fShpeyerl 	if (ccddebug & CCDB_INIT)
49186373f8cSchristos 		printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave);
4926f9db1eeShpeyerl #endif
4936f9db1eeShpeyerl 	/*
4946f9db1eeShpeyerl 	 * Allocate an interleave table.
4956f9db1eeShpeyerl 	 * Chances are this is too big, but we don't care.
4966f9db1eeShpeyerl 	 */
4976f9db1eeShpeyerl 	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
498481ff5acSad 	cs->sc_itable = kmem_zalloc(size, KM_SLEEP);
49977d85de2Sthorpej 
5006f9db1eeShpeyerl 	/*
5016f9db1eeShpeyerl 	 * Trivial case: no interleave (actually interleave of disk size).
50277d85de2Sthorpej 	 * Each table entry represents a single component in its entirety.
5036f9db1eeShpeyerl 	 */
5046f9db1eeShpeyerl 	if (cs->sc_ileave == 0) {
5056f9db1eeShpeyerl 		bn = 0;
5066f9db1eeShpeyerl 		ii = cs->sc_itable;
50777d85de2Sthorpej 
5086f9db1eeShpeyerl 		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
50989d4987eSthorpej 			/* Allocate space for ii_index. */
510481ff5acSad 			ii->ii_indexsz = sizeof(int);
511481ff5acSad 			ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP);
5126f9db1eeShpeyerl 			ii->ii_ndisk = 1;
5136f9db1eeShpeyerl 			ii->ii_startblk = bn;
5146f9db1eeShpeyerl 			ii->ii_startoff = 0;
5156f9db1eeShpeyerl 			ii->ii_index[0] = ix;
5166f9db1eeShpeyerl 			bn += cs->sc_cinfo[ix].ci_size;
5176f9db1eeShpeyerl 			ii++;
5186f9db1eeShpeyerl 		}
5196f9db1eeShpeyerl 		ii->ii_ndisk = 0;
5206f9db1eeShpeyerl #ifdef DEBUG
5213214723fShpeyerl 		if (ccddebug & CCDB_INIT)
5226f9db1eeShpeyerl 			printiinfo(cs->sc_itable);
5236f9db1eeShpeyerl #endif
52477d85de2Sthorpej 		return;
5256f9db1eeShpeyerl 	}
52677d85de2Sthorpej 
5276f9db1eeShpeyerl 	/*
5286f9db1eeShpeyerl 	 * The following isn't fast or pretty; it doesn't have to be.
5296f9db1eeShpeyerl 	 */
5306f9db1eeShpeyerl 	size = 0;
5316f9db1eeShpeyerl 	bn = lbn = 0;
5326f9db1eeShpeyerl 	for (ii = cs->sc_itable; ; ii++) {
53377d85de2Sthorpej 		/* Allocate space for ii_index. */
534481ff5acSad 		ii->ii_indexsz = sizeof(int) * cs->sc_nccdisks;
535481ff5acSad 		ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP);
53677d85de2Sthorpej 
5376f9db1eeShpeyerl 		/*
5386f9db1eeShpeyerl 		 * Locate the smallest of the remaining components
5396f9db1eeShpeyerl 		 */
5406f9db1eeShpeyerl 		smallci = NULL;
5416f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
5426f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
5436f9db1eeShpeyerl 			if (ci->ci_size > size &&
5446f9db1eeShpeyerl 			    (smallci == NULL ||
5456f9db1eeShpeyerl 			     ci->ci_size < smallci->ci_size))
5466f9db1eeShpeyerl 				smallci = ci;
54777d85de2Sthorpej 
5486f9db1eeShpeyerl 		/*
5496f9db1eeShpeyerl 		 * Nobody left, all done
5506f9db1eeShpeyerl 		 */
5516f9db1eeShpeyerl 		if (smallci == NULL) {
5526f9db1eeShpeyerl 			ii->ii_ndisk = 0;
5536f9db1eeShpeyerl 			break;
5546f9db1eeShpeyerl 		}
55577d85de2Sthorpej 
5566f9db1eeShpeyerl 		/*
5576f9db1eeShpeyerl 		 * Record starting logical block and component offset
5586f9db1eeShpeyerl 		 */
5596f9db1eeShpeyerl 		ii->ii_startblk = bn / cs->sc_ileave;
5606f9db1eeShpeyerl 		ii->ii_startoff = lbn;
56177d85de2Sthorpej 
5626f9db1eeShpeyerl 		/*
5636f9db1eeShpeyerl 		 * Determine how many disks take part in this interleave
5646f9db1eeShpeyerl 		 * and record their indices.
5656f9db1eeShpeyerl 		 */
5666f9db1eeShpeyerl 		ix = 0;
5676f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
5686f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
5696f9db1eeShpeyerl 			if (ci->ci_size >= smallci->ci_size)
5706f9db1eeShpeyerl 				ii->ii_index[ix++] = ci - cs->sc_cinfo;
5716f9db1eeShpeyerl 		ii->ii_ndisk = ix;
5726f9db1eeShpeyerl 		bn += ix * (smallci->ci_size - size);
5736f9db1eeShpeyerl 		lbn = smallci->ci_size / cs->sc_ileave;
5746f9db1eeShpeyerl 		size = smallci->ci_size;
5756f9db1eeShpeyerl 	}
5766f9db1eeShpeyerl #ifdef DEBUG
5773214723fShpeyerl 	if (ccddebug & CCDB_INIT)
5786f9db1eeShpeyerl 		printiinfo(cs->sc_itable);
5796f9db1eeShpeyerl #endif
5806f9db1eeShpeyerl }
5816f9db1eeShpeyerl 
58277d85de2Sthorpej /* ARGSUSED */
583b8d38500Sthorpej static int
584168cd830Schristos ccdopen(dev_t dev, int flags, int fmt, struct lwp *l)
5856f9db1eeShpeyerl {
5866f9db1eeShpeyerl 	int unit = ccdunit(dev);
58777d85de2Sthorpej 	struct ccd_softc *cs;
58877d85de2Sthorpej 	struct disklabel *lp;
589b8dcfbd3Sthorpej 	int error = 0, part, pmask;
5906f9db1eeShpeyerl 
5916f9db1eeShpeyerl #ifdef DEBUG
5923214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
59308ebead9Scegger 		printf("ccdopen(0x%"PRIx64", 0x%x)\n", dev, flags);
5946f9db1eeShpeyerl #endif
595b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
596b08b65c6Schristos 		return ENXIO;
59777d85de2Sthorpej 
598481ff5acSad 	mutex_enter(&cs->sc_dvlock);
599b8dcfbd3Sthorpej 
6005b39541eSthorpej 	lp = cs->sc_dkdev.dk_label;
60177d85de2Sthorpej 
60277d85de2Sthorpej 	part = DISKPART(dev);
60377d85de2Sthorpej 	pmask = (1 << part);
60477d85de2Sthorpej 
605b8dcfbd3Sthorpej 	/*
606b8dcfbd3Sthorpej 	 * If we're initialized, check to see if there are any other
607b8dcfbd3Sthorpej 	 * open partitions.  If not, then it's safe to update
6084aef0509Sthorpej 	 * the in-core disklabel.  Only read the disklabel if it is
6094aef0509Sthorpej 	 * not already valid.
610b8dcfbd3Sthorpej 	 */
6114aef0509Sthorpej 	if ((cs->sc_flags & (CCDF_INITED|CCDF_VLABEL)) == CCDF_INITED &&
6124aef0509Sthorpej 	    cs->sc_dkdev.dk_openmask == 0)
613b8dcfbd3Sthorpej 		ccdgetdisklabel(dev);
614b8dcfbd3Sthorpej 
61577d85de2Sthorpej 	/* Check that the partition exists. */
61691bd533cSthorpej 	if (part != RAW_PART) {
61791bd533cSthorpej 		if (((cs->sc_flags & CCDF_INITED) == 0) ||
618bb608224Sthorpej 		    ((part >= lp->d_npartitions) ||
619b8dcfbd3Sthorpej 		     (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
620b8dcfbd3Sthorpej 			error = ENXIO;
621b8dcfbd3Sthorpej 			goto done;
622b8dcfbd3Sthorpej 		}
62391bd533cSthorpej 	}
62477d85de2Sthorpej 
62577d85de2Sthorpej 	/* Prevent our unit from being unconfigured while open. */
62677d85de2Sthorpej 	switch (fmt) {
62777d85de2Sthorpej 	case S_IFCHR:
62877d85de2Sthorpej 		cs->sc_dkdev.dk_copenmask |= pmask;
62977d85de2Sthorpej 		break;
63077d85de2Sthorpej 
63177d85de2Sthorpej 	case S_IFBLK:
63277d85de2Sthorpej 		cs->sc_dkdev.dk_bopenmask |= pmask;
63377d85de2Sthorpej 		break;
63477d85de2Sthorpej 	}
63577d85de2Sthorpej 	cs->sc_dkdev.dk_openmask =
63677d85de2Sthorpej 	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
63777d85de2Sthorpej 
638b8dcfbd3Sthorpej  done:
639481ff5acSad 	mutex_exit(&cs->sc_dvlock);
64049c5d29eSthorpej 	return (error);
6416f9db1eeShpeyerl }
6426f9db1eeShpeyerl 
64377d85de2Sthorpej /* ARGSUSED */
644b8d38500Sthorpej static int
645168cd830Schristos ccdclose(dev_t dev, int flags, int fmt, struct lwp *l)
646a44a2765Scgd {
64777d85de2Sthorpej 	int unit = ccdunit(dev);
64877d85de2Sthorpej 	struct ccd_softc *cs;
6499abeea58Sad 	int part;
65077d85de2Sthorpej 
651a44a2765Scgd #ifdef DEBUG
652a44a2765Scgd 	if (ccddebug & CCDB_FOLLOW)
65308ebead9Scegger 		printf("ccdclose(0x%"PRIx64", 0x%x)\n", dev, flags);
654a44a2765Scgd #endif
65577d85de2Sthorpej 
656b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
657b08b65c6Schristos 		return ENXIO;
658b8dcfbd3Sthorpej 
659481ff5acSad 	mutex_enter(&cs->sc_dvlock);
660b8dcfbd3Sthorpej 
66177d85de2Sthorpej 	part = DISKPART(dev);
66277d85de2Sthorpej 
66377d85de2Sthorpej 	/* ...that much closer to allowing unconfiguration... */
66477d85de2Sthorpej 	switch (fmt) {
66577d85de2Sthorpej 	case S_IFCHR:
66677d85de2Sthorpej 		cs->sc_dkdev.dk_copenmask &= ~(1 << part);
66777d85de2Sthorpej 		break;
66877d85de2Sthorpej 
66977d85de2Sthorpej 	case S_IFBLK:
67077d85de2Sthorpej 		cs->sc_dkdev.dk_bopenmask &= ~(1 << part);
67177d85de2Sthorpej 		break;
67277d85de2Sthorpej 	}
67377d85de2Sthorpej 	cs->sc_dkdev.dk_openmask =
67477d85de2Sthorpej 	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
67577d85de2Sthorpej 
6764aef0509Sthorpej 	if (cs->sc_dkdev.dk_openmask == 0) {
6774aef0509Sthorpej 		if ((cs->sc_flags & CCDF_KLABEL) == 0)
6784aef0509Sthorpej 			cs->sc_flags &= ~CCDF_VLABEL;
6794aef0509Sthorpej 	}
6804aef0509Sthorpej 
681481ff5acSad 	mutex_exit(&cs->sc_dvlock);
682a44a2765Scgd 	return (0);
683a44a2765Scgd }
684a44a2765Scgd 
685481ff5acSad static bool
686481ff5acSad ccdbackoff(struct ccd_softc *cs)
687481ff5acSad {
688481ff5acSad 
689481ff5acSad 	/* XXX Arbitrary, should be a uvm call. */
690481ff5acSad 	return uvmexp.free < (uvmexp.freemin >> 1) &&
691481ff5acSad 	    disk_isbusy(&cs->sc_dkdev);
692481ff5acSad }
693481ff5acSad 
694481ff5acSad static void
695481ff5acSad ccdthread(void *cookie)
696481ff5acSad {
697481ff5acSad 	struct ccd_softc *cs;
698481ff5acSad 
699481ff5acSad 	cs = cookie;
700481ff5acSad 
701481ff5acSad #ifdef DEBUG
702481ff5acSad  	if (ccddebug & CCDB_FOLLOW)
703481ff5acSad  		printf("ccdthread: hello\n");
704481ff5acSad #endif
705481ff5acSad 
706481ff5acSad 	mutex_enter(cs->sc_iolock);
707481ff5acSad 	while (__predict_true(!cs->sc_zap)) {
708481ff5acSad 		if (bufq_peek(cs->sc_bufq) == NULL) {
709481ff5acSad 			/* Nothing to do. */
710481ff5acSad 			cv_wait(&cs->sc_push, cs->sc_iolock);
711481ff5acSad 			continue;
712481ff5acSad 		}
713481ff5acSad 		if (ccdbackoff(cs)) {
714481ff5acSad 			/* Wait for memory to become available. */
715481ff5acSad 			(void)cv_timedwait(&cs->sc_push, cs->sc_iolock, 1);
716481ff5acSad 			continue;
717481ff5acSad 		}
718481ff5acSad #ifdef DEBUG
719481ff5acSad  		if (ccddebug & CCDB_FOLLOW)
720481ff5acSad  			printf("ccdthread: dispatching I/O\n");
721481ff5acSad #endif
722481ff5acSad 		ccdstart(cs);
723481ff5acSad 		mutex_enter(cs->sc_iolock);
724481ff5acSad 	}
725481ff5acSad 	cs->sc_thread = NULL;
726481ff5acSad 	mutex_exit(cs->sc_iolock);
727481ff5acSad #ifdef DEBUG
728481ff5acSad  	if (ccddebug & CCDB_FOLLOW)
729481ff5acSad  		printf("ccdthread: goodbye\n");
730481ff5acSad #endif
731481ff5acSad 	kthread_exit(0);
732481ff5acSad }
733481ff5acSad 
734b8d38500Sthorpej static void
735b8d38500Sthorpej ccdstrategy(struct buf *bp)
7366f9db1eeShpeyerl {
737169ac5b3Saugustss 	int unit = ccdunit(bp->b_dev);
738b08b65c6Schristos 	struct ccd_softc *cs;
739b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
740b08b65c6Schristos 		return;
7416f9db1eeShpeyerl 
742481ff5acSad 	/* Must be open or reading label. */
743481ff5acSad 	KASSERT(cs->sc_dkdev.dk_openmask != 0 ||
744481ff5acSad 	    (cs->sc_flags & CCDF_RLABEL) != 0);
745481ff5acSad 
746481ff5acSad 	mutex_enter(cs->sc_iolock);
747481ff5acSad 	/* Synchronize with device init/uninit. */
748481ff5acSad 	if (__predict_false((cs->sc_flags & CCDF_INITED) == 0)) {
749481ff5acSad 		mutex_exit(cs->sc_iolock);
750c6aa25bfSthorpej #ifdef DEBUG
751c6aa25bfSthorpej  		if (ccddebug & CCDB_FOLLOW)
752c6aa25bfSthorpej  			printf("ccdstrategy: unit %d: not inited\n", unit);
753c6aa25bfSthorpej #endif
7546f9db1eeShpeyerl  		bp->b_error = ENXIO;
755481ff5acSad  		bp->b_resid = bp->b_bcount;
756481ff5acSad  		biodone(bp);
757481ff5acSad 		return;
7586f9db1eeShpeyerl 	}
75977d85de2Sthorpej 
760481ff5acSad 	/* Defer to thread if system is low on memory. */
761481ff5acSad 	bufq_put(cs->sc_bufq, bp);
762481ff5acSad 	if (__predict_false(ccdbackoff(cs))) {
763481ff5acSad 		mutex_exit(cs->sc_iolock);
764481ff5acSad #ifdef DEBUG
765481ff5acSad  		if (ccddebug & CCDB_FOLLOW)
766481ff5acSad  			printf("ccdstrategy: holding off on I/O\n");
767481ff5acSad #endif
768481ff5acSad 		return;
769481ff5acSad 	}
770481ff5acSad 	ccdstart(cs);
771481ff5acSad }
772481ff5acSad 
773481ff5acSad static void
774481ff5acSad ccdstart(struct ccd_softc *cs)
775481ff5acSad {
776481ff5acSad 	daddr_t blkno;
777481ff5acSad 	int wlabel;
778481ff5acSad 	struct disklabel *lp;
779481ff5acSad 	long bcount, rcount;
780481ff5acSad 	struct ccdbuf *cbp;
781481ff5acSad 	char *addr;
782481ff5acSad 	daddr_t bn;
783481ff5acSad 	vnode_t *vp;
784481ff5acSad 	buf_t *bp;
785481ff5acSad 
786481ff5acSad 	KASSERT(mutex_owned(cs->sc_iolock));
787481ff5acSad 
788481ff5acSad 	disk_busy(&cs->sc_dkdev);
789481ff5acSad 	bp = bufq_get(cs->sc_bufq);
790481ff5acSad 	KASSERT(bp != NULL);
791481ff5acSad 
792481ff5acSad #ifdef DEBUG
793481ff5acSad 	if (ccddebug & CCDB_FOLLOW)
794481ff5acSad 		printf("ccdstart(%s, %p)\n", cs->sc_xname, bp);
795481ff5acSad #endif
796481ff5acSad 
79777d85de2Sthorpej 	/* If it's a nil transfer, wake up the top half now. */
79877d85de2Sthorpej 	if (bp->b_bcount == 0)
7996f9db1eeShpeyerl 		goto done;
80077d85de2Sthorpej 
8015b39541eSthorpej 	lp = cs->sc_dkdev.dk_label;
802b8dcfbd3Sthorpej 
80377d85de2Sthorpej 	/*
804ab0109adSthorpej 	 * Do bounds checking and adjust transfer.  If there's an
8052f25411dSthorpej 	 * error, the bounds check will flag that for us.  Convert
8062f25411dSthorpej 	 * the partition relative block number to an absolute.
80777d85de2Sthorpej 	 */
8082f25411dSthorpej 	blkno = bp->b_blkno;
80977d85de2Sthorpej 	wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
8102f25411dSthorpej 	if (DISKPART(bp->b_dev) != RAW_PART) {
811e43fecb2Sthorpej 		if (bounds_check_with_label(&cs->sc_dkdev, bp, wlabel) <= 0)
8126f9db1eeShpeyerl 			goto done;
8132f25411dSthorpej 		blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
8142f25411dSthorpej 	}
815481ff5acSad 	mutex_exit(cs->sc_iolock);
8162f25411dSthorpej 	bp->b_rawblkno = blkno;
81777d85de2Sthorpej 
818481ff5acSad 	/* Allocate the component buffers and start I/O! */
8192f25411dSthorpej 	bp->b_resid = bp->b_bcount;
8202f25411dSthorpej 	bn = bp->b_rawblkno;
8213214723fShpeyerl 	addr = bp->b_data;
8226f9db1eeShpeyerl 	for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
823092c2019Sthorpej 		cbp = ccdbuffer(cs, bp, bn, addr, bcount);
824092c2019Sthorpej 		rcount = cbp->cb_buf.b_bcount;
825d272bb00Sthorpej 		bn += btodb(rcount);
826d272bb00Sthorpej 		addr += rcount;
827481ff5acSad 		vp = cbp->cb_buf.b_vp;
828481ff5acSad 		if ((cbp->cb_buf.b_flags & B_READ) == 0) {
829e225b7bdSrmind 			mutex_enter(vp->v_interlock);
830481ff5acSad 			vp->v_numoutput++;
831e225b7bdSrmind 			mutex_exit(vp->v_interlock);
832d272bb00Sthorpej 		}
833481ff5acSad 		(void)VOP_STRATEGY(vp, &cbp->cb_buf);
834481ff5acSad 	}
835481ff5acSad 	return;
836d272bb00Sthorpej 
837481ff5acSad  done:
838481ff5acSad 	disk_unbusy(&cs->sc_dkdev, 0, 0);
839481ff5acSad 	cv_broadcast(&cs->sc_stop);
840481ff5acSad 	cv_broadcast(&cs->sc_push);
841481ff5acSad 	mutex_exit(cs->sc_iolock);
842481ff5acSad 	bp->b_resid = bp->b_bcount;
843481ff5acSad 	biodone(bp);
8442f25411dSthorpej }
8456f9db1eeShpeyerl 
8466f9db1eeShpeyerl /*
8476f9db1eeShpeyerl  * Build a component buffer header.
8486f9db1eeShpeyerl  */
849092c2019Sthorpej static struct ccdbuf *
85053524e44Schristos ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, void *addr,
851b8d38500Sthorpej     long bcount)
8526f9db1eeShpeyerl {
853169ac5b3Saugustss 	struct ccdcinfo *ci;
854169ac5b3Saugustss 	struct ccdbuf *cbp;
855169ac5b3Saugustss 	daddr_t cbn, cboff;
856169ac5b3Saugustss 	u_int64_t cbc;
8577770d7c4Sthorpej 	int ccdisk;
8586f9db1eeShpeyerl 
8596f9db1eeShpeyerl #ifdef DEBUG
8603214723fShpeyerl 	if (ccddebug & CCDB_IO)
8614e0e5333Skleink 		printf("ccdbuffer(%p, %p, %" PRId64 ", %p, %ld)\n",
8626f9db1eeShpeyerl 		       cs, bp, bn, addr, bcount);
8636f9db1eeShpeyerl #endif
8646f9db1eeShpeyerl 	/*
8656f9db1eeShpeyerl 	 * Determine which component bn falls in.
8666f9db1eeShpeyerl 	 */
8676f9db1eeShpeyerl 	cbn = bn;
8686f9db1eeShpeyerl 	cboff = 0;
86977d85de2Sthorpej 
8706f9db1eeShpeyerl 	/*
8716f9db1eeShpeyerl 	 * Serially concatenated
8726f9db1eeShpeyerl 	 */
8736f9db1eeShpeyerl 	if (cs->sc_ileave == 0) {
874169ac5b3Saugustss 		daddr_t sblk;
8756f9db1eeShpeyerl 
8766f9db1eeShpeyerl 		sblk = 0;
8777770d7c4Sthorpej 		for (ccdisk = 0, ci = &cs->sc_cinfo[ccdisk];
8787770d7c4Sthorpej 		    cbn >= sblk + ci->ci_size;
8797770d7c4Sthorpej 		    ccdisk++, ci = &cs->sc_cinfo[ccdisk])
8806f9db1eeShpeyerl 			sblk += ci->ci_size;
8816f9db1eeShpeyerl 		cbn -= sblk;
8826f9db1eeShpeyerl 	}
8836f9db1eeShpeyerl 	/*
8846f9db1eeShpeyerl 	 * Interleaved
8856f9db1eeShpeyerl 	 */
8866f9db1eeShpeyerl 	else {
887169ac5b3Saugustss 		struct ccdiinfo *ii;
8887770d7c4Sthorpej 		int off;
8896f9db1eeShpeyerl 
8906f9db1eeShpeyerl 		cboff = cbn % cs->sc_ileave;
8916f9db1eeShpeyerl 		cbn /= cs->sc_ileave;
8926f9db1eeShpeyerl 		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
8936f9db1eeShpeyerl 			if (ii->ii_startblk > cbn)
8946f9db1eeShpeyerl 				break;
8956f9db1eeShpeyerl 		ii--;
8966f9db1eeShpeyerl 		off = cbn - ii->ii_startblk;
8976f9db1eeShpeyerl 		if (ii->ii_ndisk == 1) {
8986f9db1eeShpeyerl 			ccdisk = ii->ii_index[0];
8996f9db1eeShpeyerl 			cbn = ii->ii_startoff + off;
9006f9db1eeShpeyerl 		} else {
9016f9db1eeShpeyerl 			ccdisk = ii->ii_index[off % ii->ii_ndisk];
9026f9db1eeShpeyerl 			cbn = ii->ii_startoff + off / ii->ii_ndisk;
9036f9db1eeShpeyerl 		}
9046f9db1eeShpeyerl 		cbn *= cs->sc_ileave;
9056f9db1eeShpeyerl 		ci = &cs->sc_cinfo[ccdisk];
9066f9db1eeShpeyerl 	}
90777d85de2Sthorpej 
9086f9db1eeShpeyerl 	/*
9096f9db1eeShpeyerl 	 * Fill in the component buf structure.
9106f9db1eeShpeyerl 	 */
911dd5ae858Sthorpej 	cbp = CCD_GETBUF();
912481ff5acSad 	KASSERT(cbp != NULL);
9134a780c9aSad 	buf_init(&cbp->cb_buf);
9144a780c9aSad 	cbp->cb_buf.b_flags = bp->b_flags;
9154a780c9aSad 	cbp->cb_buf.b_oflags = bp->b_oflags;
9164a780c9aSad 	cbp->cb_buf.b_cflags = bp->b_cflags;
917d318abdbSchristos 	cbp->cb_buf.b_iodone = ccdiodone;
918f694af67Scgd 	cbp->cb_buf.b_proc = bp->b_proc;
9193db4e2acShannken 	cbp->cb_buf.b_dev = ci->ci_dev;
920f694af67Scgd 	cbp->cb_buf.b_blkno = cbn + cboff;
921f694af67Scgd 	cbp->cb_buf.b_data = addr;
92277d85de2Sthorpej 	cbp->cb_buf.b_vp = ci->ci_vp;
923e225b7bdSrmind 	cbp->cb_buf.b_objlock = ci->ci_vp->v_interlock;
9246f9db1eeShpeyerl 	if (cs->sc_ileave == 0)
925902855d6Sthorpej 		cbc = dbtob((u_int64_t)(ci->ci_size - cbn));
9266f9db1eeShpeyerl 	else
927902855d6Sthorpej 		cbc = dbtob((u_int64_t)(cs->sc_ileave - cboff));
928902855d6Sthorpej 	cbp->cb_buf.b_bcount = cbc < bcount ? cbc : bcount;
929f694af67Scgd 
9306f9db1eeShpeyerl 	/*
931f694af67Scgd 	 * context for ccdiodone
9326f9db1eeShpeyerl 	 */
933f694af67Scgd 	cbp->cb_obp = bp;
934c6aa25bfSthorpej 	cbp->cb_sc = cs;
9357770d7c4Sthorpej 	cbp->cb_comp = ccdisk;
936f694af67Scgd 
9377266a959Syamt 	BIO_COPYPRIO(&cbp->cb_buf, bp);
9387266a959Syamt 
9396f9db1eeShpeyerl #ifdef DEBUG
9403214723fShpeyerl 	if (ccddebug & CCDB_IO)
94108ebead9Scegger 		printf(" dev 0x%"PRIx64"(u%lu): cbp %p bn %" PRId64 " addr %p"
94222399b45Syamt 		       " bcnt %d\n",
943fefcc128Smjacob 		    ci->ci_dev, (unsigned long) (ci-cs->sc_cinfo), cbp,
944fefcc128Smjacob 		    cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
945fefcc128Smjacob 		    cbp->cb_buf.b_bcount);
9466f9db1eeShpeyerl #endif
947092c2019Sthorpej 
948092c2019Sthorpej 	return (cbp);
9496f9db1eeShpeyerl }
9506f9db1eeShpeyerl 
9516f9db1eeShpeyerl /*
95277d85de2Sthorpej  * Called at interrupt time.
9536f9db1eeShpeyerl  * Mark the component as done and if all components are done,
9546f9db1eeShpeyerl  * take a ccd interrupt.
9556f9db1eeShpeyerl  */
956b8d38500Sthorpej static void
957b8d38500Sthorpej ccdiodone(struct buf *vbp)
9586f9db1eeShpeyerl {
959d318abdbSchristos 	struct ccdbuf *cbp = (struct ccdbuf *) vbp;
960c6aa25bfSthorpej 	struct buf *bp = cbp->cb_obp;
961c6aa25bfSthorpej 	struct ccd_softc *cs = cbp->cb_sc;
962481ff5acSad 	int count;
9636f9db1eeShpeyerl 
9646f9db1eeShpeyerl #ifdef DEBUG
9653214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
96686373f8cSchristos 		printf("ccdiodone(%p)\n", cbp);
9673214723fShpeyerl 	if (ccddebug & CCDB_IO) {
96822399b45Syamt 		printf("ccdiodone: bp %p bcount %d resid %d\n",
9696f9db1eeShpeyerl 		       bp, bp->b_bcount, bp->b_resid);
97008ebead9Scegger 		printf(" dev 0x%"PRIx64"(u%d), cbp %p bn %" PRId64 " addr %p"
97122399b45Syamt 		       " bcnt %d\n",
972f694af67Scgd 		       cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
973f694af67Scgd 		       cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
974f694af67Scgd 		       cbp->cb_buf.b_bcount);
9756f9db1eeShpeyerl 	}
9766f9db1eeShpeyerl #endif
9776f9db1eeShpeyerl 
978eb171eaaSad 	if (cbp->cb_buf.b_error != 0) {
979eb171eaaSad 		bp->b_error = cbp->cb_buf.b_error;
98000172c10Sthorpej 		printf("%s: error %d on component %d\n",
98100172c10Sthorpej 		       cs->sc_xname, bp->b_error, cbp->cb_comp);
9826f9db1eeShpeyerl 	}
983f694af67Scgd 	count = cbp->cb_buf.b_bcount;
9844a780c9aSad 	buf_destroy(&cbp->cb_buf);
985dd5ae858Sthorpej 	CCD_PUTBUF(cbp);
9866f9db1eeShpeyerl 
9876f9db1eeShpeyerl 	/*
9886f9db1eeShpeyerl 	 * If all done, "interrupt".
9896f9db1eeShpeyerl 	 */
990481ff5acSad 	mutex_enter(cs->sc_iolock);
9916f9db1eeShpeyerl 	bp->b_resid -= count;
9926f9db1eeShpeyerl 	if (bp->b_resid < 0)
9936f9db1eeShpeyerl 		panic("ccdiodone: count");
994481ff5acSad 	if (bp->b_resid == 0) {
995481ff5acSad 		/*
996481ff5acSad 		 * Request is done for better or worse, wakeup the top half.
997481ff5acSad 		 */
998481ff5acSad 		if (bp->b_error != 0)
999481ff5acSad 			bp->b_resid = bp->b_bcount;
1000481ff5acSad 		disk_unbusy(&cs->sc_dkdev, (bp->b_bcount - bp->b_resid),
1001481ff5acSad 		    (bp->b_flags & B_READ));
1002481ff5acSad 		if (!disk_isbusy(&cs->sc_dkdev)) {
1003481ff5acSad 			if (bufq_peek(cs->sc_bufq) != NULL) {
1004481ff5acSad 				cv_broadcast(&cs->sc_push);
1005481ff5acSad 			}
1006481ff5acSad 			cv_broadcast(&cs->sc_stop);
1007481ff5acSad 		}
1008481ff5acSad 		mutex_exit(cs->sc_iolock);
1009481ff5acSad 		biodone(bp);
1010481ff5acSad 	} else
1011481ff5acSad 		mutex_exit(cs->sc_iolock);
10126f9db1eeShpeyerl }
10136f9db1eeShpeyerl 
101477d85de2Sthorpej /* ARGSUSED */
1015b8d38500Sthorpej static int
1016168cd830Schristos ccdread(dev_t dev, struct uio *uio, int flags)
10173214723fShpeyerl {
101877d85de2Sthorpej 	int unit = ccdunit(dev);
101977d85de2Sthorpej 	struct ccd_softc *cs;
10203214723fShpeyerl 
10213214723fShpeyerl #ifdef DEBUG
10223214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
102308ebead9Scegger 		printf("ccdread(0x%"PRIx64", %p)\n", dev, uio);
10243214723fShpeyerl #endif
1025b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
1026b08b65c6Schristos 		return 0;
102777d85de2Sthorpej 
1028481ff5acSad 	/* Unlocked advisory check, ccdstrategy check is synchronous. */
102977d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
103077d85de2Sthorpej 		return (ENXIO);
103177d85de2Sthorpej 
10323214723fShpeyerl 	return (physio(ccdstrategy, NULL, dev, B_READ, minphys, uio));
10333214723fShpeyerl }
10343214723fShpeyerl 
103577d85de2Sthorpej /* ARGSUSED */
1036b8d38500Sthorpej static int
1037168cd830Schristos ccdwrite(dev_t dev, struct uio *uio, int flags)
10383214723fShpeyerl {
103977d85de2Sthorpej 	int unit = ccdunit(dev);
104077d85de2Sthorpej 	struct ccd_softc *cs;
10413214723fShpeyerl 
10423214723fShpeyerl #ifdef DEBUG
10433214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
104408ebead9Scegger 		printf("ccdwrite(0x%"PRIx64", %p)\n", dev, uio);
10453214723fShpeyerl #endif
1046b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
1047b08b65c6Schristos 		return ENOENT;
104877d85de2Sthorpej 
1049481ff5acSad 	/* Unlocked advisory check, ccdstrategy check is synchronous. */
105077d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
105177d85de2Sthorpej 		return (ENXIO);
105277d85de2Sthorpej 
10533214723fShpeyerl 	return (physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio));
10543214723fShpeyerl }
10553214723fShpeyerl 
1056b8d38500Sthorpej static int
105753524e44Schristos ccdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
10586f9db1eeShpeyerl {
105977d85de2Sthorpej 	int unit = ccdunit(dev);
1060481ff5acSad 	int i, j, lookedup = 0, error = 0;
10618f8b0d91Smycroft 	int part, pmask;
106277d85de2Sthorpej 	struct ccd_softc *cs;
106377d85de2Sthorpej 	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
10642867b68bSelad 	kauth_cred_t uc;
106577d85de2Sthorpej 	char **cpp;
10668f6ed30dSdholland 	struct pathbuf *pb;
106777d85de2Sthorpej 	struct vnode **vpp;
1068e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1069e2d1c1f9Sfvdl 	struct disklabel newlabel;
1070e2d1c1f9Sfvdl #endif
107177d85de2Sthorpej 
1072b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
1073b08b65c6Schristos 		return ENOENT;
1074481ff5acSad 	uc = kauth_cred_get();
107597297f37Sjld 
10767d706cf7Ssborrill /*
10777d706cf7Ssborrill  * Compat code must not be called if on a platform where
10787d706cf7Ssborrill  * sizeof (size_t) == sizeof (uint64_t) as CCDIOCSET will
10797d706cf7Ssborrill  * be the same as CCDIOCSET_60
10807d706cf7Ssborrill  */
10817d706cf7Ssborrill #if defined(COMPAT_60) && !defined(_LP64)
10827d706cf7Ssborrill 	switch (cmd) {
10837d706cf7Ssborrill 	case CCDIOCSET_60: {
10847d706cf7Ssborrill 		struct ccd_ioctl ccionew;
10857d706cf7Ssborrill        		struct ccd_ioctl_60 *ccio60 =
10867d706cf7Ssborrill        		    (struct ccd_ioctl_60 *)data;
10877d706cf7Ssborrill 		ccionew.ccio_disks = ccio->ccio_disks;
10887d706cf7Ssborrill 		ccionew.ccio_ndisks = ccio->ccio_ndisks;
10897d706cf7Ssborrill 		ccionew.ccio_ileave = ccio->ccio_ileave;
10907d706cf7Ssborrill 		ccionew.ccio_flags = ccio->ccio_flags;
10917d706cf7Ssborrill 		ccionew.ccio_unit = ccio->ccio_unit;
10927d706cf7Ssborrill 		error = ccdioctl(dev, CCDIOCSET, &ccionew, flag, l);
10937d706cf7Ssborrill 		if (!error) {
10947d706cf7Ssborrill 			/* Copy data back, adjust types if necessary */
10957d706cf7Ssborrill 			ccio60->ccio_disks = ccionew.ccio_disks;
10967d706cf7Ssborrill 			ccio60->ccio_ndisks = ccionew.ccio_ndisks;
10977d706cf7Ssborrill 			ccio60->ccio_ileave = ccionew.ccio_ileave;
10987d706cf7Ssborrill 			ccio60->ccio_flags = ccionew.ccio_flags;
10997d706cf7Ssborrill 			ccio60->ccio_unit = ccionew.ccio_unit;
11007d706cf7Ssborrill 			ccio60->ccio_size = (size_t)ccionew.ccio_size;
11017d706cf7Ssborrill 		}
11027d706cf7Ssborrill 		return error;
11037d706cf7Ssborrill 		}
11047d706cf7Ssborrill 		break;
11057d706cf7Ssborrill 
11067d706cf7Ssborrill 	case CCDIOCCLR_60:
11077d706cf7Ssborrill 		/*
11087d706cf7Ssborrill 		 * ccio_size member not used, so existing struct OK
11097d706cf7Ssborrill 		 * drop through to existing non-compat version
11107d706cf7Ssborrill 		 */
11117d706cf7Ssborrill 		cmd = CCDIOCCLR;
11127d706cf7Ssborrill 		break;
11137d706cf7Ssborrill 	}
11147d706cf7Ssborrill #endif /* COMPAT_60 && !_LP64*/
11157d706cf7Ssborrill 
11164cea35ebSthorpej 	/* Must be open for writes for these commands... */
11174cea35ebSthorpej 	switch (cmd) {
11184cea35ebSthorpej 	case CCDIOCSET:
11194cea35ebSthorpej 	case CCDIOCCLR:
11204cea35ebSthorpej 	case DIOCSDINFO:
11214cea35ebSthorpej 	case DIOCWDINFO:
1122e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1123e2d1c1f9Sfvdl 	case ODIOCSDINFO:
1124e2d1c1f9Sfvdl 	case ODIOCWDINFO:
1125e2d1c1f9Sfvdl #endif
11264aef0509Sthorpej 	case DIOCKLABEL:
11274cea35ebSthorpej 	case DIOCWLABEL:
11284cea35ebSthorpej 		if ((flag & FWRITE) == 0)
11294cea35ebSthorpej 			return (EBADF);
11304cea35ebSthorpej 	}
11314cea35ebSthorpej 
1132481ff5acSad 	mutex_enter(&cs->sc_dvlock);
11330272b2abSthorpej 
11344cea35ebSthorpej 	/* Must be initialized for these... */
11354cea35ebSthorpej 	switch (cmd) {
11364cea35ebSthorpej 	case CCDIOCCLR:
1137d72a3b2dSkleink 	case DIOCGDINFO:
1138f64a29d7Sthorpej 	case DIOCCACHESYNC:
11394cea35ebSthorpej 	case DIOCSDINFO:
11404cea35ebSthorpej 	case DIOCWDINFO:
11414cea35ebSthorpej 	case DIOCGPART:
11424cea35ebSthorpej 	case DIOCWLABEL:
11434aef0509Sthorpej 	case DIOCKLABEL:
1144939e074dSthorpej 	case DIOCGDEFLABEL:
1145e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1146e2d1c1f9Sfvdl 	case ODIOCGDINFO:
1147e2d1c1f9Sfvdl 	case ODIOCSDINFO:
1148e2d1c1f9Sfvdl 	case ODIOCWDINFO:
1149e2d1c1f9Sfvdl 	case ODIOCGDEFLABEL:
1150e2d1c1f9Sfvdl #endif
11510272b2abSthorpej 		if ((cs->sc_flags & CCDF_INITED) == 0) {
11520272b2abSthorpej 			error = ENXIO;
11530272b2abSthorpej 			goto out;
11540272b2abSthorpej 		}
11554cea35ebSthorpej 	}
11564cea35ebSthorpej 
115777d85de2Sthorpej 	switch (cmd) {
115877d85de2Sthorpej 	case CCDIOCSET:
11590272b2abSthorpej 		if (cs->sc_flags & CCDF_INITED) {
11600272b2abSthorpej 			error = EBUSY;
11610272b2abSthorpej 			goto out;
11620272b2abSthorpej 		}
1163b8dcfbd3Sthorpej 
1164ab27c3f8Sthorpej 		/* Validate the flags. */
11650272b2abSthorpej 		if ((ccio->ccio_flags & CCDF_USERMASK) != ccio->ccio_flags) {
11660272b2abSthorpej 			error = EINVAL;
11670272b2abSthorpej 			goto out;
11680272b2abSthorpej 		}
1169ab27c3f8Sthorpej 
1170481ff5acSad 		if (ccio->ccio_ndisks > CCD_MAXNDISKS ||
1171481ff5acSad 		    ccio->ccio_ndisks == 0) {
1172539f3bccSjdolecek 			error = EINVAL;
1173539f3bccSjdolecek 			goto out;
1174539f3bccSjdolecek 		}
1175539f3bccSjdolecek 
117677d85de2Sthorpej 		/* Fill in some important bits. */
11770272b2abSthorpej 		cs->sc_ileave = ccio->ccio_ileave;
11780272b2abSthorpej 		cs->sc_nccdisks = ccio->ccio_ndisks;
11790272b2abSthorpej 		cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK;
118077d85de2Sthorpej 
118177d85de2Sthorpej 		/*
118277d85de2Sthorpej 		 * Allocate space for and copy in the array of
11837d706cf7Ssborrill 		 * component pathnames and device numbers.
118477d85de2Sthorpej 		 */
1185481ff5acSad 		cpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*cpp), KM_SLEEP);
1186481ff5acSad 		vpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*vpp), KM_SLEEP);
1187bd99e342Sdsl 		error = copyin(ccio->ccio_disks, cpp,
1188481ff5acSad 		    ccio->ccio_ndisks * sizeof(*cpp));
118977d85de2Sthorpej 		if (error) {
1190481ff5acSad 			kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1191481ff5acSad 			kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
11920272b2abSthorpej 			goto out;
11936f9db1eeShpeyerl 		}
11946f9db1eeShpeyerl 
119577d85de2Sthorpej #ifdef DEBUG
119677d85de2Sthorpej 		if (ccddebug & CCDB_INIT)
119777d85de2Sthorpej 			for (i = 0; i < ccio->ccio_ndisks; ++i)
11987cdea212Schristos 				printf("ccdioctl: component %d: %p\n",
119977d85de2Sthorpej 				    i, cpp[i]);
120077d85de2Sthorpej #endif
120177d85de2Sthorpej 
120277d85de2Sthorpej 		for (i = 0; i < ccio->ccio_ndisks; ++i) {
120377d85de2Sthorpej #ifdef DEBUG
120477d85de2Sthorpej 			if (ccddebug & CCDB_INIT)
120586373f8cSchristos 				printf("ccdioctl: lookedup = %d\n", lookedup);
120677d85de2Sthorpej #endif
12078f6ed30dSdholland 			error = pathbuf_copyin(cpp[i], &pb);
12080a4a6fb0Sdholland 			if (error == 0) {
12098f6ed30dSdholland 				error = dk_lookup(pb, l, &vpp[i]);
12100a4a6fb0Sdholland 			}
12118f6ed30dSdholland 			pathbuf_destroy(pb);
12128f6ed30dSdholland 			if (error != 0) {
121377d85de2Sthorpej 				for (j = 0; j < lookedup; ++j)
1214a2db2c18Sthorpej 					(void)vn_close(vpp[j], FREAD|FWRITE,
1215a9ca7a37Sad 					    uc);
1216481ff5acSad 				kmem_free(vpp, ccio->ccio_ndisks *
1217481ff5acSad 				    sizeof(*vpp));
1218481ff5acSad 				kmem_free(cpp, ccio->ccio_ndisks *
1219481ff5acSad 				    sizeof(*cpp));
12200272b2abSthorpej 				goto out;
122177d85de2Sthorpej 			}
122277d85de2Sthorpej 			++lookedup;
122377d85de2Sthorpej 		}
122477d85de2Sthorpej 
1225481ff5acSad 		/* Attach the disk. */
1226481ff5acSad 		disk_attach(&cs->sc_dkdev);
1227481ff5acSad 		bufq_alloc(&cs->sc_bufq, "fcfs", 0);
1228481ff5acSad 
122977d85de2Sthorpej 		/*
123077d85de2Sthorpej 		 * Initialize the ccd.  Fills in the softc for us.
123177d85de2Sthorpej 		 */
123295e1ffb1Schristos 		if ((error = ccdinit(cs, cpp, vpp, l)) != 0) {
123377d85de2Sthorpej 			for (j = 0; j < lookedup; ++j)
1234a1bc3740Sthorpej 				(void)vn_close(vpp[j], FREAD|FWRITE,
1235a9ca7a37Sad 				    uc);
1236481ff5acSad 			kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1237481ff5acSad 			kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
1238481ff5acSad 			disk_detach(&cs->sc_dkdev);
1239481ff5acSad 			bufq_free(cs->sc_bufq);
12400272b2abSthorpej 			goto out;
124177d85de2Sthorpej 		}
124277d85de2Sthorpej 
12430272b2abSthorpej 		/* We can free the temporary variables now. */
1244481ff5acSad 		kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1245481ff5acSad 		kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
12460272b2abSthorpej 
124777d85de2Sthorpej 		/*
124877d85de2Sthorpej 		 * The ccd has been successfully initialized, so
12495b39541eSthorpej 		 * we can place it into the array.  Don't try to
12505b39541eSthorpej 		 * read the disklabel until the disk has been attached,
12515b39541eSthorpej 		 * because space for the disklabel is allocated
12525b39541eSthorpej 		 * in disk_attach();
125377d85de2Sthorpej 		 */
125477d85de2Sthorpej 		ccio->ccio_unit = unit;
125577d85de2Sthorpej 		ccio->ccio_size = cs->sc_size;
12565b39541eSthorpej 
12575b39541eSthorpej 		/* Try and read the disklabel. */
125877d85de2Sthorpej 		ccdgetdisklabel(dev);
125977d85de2Sthorpej 		break;
126077d85de2Sthorpej 
126177d85de2Sthorpej 	case CCDIOCCLR:
126277d85de2Sthorpej 		/*
126377d85de2Sthorpej 		 * Don't unconfigure if any other partitions are open
126477d85de2Sthorpej 		 * or if both the character and block flavors of this
126577d85de2Sthorpej 		 * partition are open.
126677d85de2Sthorpej 		 */
126777d85de2Sthorpej 		part = DISKPART(dev);
126877d85de2Sthorpej 		pmask = (1 << part);
126977d85de2Sthorpej 		if ((cs->sc_dkdev.dk_openmask & ~pmask) ||
127077d85de2Sthorpej 		    ((cs->sc_dkdev.dk_bopenmask & pmask) &&
1271b8dcfbd3Sthorpej 		    (cs->sc_dkdev.dk_copenmask & pmask))) {
12720272b2abSthorpej 			error = EBUSY;
12730272b2abSthorpej 			goto out;
1274b8dcfbd3Sthorpej 		}
127577d85de2Sthorpej 
1276481ff5acSad 		/* Stop new I/O, wait for in-flight I/O to complete. */
1277481ff5acSad 		mutex_enter(cs->sc_iolock);
1278481ff5acSad 		cs->sc_flags &= ~(CCDF_INITED|CCDF_VLABEL);
1279481ff5acSad 		cs->sc_zap = true;
1280481ff5acSad 		while (disk_isbusy(&cs->sc_dkdev) ||
1281481ff5acSad 		    bufq_peek(cs->sc_bufq) != NULL ||
1282481ff5acSad 		    cs->sc_thread != NULL) {
1283481ff5acSad 			cv_broadcast(&cs->sc_push);
1284481ff5acSad 			(void)cv_timedwait(&cs->sc_stop, cs->sc_iolock, hz);
1285481ff5acSad 		}
1286481ff5acSad 		mutex_exit(cs->sc_iolock);
12872f25411dSthorpej 
128877d85de2Sthorpej 		/*
128977d85de2Sthorpej 		 * Free ccd_softc information and clear entry.
129077d85de2Sthorpej 		 */
12917268bf55Sthorpej 
12927268bf55Sthorpej 		/* Close the components and free their pathnames. */
129377d85de2Sthorpej 		for (i = 0; i < cs->sc_nccdisks; ++i) {
129477d85de2Sthorpej 			/*
129577d85de2Sthorpej 			 * XXX: this close could potentially fail and
129677d85de2Sthorpej 			 * cause Bad Things.  Maybe we need to force
129777d85de2Sthorpej 			 * the close to happen?
129877d85de2Sthorpej 			 */
129977d85de2Sthorpej #ifdef DEBUG
130077d85de2Sthorpej 			if (ccddebug & CCDB_VNODE)
130177d85de2Sthorpej 				vprint("CCDIOCCLR: vnode info",
130277d85de2Sthorpej 				    cs->sc_cinfo[i].ci_vp);
130377d85de2Sthorpej #endif
130477d85de2Sthorpej 			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1305a9ca7a37Sad 			    uc);
1306481ff5acSad 			kmem_free(cs->sc_cinfo[i].ci_path,
1307481ff5acSad 			    cs->sc_cinfo[i].ci_pathlen);
130877d85de2Sthorpej 		}
13097268bf55Sthorpej 
13107268bf55Sthorpej 		/* Free interleave index. */
1311481ff5acSad 		for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) {
1312481ff5acSad 			kmem_free(cs->sc_itable[i].ii_index,
1313481ff5acSad 			    cs->sc_itable[i].ii_indexsz);
1314481ff5acSad 		}
13157268bf55Sthorpej 
13167268bf55Sthorpej 		/* Free component info and interleave table. */
1317481ff5acSad 		kmem_free(cs->sc_cinfo, cs->sc_nccdisks *
1318481ff5acSad 		    sizeof(struct ccdcinfo));
1319481ff5acSad 		kmem_free(cs->sc_itable, (cs->sc_nccdisks + 1) *
1320481ff5acSad 		    sizeof(struct ccdiinfo));
132177d85de2Sthorpej 
13227d706cf7Ssborrill 		aprint_normal("%s: detached\n", cs->sc_xname);
13237d706cf7Ssborrill 
13247d706cf7Ssborrill 		/* Detach the disk. */
13252af68666Sad 		disk_detach(&cs->sc_dkdev);
1326481ff5acSad 		bufq_free(cs->sc_bufq);
1327b08b65c6Schristos 		ccdput(cs);
132855252202Sjoerg 		/* Don't break, otherwise cs is read again. */
132955252202Sjoerg 		return 0;
133077d85de2Sthorpej 
133177d85de2Sthorpej 	case DIOCGDINFO:
13325b39541eSthorpej 		*(struct disklabel *)data = *(cs->sc_dkdev.dk_label);
133377d85de2Sthorpej 		break;
1334481ff5acSad 
1335e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1336e2d1c1f9Sfvdl 	case ODIOCGDINFO:
1337e2d1c1f9Sfvdl 		newlabel = *(cs->sc_dkdev.dk_label);
1338e2d1c1f9Sfvdl 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
1339d040bd59Sfvdl 			return ENOTTY;
1340e2d1c1f9Sfvdl 		memcpy(data, &newlabel, sizeof (struct olddisklabel));
1341e2d1c1f9Sfvdl 		break;
1342e2d1c1f9Sfvdl #endif
134377d85de2Sthorpej 
134477d85de2Sthorpej 	case DIOCGPART:
13455b39541eSthorpej 		((struct partinfo *)data)->disklab = cs->sc_dkdev.dk_label;
134677d85de2Sthorpej 		((struct partinfo *)data)->part =
13475b39541eSthorpej 		    &cs->sc_dkdev.dk_label->d_partitions[DISKPART(dev)];
134877d85de2Sthorpej 		break;
134977d85de2Sthorpej 
1350f64a29d7Sthorpej 	case DIOCCACHESYNC:
1351f64a29d7Sthorpej 		/*
1352f64a29d7Sthorpej 		 * XXX Do we really need to care about having a writable
1353f64a29d7Sthorpej 		 * file descriptor here?
1354f64a29d7Sthorpej 		 */
1355f64a29d7Sthorpej 		if ((flag & FWRITE) == 0)
1356f64a29d7Sthorpej 			return (EBADF);
1357f64a29d7Sthorpej 
1358f64a29d7Sthorpej 		/*
1359f64a29d7Sthorpej 		 * We pass this call down to all components and report
1360f64a29d7Sthorpej 		 * the first error we encounter.
1361f64a29d7Sthorpej 		 */
1362f64a29d7Sthorpej 		for (error = 0, i = 0; i < cs->sc_nccdisks; i++) {
1363f64a29d7Sthorpej 			j = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, data,
136461e8303eSpooka 				      flag, uc);
1365f64a29d7Sthorpej 			if (j != 0 && error == 0)
1366f64a29d7Sthorpej 				error = j;
1367f64a29d7Sthorpej 		}
1368f64a29d7Sthorpej 		break;
1369f64a29d7Sthorpej 
137077d85de2Sthorpej 	case DIOCWDINFO:
137177d85de2Sthorpej 	case DIOCSDINFO:
1372e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1373e2d1c1f9Sfvdl 	case ODIOCWDINFO:
1374e2d1c1f9Sfvdl 	case ODIOCSDINFO:
1375e2d1c1f9Sfvdl #endif
1376e2d1c1f9Sfvdl 	{
1377e2d1c1f9Sfvdl 		struct disklabel *lp;
1378e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1379e2d1c1f9Sfvdl 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
1380e2d1c1f9Sfvdl 			memset(&newlabel, 0, sizeof newlabel);
1381e2d1c1f9Sfvdl 			memcpy(&newlabel, data, sizeof (struct olddisklabel));
1382e2d1c1f9Sfvdl 			lp = &newlabel;
1383e2d1c1f9Sfvdl 		} else
1384e2d1c1f9Sfvdl #endif
1385e2d1c1f9Sfvdl 		lp = (struct disklabel *)data;
1386e2d1c1f9Sfvdl 
138777d85de2Sthorpej 		cs->sc_flags |= CCDF_LABELLING;
138877d85de2Sthorpej 
13895b39541eSthorpej 		error = setdisklabel(cs->sc_dkdev.dk_label,
1390e2d1c1f9Sfvdl 		    lp, 0, cs->sc_dkdev.dk_cpulabel);
139177d85de2Sthorpej 		if (error == 0) {
1392e2d1c1f9Sfvdl 			if (cmd == DIOCWDINFO
1393e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1394e2d1c1f9Sfvdl 			    || cmd == ODIOCWDINFO
1395e2d1c1f9Sfvdl #endif
1396e2d1c1f9Sfvdl 			   )
139777d85de2Sthorpej 				error = writedisklabel(CCDLABELDEV(dev),
13985b39541eSthorpej 				    ccdstrategy, cs->sc_dkdev.dk_label,
13995b39541eSthorpej 				    cs->sc_dkdev.dk_cpulabel);
140077d85de2Sthorpej 		}
140177d85de2Sthorpej 
140277d85de2Sthorpej 		cs->sc_flags &= ~CCDF_LABELLING;
140377d85de2Sthorpej 		break;
1404e2d1c1f9Sfvdl 	}
140577d85de2Sthorpej 
14064aef0509Sthorpej 	case DIOCKLABEL:
14074aef0509Sthorpej 		if (*(int *)data != 0)
14084aef0509Sthorpej 			cs->sc_flags |= CCDF_KLABEL;
14094aef0509Sthorpej 		else
14104aef0509Sthorpej 			cs->sc_flags &= ~CCDF_KLABEL;
14114aef0509Sthorpej 		break;
14124aef0509Sthorpej 
141377d85de2Sthorpej 	case DIOCWLABEL:
141477d85de2Sthorpej 		if (*(int *)data != 0)
141577d85de2Sthorpej 			cs->sc_flags |= CCDF_WLABEL;
141677d85de2Sthorpej 		else
141777d85de2Sthorpej 			cs->sc_flags &= ~CCDF_WLABEL;
141877d85de2Sthorpej 		break;
141977d85de2Sthorpej 
1420939e074dSthorpej 	case DIOCGDEFLABEL:
1421939e074dSthorpej 		ccdgetdefaultlabel(cs, (struct disklabel *)data);
1422939e074dSthorpej 		break;
1423939e074dSthorpej 
1424e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1425e2d1c1f9Sfvdl 	case ODIOCGDEFLABEL:
1426e2d1c1f9Sfvdl 		ccdgetdefaultlabel(cs, &newlabel);
1427e2d1c1f9Sfvdl 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
1428d040bd59Sfvdl 			return ENOTTY;
1429e2d1c1f9Sfvdl 		memcpy(data, &newlabel, sizeof (struct olddisklabel));
1430e2d1c1f9Sfvdl 		break;
1431e2d1c1f9Sfvdl #endif
1432e2d1c1f9Sfvdl 
143377d85de2Sthorpej 	default:
14340272b2abSthorpej 		error = ENOTTY;
143577d85de2Sthorpej 	}
143677d85de2Sthorpej 
14370272b2abSthorpej  out:
1438481ff5acSad 	mutex_exit(&cs->sc_dvlock);
14390272b2abSthorpej 	return (error);
144077d85de2Sthorpej }
144177d85de2Sthorpej 
1442b8d38500Sthorpej static int
1443b8d38500Sthorpej ccdsize(dev_t dev)
14446f9db1eeShpeyerl {
144577d85de2Sthorpej 	struct ccd_softc *cs;
1446e06bb29bSthorpej 	struct disklabel *lp;
1447e06bb29bSthorpej 	int part, unit, omask, size;
14486f9db1eeShpeyerl 
1449e06bb29bSthorpej 	unit = ccdunit(dev);
1450b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
1451b08b65c6Schristos 		return -1;
145277d85de2Sthorpej 
145377d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
145477d85de2Sthorpej 		return (-1);
145577d85de2Sthorpej 
1456e06bb29bSthorpej 	part = DISKPART(dev);
1457e06bb29bSthorpej 	omask = cs->sc_dkdev.dk_openmask & (1 << part);
1458e06bb29bSthorpej 	lp = cs->sc_dkdev.dk_label;
1459e06bb29bSthorpej 
146095e1ffb1Schristos 	if (omask == 0 && ccdopen(dev, 0, S_IFBLK, curlwp))
1461e06bb29bSthorpej 		return (-1);
1462e06bb29bSthorpej 
1463e06bb29bSthorpej 	if (lp->d_partitions[part].p_fstype != FS_SWAP)
146477d85de2Sthorpej 		size = -1;
146577d85de2Sthorpej 	else
1466e06bb29bSthorpej 		size = lp->d_partitions[part].p_size *
1467e06bb29bSthorpej 		    (lp->d_secsize / DEV_BSIZE);
146877d85de2Sthorpej 
146995e1ffb1Schristos 	if (omask == 0 && ccdclose(dev, 0, S_IFBLK, curlwp))
147077d85de2Sthorpej 		return (-1);
147177d85de2Sthorpej 
147277d85de2Sthorpej 	return (size);
14736f9db1eeShpeyerl }
14746f9db1eeShpeyerl 
147577d85de2Sthorpej static void
1476b8d38500Sthorpej ccdgetdefaultlabel(struct ccd_softc *cs, struct disklabel *lp)
147777d85de2Sthorpej {
147877d85de2Sthorpej 	struct ccdgeom *ccg = &cs->sc_geom;
147977d85de2Sthorpej 
1480c8b4ac1bSthorpej 	memset(lp, 0, sizeof(*lp));
148177d85de2Sthorpej 
148253385cb9Smlelstv 	if (cs->sc_size > UINT32_MAX)
148353385cb9Smlelstv 		lp->d_secperunit = UINT32_MAX;
148453385cb9Smlelstv 	else
148577d85de2Sthorpej 		lp->d_secperunit = cs->sc_size;
148677d85de2Sthorpej 	lp->d_secsize = ccg->ccg_secsize;
148777d85de2Sthorpej 	lp->d_nsectors = ccg->ccg_nsectors;
148877d85de2Sthorpej 	lp->d_ntracks = ccg->ccg_ntracks;
148977d85de2Sthorpej 	lp->d_ncylinders = ccg->ccg_ncylinders;
149089d4987eSthorpej 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
149177d85de2Sthorpej 
149277d85de2Sthorpej 	strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
149377d85de2Sthorpej 	lp->d_type = DTYPE_CCD;
149477d85de2Sthorpej 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
149577d85de2Sthorpej 	lp->d_rpm = 3600;
149677d85de2Sthorpej 	lp->d_interleave = 1;
149777d85de2Sthorpej 	lp->d_flags = 0;
149877d85de2Sthorpej 
149977d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_offset = 0;
150053385cb9Smlelstv 	lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
150177d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
150277d85de2Sthorpej 	lp->d_npartitions = RAW_PART + 1;
150377d85de2Sthorpej 
150477d85de2Sthorpej 	lp->d_magic = DISKMAGIC;
150577d85de2Sthorpej 	lp->d_magic2 = DISKMAGIC;
15065b39541eSthorpej 	lp->d_checksum = dkcksum(cs->sc_dkdev.dk_label);
1507939e074dSthorpej }
1508939e074dSthorpej 
1509939e074dSthorpej /*
1510939e074dSthorpej  * Read the disklabel from the ccd.  If one is not present, fake one
1511939e074dSthorpej  * up.
1512939e074dSthorpej  */
1513939e074dSthorpej static void
1514b8d38500Sthorpej ccdgetdisklabel(dev_t dev)
1515939e074dSthorpej {
1516939e074dSthorpej 	int unit = ccdunit(dev);
1517b08b65c6Schristos 	struct ccd_softc *cs;
1518d91455ceSdsl 	const char *errstring;
1519b08b65c6Schristos 	struct disklabel *lp;
1520b08b65c6Schristos 	struct cpu_disklabel *clp;
1521939e074dSthorpej 
1522b08b65c6Schristos 	if ((cs = ccdget(unit)) == NULL)
1523b08b65c6Schristos 		return;
1524b08b65c6Schristos 	lp = cs->sc_dkdev.dk_label;
1525b08b65c6Schristos 	clp = cs->sc_dkdev.dk_cpulabel;
1526481ff5acSad 	KASSERT(mutex_owned(&cs->sc_dvlock));
1527481ff5acSad 
1528c8b4ac1bSthorpej 	memset(clp, 0, sizeof(*clp));
1529939e074dSthorpej 
1530939e074dSthorpej 	ccdgetdefaultlabel(cs, lp);
153177d85de2Sthorpej 
153277d85de2Sthorpej 	/*
153377d85de2Sthorpej 	 * Call the generic disklabel extraction routine.
153477d85de2Sthorpej 	 */
1535481ff5acSad 	cs->sc_flags |= CCDF_RLABEL;
15360b441623Slukem 	if ((cs->sc_flags & CCDF_NOLABEL) != 0)
15370b441623Slukem 		errstring = "CCDF_NOLABEL set; ignoring on-disk label";
15380b441623Slukem 	else
1539d318abdbSchristos 		errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy,
1540d318abdbSchristos 		    cs->sc_dkdev.dk_label, cs->sc_dkdev.dk_cpulabel);
1541d318abdbSchristos 	if (errstring)
154277d85de2Sthorpej 		ccdmakedisklabel(cs);
1543512a0186Senami 	else {
1544512a0186Senami 		int i;
1545512a0186Senami 		struct partition *pp;
1546512a0186Senami 
1547512a0186Senami 		/*
1548512a0186Senami 		 * Sanity check whether the found disklabel is valid.
1549512a0186Senami 		 *
1550512a0186Senami 		 * This is necessary since total size of ccd may vary
1551512a0186Senami 		 * when an interleave is changed even though exactly
1552512a0186Senami 		 * same componets are used, and old disklabel may used
1553512a0186Senami 		 * if that is found.
1554512a0186Senami 		 */
1555*49e3024aSmlelstv 		if (lp->d_secperunit < UINT32_MAX ?
1556*49e3024aSmlelstv 			lp->d_secperunit != cs->sc_size :
1557*49e3024aSmlelstv 			lp->d_secperunit > cs->sc_size)
1558512a0186Senami 			printf("WARNING: %s: "
15597d706cf7Ssborrill 			    "total sector size in disklabel (%ju) != "
15607d706cf7Ssborrill 			    "the size of ccd (%ju)\n", cs->sc_xname,
15617d706cf7Ssborrill 			    (uintmax_t)lp->d_secperunit,
15627d706cf7Ssborrill 			    (uintmax_t)cs->sc_size);
1563512a0186Senami 		for (i = 0; i < lp->d_npartitions; i++) {
1564512a0186Senami 			pp = &lp->d_partitions[i];
1565512a0186Senami 			if (pp->p_offset + pp->p_size > cs->sc_size)
15669b770726Senami 				printf("WARNING: %s: end of partition `%c' "
15677d706cf7Ssborrill 				    "exceeds the size of ccd (%ju)\n",
15687d706cf7Ssborrill 				    cs->sc_xname, 'a' + i, (uintmax_t)cs->sc_size);
1569512a0186Senami 		}
1570512a0186Senami 	}
157177d85de2Sthorpej 
157277d85de2Sthorpej #ifdef DEBUG
157377d85de2Sthorpej 	/* It's actually extremely common to have unlabeled ccds. */
157477d85de2Sthorpej 	if (ccddebug & CCDB_LABEL)
157577d85de2Sthorpej 		if (errstring != NULL)
157686373f8cSchristos 			printf("%s: %s\n", cs->sc_xname, errstring);
157777d85de2Sthorpej #endif
15784aef0509Sthorpej 
15794aef0509Sthorpej 	/* In-core label now valid. */
1580481ff5acSad 	cs->sc_flags = (cs->sc_flags | CCDF_VLABEL) & ~CCDF_RLABEL;
158177d85de2Sthorpej }
158277d85de2Sthorpej 
158377d85de2Sthorpej /*
158477d85de2Sthorpej  * Take care of things one might want to take care of in the event
158577d85de2Sthorpej  * that a disklabel isn't present.
158677d85de2Sthorpej  */
158777d85de2Sthorpej static void
1588b8d38500Sthorpej ccdmakedisklabel(struct ccd_softc *cs)
158977d85de2Sthorpej {
15905b39541eSthorpej 	struct disklabel *lp = cs->sc_dkdev.dk_label;
159177d85de2Sthorpej 
159277d85de2Sthorpej 	/*
159377d85de2Sthorpej 	 * For historical reasons, if there's no disklabel present
159477d85de2Sthorpej 	 * the raw partition must be marked FS_BSDFFS.
159577d85de2Sthorpej 	 */
159677d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
159777d85de2Sthorpej 
159877d85de2Sthorpej 	strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1599939e074dSthorpej 
1600939e074dSthorpej 	lp->d_checksum = dkcksum(lp);
160177d85de2Sthorpej }
160277d85de2Sthorpej 
160377d85de2Sthorpej #ifdef DEBUG
160477d85de2Sthorpej static void
1605b8d38500Sthorpej printiinfo(struct ccdiinfo *ii)
160677d85de2Sthorpej {
1607169ac5b3Saugustss 	int ix, i;
160877d85de2Sthorpej 
160977d85de2Sthorpej 	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
16104e0e5333Skleink 		printf(" itab[%d]: #dk %d sblk %" PRId64 " soff %" PRId64,
161177d85de2Sthorpej 		    ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
161277d85de2Sthorpej 		for (i = 0; i < ii->ii_ndisk; i++)
161386373f8cSchristos 			printf(" %d", ii->ii_index[i]);
161486373f8cSchristos 		printf("\n");
161577d85de2Sthorpej 	}
161677d85de2Sthorpej }
16176f9db1eeShpeyerl #endif
16187147ed32Shaad 
16198682ae0dSpgoyette MODULE(MODULE_CLASS_DRIVER, ccd, "dk_subr");
16207147ed32Shaad 
16217147ed32Shaad static int
16227147ed32Shaad ccd_modcmd(modcmd_t cmd, void *arg)
16237147ed32Shaad {
1624cd2a35a0Smartin 	int error = 0;
1625cd2a35a0Smartin #ifdef _MODULE
1626cd2a35a0Smartin 	int bmajor = -1, cmajor = -1;
1627cd2a35a0Smartin #endif
1628b4a3a8f3Sjruoho 
16297147ed32Shaad 
16307147ed32Shaad 	switch (cmd) {
16317147ed32Shaad 	case MODULE_CMD_INIT:
1632b4a3a8f3Sjruoho #ifdef _MODULE
16337147ed32Shaad 		ccdattach(4);
16347147ed32Shaad 
16357147ed32Shaad 		return devsw_attach("ccd", &ccd_bdevsw, &bmajor,
16367147ed32Shaad 		    &ccd_cdevsw, &cmajor);
1637b4a3a8f3Sjruoho #endif
16387147ed32Shaad 		break;
16397147ed32Shaad 
16407147ed32Shaad 	case MODULE_CMD_FINI:
1641b4a3a8f3Sjruoho #ifdef _MODULE
16427147ed32Shaad 		return devsw_detach(&ccd_bdevsw, &ccd_cdevsw);
1643b4a3a8f3Sjruoho #endif
16447147ed32Shaad 		break;
16457147ed32Shaad 
16467147ed32Shaad 	case MODULE_CMD_STAT:
16477147ed32Shaad 		return ENOTTY;
16487147ed32Shaad 
16497147ed32Shaad 	default:
16507147ed32Shaad 		return ENOTTY;
16517147ed32Shaad 	}
16527147ed32Shaad 
16537147ed32Shaad 	return error;
16547147ed32Shaad }
1655b08b65c6Schristos 
1656b08b65c6Schristos static int
1657b08b65c6Schristos ccd_units_sysctl(SYSCTLFN_ARGS)
1658b08b65c6Schristos {
1659b08b65c6Schristos 	struct sysctlnode node;
1660b08b65c6Schristos 	struct ccd_softc *sc;
1661b08b65c6Schristos 	int error, i, nccd, *units;
1662b08b65c6Schristos 	size_t size;
1663b08b65c6Schristos 
1664b08b65c6Schristos 	nccd = 0;
1665b08b65c6Schristos 	mutex_enter(&ccd_lock);
1666b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1667b08b65c6Schristos 		nccd++;
1668b08b65c6Schristos 	mutex_exit(&ccd_lock);
1669b08b65c6Schristos 
1670b08b65c6Schristos 	if (nccd != 0) {
1671b08b65c6Schristos 		size = nccd * sizeof(*units);
1672b08b65c6Schristos 		units = kmem_zalloc(size, KM_SLEEP);
1673b08b65c6Schristos 		if (units == NULL)
1674b08b65c6Schristos 			return ENOMEM;
1675b08b65c6Schristos 
1676b08b65c6Schristos 		i = 0;
1677b08b65c6Schristos 		mutex_enter(&ccd_lock);
1678b08b65c6Schristos 		LIST_FOREACH(sc, &ccds, sc_link) {
1679b08b65c6Schristos 			if (i >= nccd)
1680b08b65c6Schristos 				break;
1681b08b65c6Schristos 			units[i] = sc->sc_unit;
1682b08b65c6Schristos 		}
1683b08b65c6Schristos 		mutex_exit(&ccd_lock);
1684b08b65c6Schristos 	} else {
1685b08b65c6Schristos 		units = NULL;
1686b08b65c6Schristos 		size = 0;
1687b08b65c6Schristos 	}
1688b08b65c6Schristos 
1689b08b65c6Schristos 	node = *rnode;
1690b08b65c6Schristos 	node.sysctl_data = units;
1691b08b65c6Schristos 	node.sysctl_size = size;
1692b08b65c6Schristos 
1693b08b65c6Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1694b08b65c6Schristos 	if (units)
1695b08b65c6Schristos 		kmem_free(units, size);
1696b08b65c6Schristos 	return error;
1697b08b65c6Schristos }
1698b08b65c6Schristos 
1699b08b65c6Schristos static int
1700b08b65c6Schristos ccd_info_sysctl(SYSCTLFN_ARGS)
1701b08b65c6Schristos {
1702b08b65c6Schristos 	struct sysctlnode node;
1703b08b65c6Schristos 	struct ccddiskinfo ccd;
1704b08b65c6Schristos 	struct ccd_softc *sc;
1705b08b65c6Schristos 	int unit;
1706b08b65c6Schristos 
1707b08b65c6Schristos 	if (newp == NULL || newlen != sizeof(int))
1708b08b65c6Schristos 		return EINVAL;
1709b08b65c6Schristos 
1710b08b65c6Schristos 	unit = *(const int *)newp;
1711b08b65c6Schristos 	newp = NULL;
1712b08b65c6Schristos 	newlen = 0;
1713b08b65c6Schristos 	ccd.ccd_ndisks = ~0;
1714b08b65c6Schristos 	mutex_enter(&ccd_lock);
1715b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link) {
1716b08b65c6Schristos 		if (sc->sc_unit == unit) {
1717b08b65c6Schristos 			ccd.ccd_ileave = sc->sc_ileave;
1718b08b65c6Schristos 			ccd.ccd_size = sc->sc_size;
1719b08b65c6Schristos 			ccd.ccd_ndisks = sc->sc_nccdisks;
1720b08b65c6Schristos 			ccd.ccd_flags = sc->sc_flags;
1721b08b65c6Schristos 			break;
1722b08b65c6Schristos 		}
1723b08b65c6Schristos 	}
1724b08b65c6Schristos 	mutex_exit(&ccd_lock);
1725b08b65c6Schristos 
1726b08b65c6Schristos 	if (ccd.ccd_ndisks == ~0)
1727b08b65c6Schristos 		return ENOENT;
1728b08b65c6Schristos 
1729b08b65c6Schristos 	node = *rnode;
1730b08b65c6Schristos 	node.sysctl_data = &ccd;
1731b08b65c6Schristos 	node.sysctl_size = sizeof(ccd);
1732b08b65c6Schristos 
1733b08b65c6Schristos 	return sysctl_lookup(SYSCTLFN_CALL(&node));
1734b08b65c6Schristos }
1735b08b65c6Schristos 
1736b08b65c6Schristos static int
1737b08b65c6Schristos ccd_components_sysctl(SYSCTLFN_ARGS)
1738b08b65c6Schristos {
1739b08b65c6Schristos 	struct sysctlnode node;
1740b08b65c6Schristos 	int error, unit;
1741b08b65c6Schristos 	size_t size;
1742b08b65c6Schristos 	char *names, *p, *ep;
1743b08b65c6Schristos 	struct ccd_softc *sc;
1744b08b65c6Schristos 
1745b08b65c6Schristos 	if (newp == NULL || newlen != sizeof(int))
1746b08b65c6Schristos 		return EINVAL;
1747b08b65c6Schristos 
1748b08b65c6Schristos 	size = 0;
1749b08b65c6Schristos 	unit = *(const int *)newp;
1750b08b65c6Schristos 	newp = NULL;
1751b08b65c6Schristos 	newlen = 0;
1752b08b65c6Schristos 	mutex_enter(&ccd_lock);
1753b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1754b08b65c6Schristos 		if (sc->sc_unit == unit) {
1755b08b65c6Schristos 			for (size_t i = 0; i < sc->sc_nccdisks; i++)
1756b08b65c6Schristos 				size += strlen(sc->sc_cinfo[i].ci_path) + 1;
1757b08b65c6Schristos 			break;
1758b08b65c6Schristos 		}
1759b08b65c6Schristos 	mutex_exit(&ccd_lock);
1760b08b65c6Schristos 
1761b08b65c6Schristos 	if (size == 0)
1762b08b65c6Schristos 		return ENOENT;
1763b08b65c6Schristos 	names = kmem_zalloc(size, KM_SLEEP);
1764b08b65c6Schristos 	if (names == NULL)
1765b08b65c6Schristos 		return ENOMEM;
1766b08b65c6Schristos 
1767b08b65c6Schristos 	p = names;
1768b08b65c6Schristos 	ep = names + size;
1769b08b65c6Schristos 	mutex_enter(&ccd_lock);
1770b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1771b08b65c6Schristos 		if (sc->sc_unit == unit) {
1772b08b65c6Schristos 			for (size_t i = 0; i < sc->sc_nccdisks; i++) {
1773b08b65c6Schristos 				char *d = sc->sc_cinfo[i].ci_path;
1774b08b65c6Schristos 				while (p < ep && (*p++ = *d++) != '\0')
1775b08b65c6Schristos 					continue;
1776b08b65c6Schristos 			}
1777b08b65c6Schristos 			break;
1778b08b65c6Schristos 		}
1779b08b65c6Schristos 	mutex_exit(&ccd_lock);
1780b08b65c6Schristos 
1781b08b65c6Schristos 	node = *rnode;
1782b08b65c6Schristos 	node.sysctl_data = names;
1783b08b65c6Schristos 	node.sysctl_size = ep - names;
1784b08b65c6Schristos 
1785b08b65c6Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1786b08b65c6Schristos 	kmem_free(names, size);
1787b08b65c6Schristos 	return error;
1788b08b65c6Schristos }
1789b08b65c6Schristos 
1790b08b65c6Schristos SYSCTL_SETUP(sysctl_kern_ccd_setup, "sysctl kern.ccd subtree setup")
1791b08b65c6Schristos {
1792b08b65c6Schristos 	const struct sysctlnode *node = NULL;
1793b08b65c6Schristos 
1794b08b65c6Schristos 	sysctl_createv(clog, 0, NULL, &node,
1795b08b65c6Schristos 	    CTLFLAG_PERMANENT,
1796b08b65c6Schristos 	    CTLTYPE_NODE, "ccd",
1797b08b65c6Schristos 	    SYSCTL_DESCR("ConCatenated Disk state"),
1798b08b65c6Schristos 	    NULL, 0, NULL, 0,
1799b08b65c6Schristos 	    CTL_KERN, CTL_CREATE, CTL_EOL);
1800b08b65c6Schristos 
1801b08b65c6Schristos 	if (node == NULL)
1802b08b65c6Schristos 		return;
1803b08b65c6Schristos 
1804b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1805b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READONLY,
1806b08b65c6Schristos 	    CTLTYPE_STRUCT, "units",
1807b08b65c6Schristos 	    SYSCTL_DESCR("List of ccd unit numbers"),
1808b08b65c6Schristos 	    ccd_units_sysctl, 0, NULL, 0,
1809b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1810b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1811b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1812b08b65c6Schristos 	    CTLTYPE_STRUCT, "info",
1813b08b65c6Schristos 	    SYSCTL_DESCR("Information about a CCD unit"),
1814b08b65c6Schristos 	    ccd_info_sysctl, 0, NULL, 0,
1815b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1816b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1817b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1818b08b65c6Schristos 	    CTLTYPE_STRUCT, "components",
1819b08b65c6Schristos 	    SYSCTL_DESCR("Information about CCD components"),
1820b08b65c6Schristos 	    ccd_components_sysctl, 0, NULL, 0,
1821b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1822b08b65c6Schristos }
1823