xref: /netbsd-src/sys/dev/ccd.c (revision d0b020c0f430b27b43f1593a88c8f11ef3514302)
1*d0b020c0Sandvar /*	$NetBSD: ccd.c,v 1.192 2025/01/08 08:24:07 andvar 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  *
65fd1c7fefSriastradh  * 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*d0b020c0Sandvar __KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.192 2025/01/08 08:24:07 andvar Exp $");
922bbe2de6Slukem 
936f9db1eeShpeyerl #include <sys/param.h>
946f9db1eeShpeyerl #include <sys/systm.h>
95481ff5acSad #include <sys/kernel.h>
963214723fShpeyerl #include <sys/proc.h>
976f9db1eeShpeyerl #include <sys/errno.h>
986f9db1eeShpeyerl #include <sys/buf.h>
99481ff5acSad #include <sys/kmem.h>
100dd5ae858Sthorpej #include <sys/pool.h>
101b4a3a8f3Sjruoho #include <sys/module.h>
10277d85de2Sthorpej #include <sys/namei.h>
1033214723fShpeyerl #include <sys/stat.h>
1043214723fShpeyerl #include <sys/ioctl.h>
1053214723fShpeyerl #include <sys/disklabel.h>
10677d85de2Sthorpej #include <sys/device.h>
10777d85de2Sthorpej #include <sys/disk.h>
10877d85de2Sthorpej #include <sys/syslog.h>
1093214723fShpeyerl #include <sys/fcntl.h>
11077d85de2Sthorpej #include <sys/vnode.h>
111e1930da2Schristos #include <sys/conf.h>
1129abeea58Sad #include <sys/mutex.h>
113d272bb00Sthorpej #include <sys/queue.h>
1142867b68bSelad #include <sys/kauth.h>
115481ff5acSad #include <sys/kthread.h>
116481ff5acSad #include <sys/bufq.h>
117b08b65c6Schristos #include <sys/sysctl.h>
118d91f98a8Spgoyette #include <sys/compat_stub.h>
1196f9db1eeShpeyerl 
12019b39d64Suebayasi #include <uvm/uvm_extern.h>
12119b39d64Suebayasi 
1226f9db1eeShpeyerl #include <dev/ccdvar.h>
1231a789da3Schristos #include <dev/dkvar.h>
1246f9db1eeShpeyerl 
125292a0c7fShannken #include <miscfs/specfs/specdev.h> /* for v_rdev */
126292a0c7fShannken 
127e7ae23fdSchristos #include "ioconf.h"
128e7ae23fdSchristos 
12977d85de2Sthorpej #if defined(CCDDEBUG) && !defined(DEBUG)
13077d85de2Sthorpej #define DEBUG
13177d85de2Sthorpej #endif
13277d85de2Sthorpej 
1336f9db1eeShpeyerl #ifdef DEBUG
1343214723fShpeyerl #define CCDB_FOLLOW	0x01
1353214723fShpeyerl #define CCDB_INIT	0x02
1363214723fShpeyerl #define CCDB_IO		0x04
13777d85de2Sthorpej #define CCDB_LABEL	0x08
13877d85de2Sthorpej #define CCDB_VNODE	0x10
139732dd94aSthorpej int ccddebug = 0x00;
1406f9db1eeShpeyerl #endif
1416f9db1eeShpeyerl 
142f694af67Scgd #define	ccdunit(x)	DISKUNIT(x)
1436f9db1eeShpeyerl 
144f694af67Scgd struct ccdbuf {
145f694af67Scgd 	struct buf	cb_buf;		/* new I/O buf */
146f694af67Scgd 	struct buf	*cb_obp;	/* ptr. to original I/O buf */
147c6aa25bfSthorpej 	struct ccd_softc *cb_sc;	/* pointer to ccd softc */
148f694af67Scgd 	int		cb_comp;	/* target component */
149d272bb00Sthorpej 	SIMPLEQ_ENTRY(ccdbuf) cb_q;	/* fifo of component buffers */
150f694af67Scgd };
1516f9db1eeShpeyerl 
152dd5ae858Sthorpej /* component buffer pool */
153481ff5acSad static pool_cache_t ccd_cache;
154dd5ae858Sthorpej 
15579f4a4f8Shannken #define	CCD_GETBUF(wait)	pool_cache_get(ccd_cache, wait)
156481ff5acSad #define	CCD_PUTBUF(cbp)		pool_cache_put(ccd_cache, cbp)
1576f9db1eeShpeyerl 
15877d85de2Sthorpej #define CCDLABELDEV(dev)	\
15977d85de2Sthorpej 	(MAKEDISKDEV(major((dev)), ccdunit((dev)), RAW_PART))
1606f9db1eeShpeyerl 
16177d85de2Sthorpej /* called by main() at boot time */
16237efed97Schristos void	ccddetach(void);
16377d85de2Sthorpej 
16477d85de2Sthorpej /* called by biodone() at interrupt time */
165b8d38500Sthorpej static void	ccdiodone(struct buf *);
166f694af67Scgd 
167b8d38500Sthorpej static void	ccdinterleave(struct ccd_softc *);
168b8d38500Sthorpej static int	ccdinit(struct ccd_softc *, char **, struct vnode **,
16995e1ffb1Schristos 		    struct lwp *);
170b8d38500Sthorpej static struct ccdbuf *ccdbuffer(struct ccd_softc *, struct buf *,
17179f4a4f8Shannken 		    daddr_t, void *, long, int);
172b8d38500Sthorpej static void	ccdgetdefaultlabel(struct ccd_softc *, struct disklabel *);
173b8d38500Sthorpej static void	ccdgetdisklabel(dev_t);
174b8d38500Sthorpej static void	ccdmakedisklabel(struct ccd_softc *);
17579f4a4f8Shannken static int	ccdstart(struct ccd_softc *, struct buf *, int);
176481ff5acSad static void	ccdthread(void *);
1773214723fShpeyerl 
178b8d38500Sthorpej static dev_type_open(ccdopen);
179b8d38500Sthorpej static dev_type_close(ccdclose);
180b8d38500Sthorpej static dev_type_read(ccdread);
181b8d38500Sthorpej static dev_type_write(ccdwrite);
182b8d38500Sthorpej static dev_type_ioctl(ccdioctl);
183b8d38500Sthorpej static dev_type_strategy(ccdstrategy);
184b8d38500Sthorpej static dev_type_size(ccdsize);
18577a6b82bSgehenna 
18677a6b82bSgehenna const struct bdevsw ccd_bdevsw = {
187481ff5acSad 	.d_open = ccdopen,
188481ff5acSad 	.d_close = ccdclose,
189481ff5acSad 	.d_strategy = ccdstrategy,
190481ff5acSad 	.d_ioctl = ccdioctl,
191481ff5acSad 	.d_dump = nodump,
192481ff5acSad 	.d_psize = ccdsize,
1938c70ef39Sdholland 	.d_discard = nodiscard,
194481ff5acSad 	.d_flag = D_DISK | D_MPSAFE
19577a6b82bSgehenna };
19677a6b82bSgehenna 
19777a6b82bSgehenna const struct cdevsw ccd_cdevsw = {
198481ff5acSad 	.d_open = ccdopen,
199481ff5acSad 	.d_close = ccdclose,
200481ff5acSad 	.d_read = ccdread,
201481ff5acSad 	.d_write = ccdwrite,
202481ff5acSad 	.d_ioctl = ccdioctl,
203481ff5acSad 	.d_stop = nostop,
204481ff5acSad 	.d_tty = notty,
205481ff5acSad 	.d_poll = nopoll,
206481ff5acSad 	.d_mmap = nommap,
207481ff5acSad 	.d_kqfilter = nokqfilter,
208f9228f42Sdholland 	.d_discard = nodiscard,
209481ff5acSad 	.d_flag = D_DISK | D_MPSAFE
21077a6b82bSgehenna };
21177a6b82bSgehenna 
2127686d3bdSmlelstv static const struct dkdriver ccddkdriver = {
2137686d3bdSmlelstv 	.d_strategy = ccdstrategy,
2147686d3bdSmlelstv 	.d_minphys = minphys
2157686d3bdSmlelstv };
2167686d3bdSmlelstv 
21777d85de2Sthorpej #ifdef DEBUG
218b8d38500Sthorpej static	void printiinfo(struct ccdiinfo *);
21977d85de2Sthorpej #endif
22077d85de2Sthorpej 
221b08b65c6Schristos static LIST_HEAD(, ccd_softc) ccds = LIST_HEAD_INITIALIZER(ccds);
222b08b65c6Schristos static kmutex_t ccd_lock;
223b08b65c6Schristos 
224aeebcff2Spgoyette SYSCTL_SETUP_PROTO(sysctl_kern_ccd_setup);
225aeebcff2Spgoyette 
226b08b65c6Schristos static struct ccd_softc *
227b08b65c6Schristos ccdcreate(int unit) {
228b08b65c6Schristos 	struct ccd_softc *sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
229fd34ea77Schs 
230b08b65c6Schristos 	/* Initialize per-softc structures. */
231b08b65c6Schristos 	snprintf(sc->sc_xname, sizeof(sc->sc_xname), "ccd%d", unit);
232ddeabcf2Schristos 	sc->sc_unit = unit;
233b08b65c6Schristos 	mutex_init(&sc->sc_dvlock, MUTEX_DEFAULT, IPL_NONE);
234b08b65c6Schristos 	sc->sc_iolock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
235b08b65c6Schristos 	cv_init(&sc->sc_stop, "ccdstop");
236b08b65c6Schristos 	cv_init(&sc->sc_push, "ccdthr");
2377686d3bdSmlelstv 	disk_init(&sc->sc_dkdev, sc->sc_xname, &ccddkdriver);
238b08b65c6Schristos 	return sc;
239b08b65c6Schristos }
240b08b65c6Schristos 
241b08b65c6Schristos static void
242b08b65c6Schristos ccddestroy(struct ccd_softc *sc) {
243b08b65c6Schristos 	mutex_obj_free(sc->sc_iolock);
24455252202Sjoerg 	mutex_exit(&sc->sc_dvlock);
245b08b65c6Schristos 	mutex_destroy(&sc->sc_dvlock);
246b08b65c6Schristos 	cv_destroy(&sc->sc_stop);
247b08b65c6Schristos 	cv_destroy(&sc->sc_push);
248b08b65c6Schristos 	disk_destroy(&sc->sc_dkdev);
249b08b65c6Schristos 	kmem_free(sc, sizeof(*sc));
250b08b65c6Schristos }
251b08b65c6Schristos 
252b08b65c6Schristos static struct ccd_softc *
253ef781023Schristos ccdget(int unit, int make) {
254b08b65c6Schristos 	struct ccd_softc *sc;
255b08b65c6Schristos 	if (unit < 0) {
256b08b65c6Schristos #ifdef DIAGNOSTIC
257b08b65c6Schristos 		panic("%s: unit %d!", __func__, unit);
258b08b65c6Schristos #endif
259b08b65c6Schristos 		return NULL;
260b08b65c6Schristos 	}
261b08b65c6Schristos 	mutex_enter(&ccd_lock);
262b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link) {
263b08b65c6Schristos 		if (sc->sc_unit == unit) {
264b08b65c6Schristos 			mutex_exit(&ccd_lock);
265b08b65c6Schristos 			return sc;
266b08b65c6Schristos 		}
267b08b65c6Schristos 	}
268b08b65c6Schristos 	mutex_exit(&ccd_lock);
269ef781023Schristos 	if (!make)
270ef781023Schristos 		return NULL;
271b08b65c6Schristos 	if ((sc = ccdcreate(unit)) == NULL)
272b08b65c6Schristos 		return NULL;
273b08b65c6Schristos 	mutex_enter(&ccd_lock);
274b08b65c6Schristos 	LIST_INSERT_HEAD(&ccds, sc, sc_link);
275b08b65c6Schristos 	mutex_exit(&ccd_lock);
276b08b65c6Schristos 	return sc;
277b08b65c6Schristos }
278b08b65c6Schristos 
279b08b65c6Schristos static void
280b08b65c6Schristos ccdput(struct ccd_softc *sc) {
281b08b65c6Schristos 	mutex_enter(&ccd_lock);
282b08b65c6Schristos 	LIST_REMOVE(sc, sc_link);
283b08b65c6Schristos 	mutex_exit(&ccd_lock);
284b08b65c6Schristos 	ccddestroy(sc);
285b08b65c6Schristos }
2866f9db1eeShpeyerl 
2876f9db1eeShpeyerl /*
28877d85de2Sthorpej  * Called by main() during pseudo-device attachment.  All we need
28977d85de2Sthorpej  * to do is allocate enough space for devices to be configured later.
2906f9db1eeShpeyerl  */
2916f9db1eeShpeyerl void
292b8d38500Sthorpej ccdattach(int num)
2936f9db1eeShpeyerl {
294b08b65c6Schristos 	mutex_init(&ccd_lock, MUTEX_DEFAULT, IPL_NONE);
2950272b2abSthorpej 
296dd5ae858Sthorpej 	/* Initialize the component buffer pool. */
297481ff5acSad 	ccd_cache = pool_cache_init(sizeof(struct ccdbuf), 0,
298481ff5acSad 	    0, 0, "ccdbuf", NULL, IPL_BIO, NULL, NULL, NULL);
2996f9db1eeShpeyerl }
3006f9db1eeShpeyerl 
30137efed97Schristos void
30237efed97Schristos ccddetach(void)
30337efed97Schristos {
30437efed97Schristos 	pool_cache_destroy(ccd_cache);
30537efed97Schristos 	mutex_destroy(&ccd_lock);
30637efed97Schristos }
30737efed97Schristos 
30877d85de2Sthorpej static int
309b8d38500Sthorpej ccdinit(struct ccd_softc *cs, char **cpaths, struct vnode **vpp,
31095e1ffb1Schristos     struct lwp *l)
3116f9db1eeShpeyerl {
312169ac5b3Saugustss 	struct ccdcinfo *ci = NULL;
313169ac5b3Saugustss 	int ix;
31477d85de2Sthorpej 	struct ccdgeom *ccg = &cs->sc_geom;
31592d441e0Schristos 	char *tmppath;
316aa8b5ebdSenami 	int error, path_alloced;
317e839ec30Schristos 	uint64_t psize, minsize;
318e839ec30Schristos 	unsigned secsize, maxsecsize;
319026ad668Sjnemeth 	struct disk_geom *dg;
3206f9db1eeShpeyerl 
3216f9db1eeShpeyerl #ifdef DEBUG
3223214723fShpeyerl 	if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
323c6aa25bfSthorpej 		printf("%s: ccdinit\n", cs->sc_xname);
3246f9db1eeShpeyerl #endif
32577d85de2Sthorpej 
32677d85de2Sthorpej 	/* Allocate space for the component info. */
327481ff5acSad 	cs->sc_cinfo = kmem_alloc(cs->sc_nccdisks * sizeof(*cs->sc_cinfo),
328481ff5acSad 	    KM_SLEEP);
329481ff5acSad 	tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
33092d441e0Schristos 
3310272b2abSthorpej 	cs->sc_size = 0;
3320272b2abSthorpej 
3336f9db1eeShpeyerl 	/*
3346f9db1eeShpeyerl 	 * Verify that each component piece exists and record
3356f9db1eeShpeyerl 	 * relevant information about it.
3366f9db1eeShpeyerl 	 */
33777d85de2Sthorpej 	maxsecsize = 0;
3386f9db1eeShpeyerl 	minsize = 0;
339aa8b5ebdSenami 	for (ix = 0, path_alloced = 0; ix < cs->sc_nccdisks; ix++) {
3406f9db1eeShpeyerl 		ci = &cs->sc_cinfo[ix];
3410272b2abSthorpej 		ci->ci_vp = vpp[ix];
342a44a2765Scgd 
34377d85de2Sthorpej 		/*
34477d85de2Sthorpej 		 * Copy in the pathname of the component.
34577d85de2Sthorpej 		 */
34625862469Sjoerg 		memset(tmppath, 0, MAXPATHLEN);	/* sanity */
347d318abdbSchristos 		error = copyinstr(cpaths[ix], tmppath,
348d318abdbSchristos 		    MAXPATHLEN, &ci->ci_pathlen);
349481ff5acSad 		if (ci->ci_pathlen == 0)
350481ff5acSad 			error = EINVAL;
351d318abdbSchristos 		if (error) {
35277d85de2Sthorpej #ifdef DEBUG
35377d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
35486373f8cSchristos 				printf("%s: can't copy path, error = %d\n",
3555b39541eSthorpej 				    cs->sc_xname, error);
35677d85de2Sthorpej #endif
357aa8b5ebdSenami 			goto out;
35877d85de2Sthorpej 		}
359481ff5acSad 		ci->ci_path = kmem_alloc(ci->ci_pathlen, KM_SLEEP);
360c8b4ac1bSthorpej 		memcpy(ci->ci_path, tmppath, ci->ci_pathlen);
361aa8b5ebdSenami 		path_alloced++;
36277d85de2Sthorpej 
36377d85de2Sthorpej 		/*
36477d85de2Sthorpej 		 * XXX: Cache the component's dev_t.
36577d85de2Sthorpej 		 */
366292a0c7fShannken 		ci->ci_dev = vpp[ix]->v_rdev;
36777d85de2Sthorpej 
36877d85de2Sthorpej 		/*
36977d85de2Sthorpej 		 * Get partition information for the component.
37077d85de2Sthorpej 		 */
371e839ec30Schristos 		error = getdisksize(vpp[ix], &psize, &secsize);
372d318abdbSchristos 		if (error) {
37377d85de2Sthorpej #ifdef DEBUG
37477d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
375e839ec30Schristos 				 printf("%s: %s: disksize failed, error = %d\n",
3765b39541eSthorpej 				     cs->sc_xname, ci->ci_path, error);
37777d85de2Sthorpej #endif
378aa8b5ebdSenami 			goto out;
37977d85de2Sthorpej 		}
38077d85de2Sthorpej 
38177d85de2Sthorpej 		/*
38277d85de2Sthorpej 		 * Calculate the size, truncating to an interleave
38377d85de2Sthorpej 		 * boundary if necessary.
38477d85de2Sthorpej 		 */
385e839ec30Schristos 		maxsecsize = secsize > maxsecsize ? secsize : maxsecsize;
3866f9db1eeShpeyerl 		if (cs->sc_ileave > 1)
387e839ec30Schristos 			psize -= psize % cs->sc_ileave;
3883214723fShpeyerl 
389e839ec30Schristos 		if (psize == 0) {
39077d85de2Sthorpej #ifdef DEBUG
39177d85de2Sthorpej 			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
39286373f8cSchristos 				printf("%s: %s: size == 0\n",
3935b39541eSthorpej 				    cs->sc_xname, ci->ci_path);
3943214723fShpeyerl #endif
395aa8b5ebdSenami 			error = ENODEV;
396aa8b5ebdSenami 			goto out;
39777d85de2Sthorpej 		}
39877d85de2Sthorpej 
399e839ec30Schristos 		if (minsize == 0 || psize < minsize)
400e839ec30Schristos 			minsize = psize;
401e839ec30Schristos 		ci->ci_size = psize;
402e839ec30Schristos 		cs->sc_size += psize;
4036f9db1eeShpeyerl 	}
40477d85de2Sthorpej 
40577d85de2Sthorpej 	/*
40677d85de2Sthorpej 	 * Don't allow the interleave to be smaller than
40777d85de2Sthorpej 	 * the biggest component sector.
40877d85de2Sthorpej 	 */
40977d85de2Sthorpej 	if ((cs->sc_ileave > 0) &&
41077d85de2Sthorpej 	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
41177d85de2Sthorpej #ifdef DEBUG
41277d85de2Sthorpej 		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
41386373f8cSchristos 			printf("%s: interleave must be at least %d\n",
4145b39541eSthorpej 			    cs->sc_xname, (maxsecsize / DEV_BSIZE));
41577d85de2Sthorpej #endif
416aa8b5ebdSenami 		error = EINVAL;
417aa8b5ebdSenami 		goto out;
41877d85de2Sthorpej 	}
41977d85de2Sthorpej 
4206f9db1eeShpeyerl 	/*
4216f9db1eeShpeyerl 	 * If uniform interleave is desired set all sizes to that of
4226f9db1eeShpeyerl 	 * the smallest component.
4236f9db1eeShpeyerl 	 */
4240272b2abSthorpej 	if (cs->sc_flags & CCDF_UNIFORM) {
4256f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
4266f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
4276f9db1eeShpeyerl 			ci->ci_size = minsize;
428732dd94aSthorpej 
4296f9db1eeShpeyerl 		cs->sc_size = cs->sc_nccdisks * minsize;
4306f9db1eeShpeyerl 	}
43177d85de2Sthorpej 
4326f9db1eeShpeyerl 	/*
43377d85de2Sthorpej 	 * Construct the interleave table.
4346f9db1eeShpeyerl 	 */
4350272b2abSthorpej 	ccdinterleave(cs);
43677d85de2Sthorpej 
43777d85de2Sthorpej 	/*
43877d85de2Sthorpej 	 * Create pseudo-geometry based on 1MB cylinders.  It's
43977d85de2Sthorpej 	 * pretty close.
44077d85de2Sthorpej 	 */
44177d85de2Sthorpej 	ccg->ccg_secsize = DEV_BSIZE;
44289d4987eSthorpej 	ccg->ccg_ntracks = 1;
44377d85de2Sthorpej 	ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize);
44477d85de2Sthorpej 	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
44577d85de2Sthorpej 
446026ad668Sjnemeth         dg = &cs->sc_dkdev.dk_geom;
447026ad668Sjnemeth         memset(dg, 0, sizeof(*dg));
448026ad668Sjnemeth 	dg->dg_secperunit = cs->sc_size;
449026ad668Sjnemeth 	dg->dg_secsize = ccg->ccg_secsize;
450026ad668Sjnemeth 	dg->dg_nsectors = ccg->ccg_nsectors;
451026ad668Sjnemeth 	dg->dg_ntracks = ccg->ccg_ntracks;
452026ad668Sjnemeth 	dg->dg_ncylinders = ccg->ccg_ncylinders;
453026ad668Sjnemeth 
4547d706cf7Ssborrill 	if (cs->sc_ileave > 0)
4557d706cf7Ssborrill 	        aprint_normal("%s: Interleaving %d component%s "
4567d706cf7Ssborrill 	            "(%d block interleave)\n", cs->sc_xname,
4577d706cf7Ssborrill         	    cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""),
4587d706cf7Ssborrill         	    cs->sc_ileave);
4597d706cf7Ssborrill 	else
4607d706cf7Ssborrill 	        aprint_normal("%s: Concatenating %d component%s\n",
4617d706cf7Ssborrill 	            cs->sc_xname,
4627d706cf7Ssborrill         	    cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""));
4637d706cf7Ssborrill 	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
4647d706cf7Ssborrill 		ci = &cs->sc_cinfo[ix];
4657d706cf7Ssborrill 		aprint_normal("%s: %s (%ju blocks)\n", cs->sc_xname,
4667d706cf7Ssborrill 		    ci->ci_path, (uintmax_t)ci->ci_size);
4677d706cf7Ssborrill 	}
4687d706cf7Ssborrill 	aprint_normal("%s: total %ju blocks\n", cs->sc_xname, cs->sc_size);
4697d706cf7Ssborrill 
470481ff5acSad 	/*
471481ff5acSad 	 * Create thread to handle deferred I/O.
472481ff5acSad 	 */
473481ff5acSad 	cs->sc_zap = false;
474481ff5acSad 	error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, ccdthread,
475481ff5acSad 	    cs, &cs->sc_thread, "%s", cs->sc_xname);
476481ff5acSad 	if (error) {
477481ff5acSad 		printf("ccdinit: can't create thread: %d\n", error);
478481ff5acSad 		goto out;
479481ff5acSad 	}
480481ff5acSad 
481481ff5acSad 	/*
482481ff5acSad 	 * Only now that everything is set up can we enable the device.
483481ff5acSad 	 */
484481ff5acSad 	mutex_enter(cs->sc_iolock);
48577d85de2Sthorpej 	cs->sc_flags |= CCDF_INITED;
486481ff5acSad 	mutex_exit(cs->sc_iolock);
487481ff5acSad 	kmem_free(tmppath, MAXPATHLEN);
48877d85de2Sthorpej 	return (0);
489aa8b5ebdSenami 
490aa8b5ebdSenami  out:
491481ff5acSad 	for (ix = 0; ix < path_alloced; ix++) {
492481ff5acSad 		kmem_free(cs->sc_cinfo[ix].ci_path,
493481ff5acSad 		    cs->sc_cinfo[ix].ci_pathlen);
494481ff5acSad 	}
495481ff5acSad 	kmem_free(cs->sc_cinfo, cs->sc_nccdisks * sizeof(struct ccdcinfo));
496481ff5acSad 	kmem_free(tmppath, MAXPATHLEN);
497aa8b5ebdSenami 	return (error);
4986f9db1eeShpeyerl }
4996f9db1eeShpeyerl 
50077d85de2Sthorpej static void
501b8d38500Sthorpej ccdinterleave(struct ccd_softc *cs)
5026f9db1eeShpeyerl {
503169ac5b3Saugustss 	struct ccdcinfo *ci, *smallci;
504169ac5b3Saugustss 	struct ccdiinfo *ii;
505169ac5b3Saugustss 	daddr_t bn, lbn;
506169ac5b3Saugustss 	int ix;
5076f9db1eeShpeyerl 	u_long size;
5086f9db1eeShpeyerl 
5096f9db1eeShpeyerl #ifdef DEBUG
5103214723fShpeyerl 	if (ccddebug & CCDB_INIT)
51186373f8cSchristos 		printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave);
5126f9db1eeShpeyerl #endif
5136f9db1eeShpeyerl 	/*
5146f9db1eeShpeyerl 	 * Allocate an interleave table.
5156f9db1eeShpeyerl 	 * Chances are this is too big, but we don't care.
5166f9db1eeShpeyerl 	 */
5176f9db1eeShpeyerl 	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
518481ff5acSad 	cs->sc_itable = kmem_zalloc(size, KM_SLEEP);
51977d85de2Sthorpej 
5206f9db1eeShpeyerl 	/*
5216f9db1eeShpeyerl 	 * Trivial case: no interleave (actually interleave of disk size).
52277d85de2Sthorpej 	 * Each table entry represents a single component in its entirety.
5236f9db1eeShpeyerl 	 */
5246f9db1eeShpeyerl 	if (cs->sc_ileave == 0) {
5256f9db1eeShpeyerl 		bn = 0;
5266f9db1eeShpeyerl 		ii = cs->sc_itable;
52777d85de2Sthorpej 
5286f9db1eeShpeyerl 		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
52989d4987eSthorpej 			/* Allocate space for ii_index. */
530481ff5acSad 			ii->ii_indexsz = sizeof(int);
531481ff5acSad 			ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP);
5326f9db1eeShpeyerl 			ii->ii_ndisk = 1;
5336f9db1eeShpeyerl 			ii->ii_startblk = bn;
5346f9db1eeShpeyerl 			ii->ii_startoff = 0;
5356f9db1eeShpeyerl 			ii->ii_index[0] = ix;
5366f9db1eeShpeyerl 			bn += cs->sc_cinfo[ix].ci_size;
5376f9db1eeShpeyerl 			ii++;
5386f9db1eeShpeyerl 		}
5396f9db1eeShpeyerl 		ii->ii_ndisk = 0;
5406f9db1eeShpeyerl #ifdef DEBUG
5413214723fShpeyerl 		if (ccddebug & CCDB_INIT)
5426f9db1eeShpeyerl 			printiinfo(cs->sc_itable);
5436f9db1eeShpeyerl #endif
54477d85de2Sthorpej 		return;
5456f9db1eeShpeyerl 	}
54677d85de2Sthorpej 
5476f9db1eeShpeyerl 	/*
5486f9db1eeShpeyerl 	 * The following isn't fast or pretty; it doesn't have to be.
5496f9db1eeShpeyerl 	 */
5506f9db1eeShpeyerl 	size = 0;
5516f9db1eeShpeyerl 	bn = lbn = 0;
5526f9db1eeShpeyerl 	for (ii = cs->sc_itable; ; ii++) {
55377d85de2Sthorpej 		/* Allocate space for ii_index. */
554481ff5acSad 		ii->ii_indexsz = sizeof(int) * cs->sc_nccdisks;
555481ff5acSad 		ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP);
55677d85de2Sthorpej 
5576f9db1eeShpeyerl 		/*
5586f9db1eeShpeyerl 		 * Locate the smallest of the remaining components
5596f9db1eeShpeyerl 		 */
5606f9db1eeShpeyerl 		smallci = NULL;
5616f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
5626f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
5636f9db1eeShpeyerl 			if (ci->ci_size > size &&
5646f9db1eeShpeyerl 			    (smallci == NULL ||
5656f9db1eeShpeyerl 			     ci->ci_size < smallci->ci_size))
5666f9db1eeShpeyerl 				smallci = ci;
56777d85de2Sthorpej 
5686f9db1eeShpeyerl 		/*
5696f9db1eeShpeyerl 		 * Nobody left, all done
5706f9db1eeShpeyerl 		 */
5716f9db1eeShpeyerl 		if (smallci == NULL) {
5726f9db1eeShpeyerl 			ii->ii_ndisk = 0;
5736f9db1eeShpeyerl 			break;
5746f9db1eeShpeyerl 		}
57577d85de2Sthorpej 
5766f9db1eeShpeyerl 		/*
5776f9db1eeShpeyerl 		 * Record starting logical block and component offset
5786f9db1eeShpeyerl 		 */
5796f9db1eeShpeyerl 		ii->ii_startblk = bn / cs->sc_ileave;
5806f9db1eeShpeyerl 		ii->ii_startoff = lbn;
58177d85de2Sthorpej 
5826f9db1eeShpeyerl 		/*
5836f9db1eeShpeyerl 		 * Determine how many disks take part in this interleave
5846f9db1eeShpeyerl 		 * and record their indices.
5856f9db1eeShpeyerl 		 */
5866f9db1eeShpeyerl 		ix = 0;
5876f9db1eeShpeyerl 		for (ci = cs->sc_cinfo;
5886f9db1eeShpeyerl 		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
5896f9db1eeShpeyerl 			if (ci->ci_size >= smallci->ci_size)
5906f9db1eeShpeyerl 				ii->ii_index[ix++] = ci - cs->sc_cinfo;
5916f9db1eeShpeyerl 		ii->ii_ndisk = ix;
5926f9db1eeShpeyerl 		bn += ix * (smallci->ci_size - size);
5936f9db1eeShpeyerl 		lbn = smallci->ci_size / cs->sc_ileave;
5946f9db1eeShpeyerl 		size = smallci->ci_size;
5956f9db1eeShpeyerl 	}
5966f9db1eeShpeyerl #ifdef DEBUG
5973214723fShpeyerl 	if (ccddebug & CCDB_INIT)
5986f9db1eeShpeyerl 		printiinfo(cs->sc_itable);
5996f9db1eeShpeyerl #endif
6006f9db1eeShpeyerl }
6016f9db1eeShpeyerl 
60277d85de2Sthorpej /* ARGSUSED */
603b8d38500Sthorpej static int
604168cd830Schristos ccdopen(dev_t dev, int flags, int fmt, struct lwp *l)
6056f9db1eeShpeyerl {
6066f9db1eeShpeyerl 	int unit = ccdunit(dev);
60777d85de2Sthorpej 	struct ccd_softc *cs;
60877d85de2Sthorpej 	struct disklabel *lp;
609b8dcfbd3Sthorpej 	int error = 0, part, pmask;
6106f9db1eeShpeyerl 
6116f9db1eeShpeyerl #ifdef DEBUG
6123214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
61308ebead9Scegger 		printf("ccdopen(0x%"PRIx64", 0x%x)\n", dev, flags);
6146f9db1eeShpeyerl #endif
615ef781023Schristos 	if ((cs = ccdget(unit, 1)) == NULL)
616b08b65c6Schristos 		return ENXIO;
61777d85de2Sthorpej 
618481ff5acSad 	mutex_enter(&cs->sc_dvlock);
619b8dcfbd3Sthorpej 
6205b39541eSthorpej 	lp = cs->sc_dkdev.dk_label;
62177d85de2Sthorpej 
62277d85de2Sthorpej 	part = DISKPART(dev);
62377d85de2Sthorpej 	pmask = (1 << part);
62477d85de2Sthorpej 
625b8dcfbd3Sthorpej 	/*
626b8dcfbd3Sthorpej 	 * If we're initialized, check to see if there are any other
627b8dcfbd3Sthorpej 	 * open partitions.  If not, then it's safe to update
6284aef0509Sthorpej 	 * the in-core disklabel.  Only read the disklabel if it is
6294aef0509Sthorpej 	 * not already valid.
630b8dcfbd3Sthorpej 	 */
6314aef0509Sthorpej 	if ((cs->sc_flags & (CCDF_INITED|CCDF_VLABEL)) == CCDF_INITED &&
6324aef0509Sthorpej 	    cs->sc_dkdev.dk_openmask == 0)
633b8dcfbd3Sthorpej 		ccdgetdisklabel(dev);
634b8dcfbd3Sthorpej 
63577d85de2Sthorpej 	/* Check that the partition exists. */
63691bd533cSthorpej 	if (part != RAW_PART) {
63791bd533cSthorpej 		if (((cs->sc_flags & CCDF_INITED) == 0) ||
638bb608224Sthorpej 		    ((part >= lp->d_npartitions) ||
639b8dcfbd3Sthorpej 		     (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
640b8dcfbd3Sthorpej 			error = ENXIO;
641b8dcfbd3Sthorpej 			goto done;
642b8dcfbd3Sthorpej 		}
64391bd533cSthorpej 	}
64477d85de2Sthorpej 
64577d85de2Sthorpej 	/* Prevent our unit from being unconfigured while open. */
64677d85de2Sthorpej 	switch (fmt) {
64777d85de2Sthorpej 	case S_IFCHR:
64877d85de2Sthorpej 		cs->sc_dkdev.dk_copenmask |= pmask;
64977d85de2Sthorpej 		break;
65077d85de2Sthorpej 
65177d85de2Sthorpej 	case S_IFBLK:
65277d85de2Sthorpej 		cs->sc_dkdev.dk_bopenmask |= pmask;
65377d85de2Sthorpej 		break;
65477d85de2Sthorpej 	}
65577d85de2Sthorpej 	cs->sc_dkdev.dk_openmask =
65677d85de2Sthorpej 	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
65777d85de2Sthorpej 
658b8dcfbd3Sthorpej  done:
659481ff5acSad 	mutex_exit(&cs->sc_dvlock);
66049c5d29eSthorpej 	return (error);
6616f9db1eeShpeyerl }
6626f9db1eeShpeyerl 
66377d85de2Sthorpej /* ARGSUSED */
664b8d38500Sthorpej static int
665168cd830Schristos ccdclose(dev_t dev, int flags, int fmt, struct lwp *l)
666a44a2765Scgd {
66777d85de2Sthorpej 	int unit = ccdunit(dev);
66877d85de2Sthorpej 	struct ccd_softc *cs;
6699abeea58Sad 	int part;
67077d85de2Sthorpej 
671a44a2765Scgd #ifdef DEBUG
672a44a2765Scgd 	if (ccddebug & CCDB_FOLLOW)
67308ebead9Scegger 		printf("ccdclose(0x%"PRIx64", 0x%x)\n", dev, flags);
674a44a2765Scgd #endif
67577d85de2Sthorpej 
676ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
677b08b65c6Schristos 		return ENXIO;
678b8dcfbd3Sthorpej 
679481ff5acSad 	mutex_enter(&cs->sc_dvlock);
680b8dcfbd3Sthorpej 
68177d85de2Sthorpej 	part = DISKPART(dev);
68277d85de2Sthorpej 
68377d85de2Sthorpej 	/* ...that much closer to allowing unconfiguration... */
68477d85de2Sthorpej 	switch (fmt) {
68577d85de2Sthorpej 	case S_IFCHR:
68677d85de2Sthorpej 		cs->sc_dkdev.dk_copenmask &= ~(1 << part);
68777d85de2Sthorpej 		break;
68877d85de2Sthorpej 
68977d85de2Sthorpej 	case S_IFBLK:
69077d85de2Sthorpej 		cs->sc_dkdev.dk_bopenmask &= ~(1 << part);
69177d85de2Sthorpej 		break;
69277d85de2Sthorpej 	}
69377d85de2Sthorpej 	cs->sc_dkdev.dk_openmask =
69477d85de2Sthorpej 	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
69577d85de2Sthorpej 
6964aef0509Sthorpej 	if (cs->sc_dkdev.dk_openmask == 0) {
6974aef0509Sthorpej 		if ((cs->sc_flags & CCDF_KLABEL) == 0)
6984aef0509Sthorpej 			cs->sc_flags &= ~CCDF_VLABEL;
6994aef0509Sthorpej 	}
7004aef0509Sthorpej 
701481ff5acSad 	mutex_exit(&cs->sc_dvlock);
702a44a2765Scgd 	return (0);
703a44a2765Scgd }
704a44a2765Scgd 
705481ff5acSad static void
706481ff5acSad ccdthread(void *cookie)
707481ff5acSad {
70879f4a4f8Shannken 	int error;
709481ff5acSad 	struct ccd_softc *cs;
71079f4a4f8Shannken 	struct buf *bp;
711481ff5acSad 
712481ff5acSad 	cs = cookie;
713481ff5acSad 
714481ff5acSad #ifdef DEBUG
715481ff5acSad  	if (ccddebug & CCDB_FOLLOW)
716481ff5acSad  		printf("ccdthread: hello\n");
717481ff5acSad #endif
718481ff5acSad 
719481ff5acSad 	mutex_enter(cs->sc_iolock);
720481ff5acSad 	while (__predict_true(!cs->sc_zap)) {
72179f4a4f8Shannken 		bp = bufq_get(cs->sc_bufq);
72279f4a4f8Shannken 		if (bp == NULL) {
723481ff5acSad 			/* Nothing to do. */
724481ff5acSad 			cv_wait(&cs->sc_push, cs->sc_iolock);
725481ff5acSad 			continue;
726481ff5acSad 		}
727481ff5acSad #ifdef DEBUG
728481ff5acSad  		if (ccddebug & CCDB_FOLLOW)
729481ff5acSad  			printf("ccdthread: dispatching I/O\n");
730481ff5acSad #endif
73179f4a4f8Shannken 		error = ccdstart(cs, bp, PR_WAITOK);
73279f4a4f8Shannken 		KASSERT(error == 0);
733481ff5acSad 		mutex_enter(cs->sc_iolock);
734481ff5acSad 	}
735481ff5acSad 	cs->sc_thread = NULL;
736481ff5acSad 	mutex_exit(cs->sc_iolock);
737481ff5acSad #ifdef DEBUG
738481ff5acSad  	if (ccddebug & CCDB_FOLLOW)
739481ff5acSad  		printf("ccdthread: goodbye\n");
740481ff5acSad #endif
741481ff5acSad 	kthread_exit(0);
742481ff5acSad }
743481ff5acSad 
744b8d38500Sthorpej static void
745b8d38500Sthorpej ccdstrategy(struct buf *bp)
7466f9db1eeShpeyerl {
747169ac5b3Saugustss 	int unit = ccdunit(bp->b_dev);
748b08b65c6Schristos 	struct ccd_softc *cs;
749ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
750b08b65c6Schristos 		return;
7516f9db1eeShpeyerl 
752481ff5acSad 	/* Must be open or reading label. */
753481ff5acSad 	KASSERT(cs->sc_dkdev.dk_openmask != 0 ||
754481ff5acSad 	    (cs->sc_flags & CCDF_RLABEL) != 0);
755481ff5acSad 
756481ff5acSad 	mutex_enter(cs->sc_iolock);
757481ff5acSad 	/* Synchronize with device init/uninit. */
758481ff5acSad 	if (__predict_false((cs->sc_flags & CCDF_INITED) == 0)) {
759481ff5acSad 		mutex_exit(cs->sc_iolock);
760c6aa25bfSthorpej #ifdef DEBUG
761c6aa25bfSthorpej  		if (ccddebug & CCDB_FOLLOW)
762c6aa25bfSthorpej  			printf("ccdstrategy: unit %d: not inited\n", unit);
763c6aa25bfSthorpej #endif
7646f9db1eeShpeyerl  		bp->b_error = ENXIO;
765481ff5acSad  		bp->b_resid = bp->b_bcount;
766481ff5acSad  		biodone(bp);
767481ff5acSad 		return;
7686f9db1eeShpeyerl 	}
76977d85de2Sthorpej 
77079f4a4f8Shannken 	if (ccdstart(cs, bp, PR_NOWAIT) != 0) {
771481ff5acSad 		/* Defer to thread if system is low on memory. */
772481ff5acSad 		bufq_put(cs->sc_bufq, bp);
77379f4a4f8Shannken 		cv_broadcast(&cs->sc_push);
774481ff5acSad 		mutex_exit(cs->sc_iolock);
775481ff5acSad 	}
776481ff5acSad }
777481ff5acSad 
77879f4a4f8Shannken static int
77979f4a4f8Shannken ccdstart(struct ccd_softc *cs, struct buf *bp, int wait)
780481ff5acSad {
781481ff5acSad 	daddr_t blkno;
782481ff5acSad 	int wlabel;
783481ff5acSad 	struct disklabel *lp;
784481ff5acSad 	long bcount, rcount;
785481ff5acSad 	struct ccdbuf *cbp;
786481ff5acSad 	char *addr;
787481ff5acSad 	daddr_t bn;
788481ff5acSad 	vnode_t *vp;
78979f4a4f8Shannken 	SIMPLEQ_HEAD(, ccdbuf) cbufq;
790481ff5acSad 
791481ff5acSad 	KASSERT(mutex_owned(cs->sc_iolock));
792481ff5acSad 	KASSERT(bp != NULL);
793481ff5acSad 
794ba576b71Smlelstv 	disk_busy(&cs->sc_dkdev);
795ba576b71Smlelstv 
796481ff5acSad #ifdef DEBUG
797481ff5acSad 	if (ccddebug & CCDB_FOLLOW)
798481ff5acSad 		printf("ccdstart(%s, %p)\n", cs->sc_xname, bp);
799481ff5acSad #endif
800481ff5acSad 
80177d85de2Sthorpej 	/* If it's a nil transfer, wake up the top half now. */
80277d85de2Sthorpej 	if (bp->b_bcount == 0)
8036f9db1eeShpeyerl 		goto done;
80477d85de2Sthorpej 
8055b39541eSthorpej 	lp = cs->sc_dkdev.dk_label;
806b8dcfbd3Sthorpej 
80777d85de2Sthorpej 	/*
808ab0109adSthorpej 	 * Do bounds checking and adjust transfer.  If there's an
8092f25411dSthorpej 	 * error, the bounds check will flag that for us.  Convert
8102f25411dSthorpej 	 * the partition relative block number to an absolute.
81177d85de2Sthorpej 	 */
8122f25411dSthorpej 	blkno = bp->b_blkno;
81377d85de2Sthorpej 	wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
8142f25411dSthorpej 	if (DISKPART(bp->b_dev) != RAW_PART) {
815e43fecb2Sthorpej 		if (bounds_check_with_label(&cs->sc_dkdev, bp, wlabel) <= 0)
8166f9db1eeShpeyerl 			goto done;
8172f25411dSthorpej 		blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
8182f25411dSthorpej 	}
819481ff5acSad 	mutex_exit(cs->sc_iolock);
8202f25411dSthorpej 	bp->b_rawblkno = blkno;
82177d85de2Sthorpej 
82279f4a4f8Shannken 	/* Allocate the component buffers. */
82379f4a4f8Shannken 	SIMPLEQ_INIT(&cbufq);
8242f25411dSthorpej 	bp->b_resid = bp->b_bcount;
8252f25411dSthorpej 	bn = bp->b_rawblkno;
8263214723fShpeyerl 	addr = bp->b_data;
8276f9db1eeShpeyerl 	for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
82879f4a4f8Shannken 		cbp = ccdbuffer(cs, bp, bn, addr, bcount, wait);
82979f4a4f8Shannken 		KASSERT(cbp != NULL || wait == PR_NOWAIT);
83079f4a4f8Shannken 		if (cbp == NULL) {
83179f4a4f8Shannken 			while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) {
83279f4a4f8Shannken 				SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q);
83379f4a4f8Shannken 				CCD_PUTBUF(cbp);
83479f4a4f8Shannken 			}
83579f4a4f8Shannken 			mutex_enter(cs->sc_iolock);
83679f4a4f8Shannken 			disk_unbusy(&cs->sc_dkdev, 0, 0);
83779f4a4f8Shannken 			return ENOMEM;
83879f4a4f8Shannken 		}
83979f4a4f8Shannken 		SIMPLEQ_INSERT_TAIL(&cbufq, cbp, cb_q);
840092c2019Sthorpej 		rcount = cbp->cb_buf.b_bcount;
841d272bb00Sthorpej 		bn += btodb(rcount);
842d272bb00Sthorpej 		addr += rcount;
84379f4a4f8Shannken 	}
84479f4a4f8Shannken 
84579f4a4f8Shannken 	/* All buffers set up, now fire off the requests. */
84679f4a4f8Shannken 	while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) {
84779f4a4f8Shannken 		SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q);
848481ff5acSad 		vp = cbp->cb_buf.b_vp;
849481ff5acSad 		if ((cbp->cb_buf.b_flags & B_READ) == 0) {
850e225b7bdSrmind 			mutex_enter(vp->v_interlock);
851481ff5acSad 			vp->v_numoutput++;
852e225b7bdSrmind 			mutex_exit(vp->v_interlock);
853d272bb00Sthorpej 		}
854481ff5acSad 		(void)VOP_STRATEGY(vp, &cbp->cb_buf);
855481ff5acSad 	}
85679f4a4f8Shannken 	return 0;
857d272bb00Sthorpej 
858481ff5acSad  done:
859481ff5acSad 	disk_unbusy(&cs->sc_dkdev, 0, 0);
860481ff5acSad 	cv_broadcast(&cs->sc_stop);
861481ff5acSad 	cv_broadcast(&cs->sc_push);
862481ff5acSad 	mutex_exit(cs->sc_iolock);
863481ff5acSad 	bp->b_resid = bp->b_bcount;
864481ff5acSad 	biodone(bp);
86579f4a4f8Shannken 	return 0;
8662f25411dSthorpej }
8676f9db1eeShpeyerl 
8686f9db1eeShpeyerl /*
8696f9db1eeShpeyerl  * Build a component buffer header.
8706f9db1eeShpeyerl  */
871092c2019Sthorpej static struct ccdbuf *
87253524e44Schristos ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, void *addr,
87379f4a4f8Shannken     long bcount, int wait)
8746f9db1eeShpeyerl {
875169ac5b3Saugustss 	struct ccdcinfo *ci;
876169ac5b3Saugustss 	struct ccdbuf *cbp;
877169ac5b3Saugustss 	daddr_t cbn, cboff;
878169ac5b3Saugustss 	u_int64_t cbc;
8797770d7c4Sthorpej 	int ccdisk;
8806f9db1eeShpeyerl 
8816f9db1eeShpeyerl #ifdef DEBUG
8823214723fShpeyerl 	if (ccddebug & CCDB_IO)
8834e0e5333Skleink 		printf("ccdbuffer(%p, %p, %" PRId64 ", %p, %ld)\n",
8846f9db1eeShpeyerl 		       cs, bp, bn, addr, bcount);
8856f9db1eeShpeyerl #endif
8866f9db1eeShpeyerl 	/*
8876f9db1eeShpeyerl 	 * Determine which component bn falls in.
8886f9db1eeShpeyerl 	 */
8896f9db1eeShpeyerl 	cbn = bn;
8906f9db1eeShpeyerl 	cboff = 0;
89177d85de2Sthorpej 
8926f9db1eeShpeyerl 	/*
8936f9db1eeShpeyerl 	 * Serially concatenated
8946f9db1eeShpeyerl 	 */
8956f9db1eeShpeyerl 	if (cs->sc_ileave == 0) {
896169ac5b3Saugustss 		daddr_t sblk;
8976f9db1eeShpeyerl 
8986f9db1eeShpeyerl 		sblk = 0;
8997770d7c4Sthorpej 		for (ccdisk = 0, ci = &cs->sc_cinfo[ccdisk];
9007770d7c4Sthorpej 		    cbn >= sblk + ci->ci_size;
9017770d7c4Sthorpej 		    ccdisk++, ci = &cs->sc_cinfo[ccdisk])
9026f9db1eeShpeyerl 			sblk += ci->ci_size;
9036f9db1eeShpeyerl 		cbn -= sblk;
9046f9db1eeShpeyerl 	}
9056f9db1eeShpeyerl 	/*
9066f9db1eeShpeyerl 	 * Interleaved
9076f9db1eeShpeyerl 	 */
9086f9db1eeShpeyerl 	else {
909169ac5b3Saugustss 		struct ccdiinfo *ii;
9107770d7c4Sthorpej 		int off;
9116f9db1eeShpeyerl 
9126f9db1eeShpeyerl 		cboff = cbn % cs->sc_ileave;
9136f9db1eeShpeyerl 		cbn /= cs->sc_ileave;
9146f9db1eeShpeyerl 		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
9156f9db1eeShpeyerl 			if (ii->ii_startblk > cbn)
9166f9db1eeShpeyerl 				break;
9176f9db1eeShpeyerl 		ii--;
9186f9db1eeShpeyerl 		off = cbn - ii->ii_startblk;
9196f9db1eeShpeyerl 		if (ii->ii_ndisk == 1) {
9206f9db1eeShpeyerl 			ccdisk = ii->ii_index[0];
9216f9db1eeShpeyerl 			cbn = ii->ii_startoff + off;
9226f9db1eeShpeyerl 		} else {
9236f9db1eeShpeyerl 			ccdisk = ii->ii_index[off % ii->ii_ndisk];
9246f9db1eeShpeyerl 			cbn = ii->ii_startoff + off / ii->ii_ndisk;
9256f9db1eeShpeyerl 		}
9266f9db1eeShpeyerl 		cbn *= cs->sc_ileave;
9276f9db1eeShpeyerl 		ci = &cs->sc_cinfo[ccdisk];
9286f9db1eeShpeyerl 	}
92977d85de2Sthorpej 
9306f9db1eeShpeyerl 	/*
9316f9db1eeShpeyerl 	 * Fill in the component buf structure.
9326f9db1eeShpeyerl 	 */
93379f4a4f8Shannken 	cbp = CCD_GETBUF(wait);
93479f4a4f8Shannken 	if (cbp == NULL)
93579f4a4f8Shannken 		return NULL;
9364a780c9aSad 	buf_init(&cbp->cb_buf);
9374a780c9aSad 	cbp->cb_buf.b_flags = bp->b_flags;
9384a780c9aSad 	cbp->cb_buf.b_oflags = bp->b_oflags;
9394a780c9aSad 	cbp->cb_buf.b_cflags = bp->b_cflags;
940d318abdbSchristos 	cbp->cb_buf.b_iodone = ccdiodone;
941f694af67Scgd 	cbp->cb_buf.b_proc = bp->b_proc;
9423db4e2acShannken 	cbp->cb_buf.b_dev = ci->ci_dev;
943f694af67Scgd 	cbp->cb_buf.b_blkno = cbn + cboff;
944f694af67Scgd 	cbp->cb_buf.b_data = addr;
94577d85de2Sthorpej 	cbp->cb_buf.b_vp = ci->ci_vp;
946e225b7bdSrmind 	cbp->cb_buf.b_objlock = ci->ci_vp->v_interlock;
9476f9db1eeShpeyerl 	if (cs->sc_ileave == 0)
948902855d6Sthorpej 		cbc = dbtob((u_int64_t)(ci->ci_size - cbn));
9496f9db1eeShpeyerl 	else
950902855d6Sthorpej 		cbc = dbtob((u_int64_t)(cs->sc_ileave - cboff));
951902855d6Sthorpej 	cbp->cb_buf.b_bcount = cbc < bcount ? cbc : bcount;
952f694af67Scgd 
9536f9db1eeShpeyerl 	/*
954f694af67Scgd 	 * context for ccdiodone
9556f9db1eeShpeyerl 	 */
956f694af67Scgd 	cbp->cb_obp = bp;
957c6aa25bfSthorpej 	cbp->cb_sc = cs;
9587770d7c4Sthorpej 	cbp->cb_comp = ccdisk;
959f694af67Scgd 
9607266a959Syamt 	BIO_COPYPRIO(&cbp->cb_buf, bp);
9617266a959Syamt 
9626f9db1eeShpeyerl #ifdef DEBUG
9633214723fShpeyerl 	if (ccddebug & CCDB_IO)
96408ebead9Scegger 		printf(" dev 0x%"PRIx64"(u%lu): cbp %p bn %" PRId64 " addr %p"
96522399b45Syamt 		       " bcnt %d\n",
966fefcc128Smjacob 		    ci->ci_dev, (unsigned long) (ci-cs->sc_cinfo), cbp,
967fefcc128Smjacob 		    cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
968fefcc128Smjacob 		    cbp->cb_buf.b_bcount);
9696f9db1eeShpeyerl #endif
970092c2019Sthorpej 
971092c2019Sthorpej 	return (cbp);
9726f9db1eeShpeyerl }
9736f9db1eeShpeyerl 
9746f9db1eeShpeyerl /*
97577d85de2Sthorpej  * Called at interrupt time.
9766f9db1eeShpeyerl  * Mark the component as done and if all components are done,
9776f9db1eeShpeyerl  * take a ccd interrupt.
9786f9db1eeShpeyerl  */
979b8d38500Sthorpej static void
980b8d38500Sthorpej ccdiodone(struct buf *vbp)
9816f9db1eeShpeyerl {
982d318abdbSchristos 	struct ccdbuf *cbp = (struct ccdbuf *) vbp;
983c6aa25bfSthorpej 	struct buf *bp = cbp->cb_obp;
984c6aa25bfSthorpej 	struct ccd_softc *cs = cbp->cb_sc;
985481ff5acSad 	int count;
9866f9db1eeShpeyerl 
9876f9db1eeShpeyerl #ifdef DEBUG
9883214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
98986373f8cSchristos 		printf("ccdiodone(%p)\n", cbp);
9903214723fShpeyerl 	if (ccddebug & CCDB_IO) {
99122399b45Syamt 		printf("ccdiodone: bp %p bcount %d resid %d\n",
9926f9db1eeShpeyerl 		       bp, bp->b_bcount, bp->b_resid);
99308ebead9Scegger 		printf(" dev 0x%"PRIx64"(u%d), cbp %p bn %" PRId64 " addr %p"
99422399b45Syamt 		       " bcnt %d\n",
995f694af67Scgd 		       cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
996f694af67Scgd 		       cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
997f694af67Scgd 		       cbp->cb_buf.b_bcount);
9986f9db1eeShpeyerl 	}
9996f9db1eeShpeyerl #endif
10006f9db1eeShpeyerl 
1001eb171eaaSad 	if (cbp->cb_buf.b_error != 0) {
1002eb171eaaSad 		bp->b_error = cbp->cb_buf.b_error;
100300172c10Sthorpej 		printf("%s: error %d on component %d\n",
100400172c10Sthorpej 		       cs->sc_xname, bp->b_error, cbp->cb_comp);
10056f9db1eeShpeyerl 	}
1006f694af67Scgd 	count = cbp->cb_buf.b_bcount;
10074a780c9aSad 	buf_destroy(&cbp->cb_buf);
1008dd5ae858Sthorpej 	CCD_PUTBUF(cbp);
10096f9db1eeShpeyerl 
10106f9db1eeShpeyerl 	/*
10116f9db1eeShpeyerl 	 * If all done, "interrupt".
10126f9db1eeShpeyerl 	 */
1013481ff5acSad 	mutex_enter(cs->sc_iolock);
10146f9db1eeShpeyerl 	bp->b_resid -= count;
10156f9db1eeShpeyerl 	if (bp->b_resid < 0)
10166f9db1eeShpeyerl 		panic("ccdiodone: count");
1017481ff5acSad 	if (bp->b_resid == 0) {
1018481ff5acSad 		/*
1019481ff5acSad 		 * Request is done for better or worse, wakeup the top half.
1020481ff5acSad 		 */
1021481ff5acSad 		if (bp->b_error != 0)
1022481ff5acSad 			bp->b_resid = bp->b_bcount;
1023481ff5acSad 		disk_unbusy(&cs->sc_dkdev, (bp->b_bcount - bp->b_resid),
1024481ff5acSad 		    (bp->b_flags & B_READ));
1025481ff5acSad 		if (!disk_isbusy(&cs->sc_dkdev)) {
1026481ff5acSad 			if (bufq_peek(cs->sc_bufq) != NULL) {
1027481ff5acSad 				cv_broadcast(&cs->sc_push);
1028481ff5acSad 			}
1029481ff5acSad 			cv_broadcast(&cs->sc_stop);
1030481ff5acSad 		}
1031481ff5acSad 		mutex_exit(cs->sc_iolock);
1032481ff5acSad 		biodone(bp);
1033481ff5acSad 	} else
1034481ff5acSad 		mutex_exit(cs->sc_iolock);
10356f9db1eeShpeyerl }
10366f9db1eeShpeyerl 
103777d85de2Sthorpej /* ARGSUSED */
1038b8d38500Sthorpej static int
1039168cd830Schristos ccdread(dev_t dev, struct uio *uio, int flags)
10403214723fShpeyerl {
104177d85de2Sthorpej 	int unit = ccdunit(dev);
104277d85de2Sthorpej 	struct ccd_softc *cs;
10433214723fShpeyerl 
10443214723fShpeyerl #ifdef DEBUG
10453214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
104608ebead9Scegger 		printf("ccdread(0x%"PRIx64", %p)\n", dev, uio);
10473214723fShpeyerl #endif
1048ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
1049b08b65c6Schristos 		return 0;
105077d85de2Sthorpej 
1051481ff5acSad 	/* Unlocked advisory check, ccdstrategy check is synchronous. */
105277d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
105377d85de2Sthorpej 		return (ENXIO);
105477d85de2Sthorpej 
10553214723fShpeyerl 	return (physio(ccdstrategy, NULL, dev, B_READ, minphys, uio));
10563214723fShpeyerl }
10573214723fShpeyerl 
105877d85de2Sthorpej /* ARGSUSED */
1059b8d38500Sthorpej static int
1060168cd830Schristos ccdwrite(dev_t dev, struct uio *uio, int flags)
10613214723fShpeyerl {
106277d85de2Sthorpej 	int unit = ccdunit(dev);
106377d85de2Sthorpej 	struct ccd_softc *cs;
10643214723fShpeyerl 
10653214723fShpeyerl #ifdef DEBUG
10663214723fShpeyerl 	if (ccddebug & CCDB_FOLLOW)
106708ebead9Scegger 		printf("ccdwrite(0x%"PRIx64", %p)\n", dev, uio);
10683214723fShpeyerl #endif
1069ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
1070b08b65c6Schristos 		return ENOENT;
107177d85de2Sthorpej 
1072481ff5acSad 	/* Unlocked advisory check, ccdstrategy check is synchronous. */
107377d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
107477d85de2Sthorpej 		return (ENXIO);
107577d85de2Sthorpej 
10763214723fShpeyerl 	return (physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio));
10773214723fShpeyerl }
10783214723fShpeyerl 
1079e634efa8Schristos int (*compat_ccd_ioctl_60)(dev_t, u_long, void *, int, struct lwp *,
1080e634efa8Schristos     int (*)(dev_t, u_long, void *, int, struct lwp *)) = (void *)enosys;
1081e634efa8Schristos 
1082b8d38500Sthorpej static int
108353524e44Schristos ccdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
10846f9db1eeShpeyerl {
108577d85de2Sthorpej 	int unit = ccdunit(dev);
1086481ff5acSad 	int i, j, lookedup = 0, error = 0;
1087d91f98a8Spgoyette 	int part, pmask, make, hook;
108877d85de2Sthorpej 	struct ccd_softc *cs;
108977d85de2Sthorpej 	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
10902867b68bSelad 	kauth_cred_t uc;
109177d85de2Sthorpej 	char **cpp;
10928f6ed30dSdholland 	struct pathbuf *pb;
109377d85de2Sthorpej 	struct vnode **vpp;
1094e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1095e2d1c1f9Sfvdl 	struct disklabel newlabel;
1096e2d1c1f9Sfvdl #endif
109777d85de2Sthorpej 
1098ef781023Schristos 	switch (cmd) {
1099ef781023Schristos 	case CCDIOCSET:
1100ef781023Schristos 		make = 1;
1101ef781023Schristos 		break;
1102ef781023Schristos 	default:
11038c2f80f1Spgoyette 		MODULE_HOOK_CALL(ccd_ioctl_60_hook,
1104d91f98a8Spgoyette 				 (0, cmd, NULL, 0, NULL, NULL),
1105d91f98a8Spgoyette 				 enosys(), hook);
1106d91f98a8Spgoyette 		if (hook == 0)
1107e634efa8Schristos 			make = 1;
1108e634efa8Schristos 		else
1109ef781023Schristos 			make = 0;
1110ef781023Schristos 		break;
1111ef781023Schristos 	}
1112ef781023Schristos 
1113ef781023Schristos 	if ((cs = ccdget(unit, make)) == NULL)
1114b08b65c6Schristos 		return ENOENT;
1115481ff5acSad 	uc = kauth_cred_get();
111697297f37Sjld 
11178c2f80f1Spgoyette 	MODULE_HOOK_CALL(ccd_ioctl_60_hook,
1118d91f98a8Spgoyette 			 (dev, cmd, data, flag, l, ccdioctl),
1119d91f98a8Spgoyette 			 enosys(), error);
1120e634efa8Schristos 	if (error != ENOSYS)
11217d706cf7Ssborrill 		return error;
11227d706cf7Ssborrill 
11234cea35ebSthorpej 	/* Must be open for writes for these commands... */
11244cea35ebSthorpej 	switch (cmd) {
11254cea35ebSthorpej 	case CCDIOCSET:
11264cea35ebSthorpej 	case CCDIOCCLR:
11274cea35ebSthorpej 	case DIOCSDINFO:
11284cea35ebSthorpej 	case DIOCWDINFO:
1129c5ff2ab7Smlelstv 	case DIOCCACHESYNC:
1130c5ff2ab7Smlelstv 	case DIOCAWEDGE:
1131c5ff2ab7Smlelstv 	case DIOCDWEDGE:
1132ad4912fdSmartin 	case DIOCRMWEDGES:
1133c5ff2ab7Smlelstv 	case DIOCMWEDGES:
1134e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1135e2d1c1f9Sfvdl 	case ODIOCSDINFO:
1136e2d1c1f9Sfvdl 	case ODIOCWDINFO:
1137e2d1c1f9Sfvdl #endif
11384aef0509Sthorpej 	case DIOCKLABEL:
11394cea35ebSthorpej 	case DIOCWLABEL:
11404cea35ebSthorpej 		if ((flag & FWRITE) == 0)
11414cea35ebSthorpej 			return (EBADF);
11424cea35ebSthorpej 	}
11434cea35ebSthorpej 
11444cea35ebSthorpej 	/* Must be initialized for these... */
11454cea35ebSthorpej 	switch (cmd) {
11464cea35ebSthorpej 	case CCDIOCCLR:
1147d72a3b2dSkleink 	case DIOCGDINFO:
114822308629Sjdolecek 	case DIOCGSTRATEGY:
114922308629Sjdolecek 	case DIOCGCACHE:
1150f64a29d7Sthorpej 	case DIOCCACHESYNC:
1151c5ff2ab7Smlelstv 	case DIOCAWEDGE:
1152c5ff2ab7Smlelstv 	case DIOCDWEDGE:
1153c5ff2ab7Smlelstv 	case DIOCLWEDGES:
1154c5ff2ab7Smlelstv 	case DIOCMWEDGES:
11554cea35ebSthorpej 	case DIOCSDINFO:
11564cea35ebSthorpej 	case DIOCWDINFO:
11578d10f962Schristos 	case DIOCGPARTINFO:
11584cea35ebSthorpej 	case DIOCWLABEL:
11594aef0509Sthorpej 	case DIOCKLABEL:
1160939e074dSthorpej 	case DIOCGDEFLABEL:
1161e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
1162e2d1c1f9Sfvdl 	case ODIOCGDINFO:
1163e2d1c1f9Sfvdl 	case ODIOCSDINFO:
1164e2d1c1f9Sfvdl 	case ODIOCWDINFO:
1165e2d1c1f9Sfvdl 	case ODIOCGDEFLABEL:
1166e2d1c1f9Sfvdl #endif
11677686d3bdSmlelstv 		if ((cs->sc_flags & CCDF_INITED) == 0)
11687686d3bdSmlelstv 			return ENXIO;
11694cea35ebSthorpej 	}
11704cea35ebSthorpej 
1171c60db2e9Schristos 	error = disk_ioctl(&cs->sc_dkdev, dev, cmd, data, flag, l);
117275320acfSchristos 	if (error != EPASSTHROUGH)
11737686d3bdSmlelstv 		return error;
11747686d3bdSmlelstv 
11757686d3bdSmlelstv 	switch (cmd) {
11767686d3bdSmlelstv 	case DIOCGSTRATEGY:
11777686d3bdSmlelstv 	    {
11787686d3bdSmlelstv 		struct disk_strategy *dks = (void *)data;
11797686d3bdSmlelstv 
11807686d3bdSmlelstv 		mutex_enter(cs->sc_iolock);
11817686d3bdSmlelstv 		if (cs->sc_bufq != NULL)
11827686d3bdSmlelstv 			strlcpy(dks->dks_name,
11837686d3bdSmlelstv 			    bufq_getstrategyname(cs->sc_bufq),
11847686d3bdSmlelstv 			    sizeof(dks->dks_name));
11857686d3bdSmlelstv 		else
11867686d3bdSmlelstv 			error = EINVAL;
11877686d3bdSmlelstv 		mutex_exit(cs->sc_iolock);
11887686d3bdSmlelstv 		dks->dks_paramlen = 0;
11897686d3bdSmlelstv 		break;
11907686d3bdSmlelstv 	    }
11917686d3bdSmlelstv 
11927686d3bdSmlelstv 	case DIOCWDINFO:
11937686d3bdSmlelstv 	case DIOCSDINFO:
11947686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL
11957686d3bdSmlelstv 	case ODIOCWDINFO:
11967686d3bdSmlelstv 	case ODIOCSDINFO:
11977686d3bdSmlelstv #endif
11987686d3bdSmlelstv 	{
11997686d3bdSmlelstv 		struct disklabel *lp;
12007686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL
12017686d3bdSmlelstv 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
12027686d3bdSmlelstv 			memset(&newlabel, 0, sizeof newlabel);
12037686d3bdSmlelstv 			memcpy(&newlabel, data, sizeof (struct olddisklabel));
12047686d3bdSmlelstv 			lp = &newlabel;
12057686d3bdSmlelstv 		} else
12067686d3bdSmlelstv #endif
12077686d3bdSmlelstv 		lp = (struct disklabel *)data;
12087686d3bdSmlelstv 
12097686d3bdSmlelstv 		cs->sc_flags |= CCDF_LABELLING;
12107686d3bdSmlelstv 
12117686d3bdSmlelstv 		error = setdisklabel(cs->sc_dkdev.dk_label,
12127686d3bdSmlelstv 		    lp, 0, cs->sc_dkdev.dk_cpulabel);
12137686d3bdSmlelstv 		if (error == 0) {
12147686d3bdSmlelstv 			if (cmd == DIOCWDINFO
12157686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL
12167686d3bdSmlelstv 			    || cmd == ODIOCWDINFO
12177686d3bdSmlelstv #endif
12187686d3bdSmlelstv 			   )
12197686d3bdSmlelstv 				error = writedisklabel(CCDLABELDEV(dev),
12207686d3bdSmlelstv 				    ccdstrategy, cs->sc_dkdev.dk_label,
12217686d3bdSmlelstv 				    cs->sc_dkdev.dk_cpulabel);
12227686d3bdSmlelstv 		}
12237686d3bdSmlelstv 
12247686d3bdSmlelstv 		cs->sc_flags &= ~CCDF_LABELLING;
12257686d3bdSmlelstv 		break;
12267686d3bdSmlelstv 	}
12277686d3bdSmlelstv 
12287686d3bdSmlelstv 	case DIOCKLABEL:
12297686d3bdSmlelstv 		if (*(int *)data != 0)
12307686d3bdSmlelstv 			cs->sc_flags |= CCDF_KLABEL;
12317686d3bdSmlelstv 		else
12327686d3bdSmlelstv 			cs->sc_flags &= ~CCDF_KLABEL;
12337686d3bdSmlelstv 		break;
12347686d3bdSmlelstv 
12357686d3bdSmlelstv 	case DIOCWLABEL:
12367686d3bdSmlelstv 		if (*(int *)data != 0)
12377686d3bdSmlelstv 			cs->sc_flags |= CCDF_WLABEL;
12387686d3bdSmlelstv 		else
12397686d3bdSmlelstv 			cs->sc_flags &= ~CCDF_WLABEL;
12407686d3bdSmlelstv 		break;
12417686d3bdSmlelstv 
12427686d3bdSmlelstv 	case DIOCGDEFLABEL:
12437686d3bdSmlelstv 		ccdgetdefaultlabel(cs, (struct disklabel *)data);
12447686d3bdSmlelstv 		break;
12457686d3bdSmlelstv 
12467686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL
12477686d3bdSmlelstv 	case ODIOCGDEFLABEL:
12487686d3bdSmlelstv 		ccdgetdefaultlabel(cs, &newlabel);
12497686d3bdSmlelstv 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
12507686d3bdSmlelstv 			return ENOTTY;
12517686d3bdSmlelstv 		memcpy(data, &newlabel, sizeof (struct olddisklabel));
12527686d3bdSmlelstv 		break;
12537686d3bdSmlelstv #endif
12547686d3bdSmlelstv 	default:
12557686d3bdSmlelstv 		error = ENOTTY;
12567686d3bdSmlelstv 			break;
12577686d3bdSmlelstv 	}
12587686d3bdSmlelstv 
12597686d3bdSmlelstv 	if (error != ENOTTY)
12607686d3bdSmlelstv 		return error;
12617686d3bdSmlelstv 
12627686d3bdSmlelstv 	mutex_enter(&cs->sc_dvlock);
126375320acfSchristos 
1264c5ff2ab7Smlelstv 	error = 0;
126577d85de2Sthorpej 	switch (cmd) {
126677d85de2Sthorpej 	case CCDIOCSET:
12670272b2abSthorpej 		if (cs->sc_flags & CCDF_INITED) {
12680272b2abSthorpej 			error = EBUSY;
12690272b2abSthorpej 			goto out;
12700272b2abSthorpej 		}
1271b8dcfbd3Sthorpej 
1272ab27c3f8Sthorpej 		/* Validate the flags. */
12730272b2abSthorpej 		if ((ccio->ccio_flags & CCDF_USERMASK) != ccio->ccio_flags) {
12740272b2abSthorpej 			error = EINVAL;
12750272b2abSthorpej 			goto out;
12760272b2abSthorpej 		}
1277ab27c3f8Sthorpej 
1278481ff5acSad 		if (ccio->ccio_ndisks > CCD_MAXNDISKS ||
1279481ff5acSad 		    ccio->ccio_ndisks == 0) {
1280539f3bccSjdolecek 			error = EINVAL;
1281539f3bccSjdolecek 			goto out;
1282539f3bccSjdolecek 		}
1283539f3bccSjdolecek 
128477d85de2Sthorpej 		/* Fill in some important bits. */
12850272b2abSthorpej 		cs->sc_ileave = ccio->ccio_ileave;
12860272b2abSthorpej 		cs->sc_nccdisks = ccio->ccio_ndisks;
12870272b2abSthorpej 		cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK;
128877d85de2Sthorpej 
128977d85de2Sthorpej 		/*
129077d85de2Sthorpej 		 * Allocate space for and copy in the array of
12917d706cf7Ssborrill 		 * component pathnames and device numbers.
129277d85de2Sthorpej 		 */
1293481ff5acSad 		cpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*cpp), KM_SLEEP);
1294481ff5acSad 		vpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*vpp), KM_SLEEP);
1295bd99e342Sdsl 		error = copyin(ccio->ccio_disks, cpp,
1296481ff5acSad 		    ccio->ccio_ndisks * sizeof(*cpp));
129777d85de2Sthorpej 		if (error) {
1298481ff5acSad 			kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1299481ff5acSad 			kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
13000272b2abSthorpej 			goto out;
13016f9db1eeShpeyerl 		}
13026f9db1eeShpeyerl 
130377d85de2Sthorpej #ifdef DEBUG
130477d85de2Sthorpej 		if (ccddebug & CCDB_INIT)
130577d85de2Sthorpej 			for (i = 0; i < ccio->ccio_ndisks; ++i)
13067cdea212Schristos 				printf("ccdioctl: component %d: %p\n",
130777d85de2Sthorpej 				    i, cpp[i]);
130877d85de2Sthorpej #endif
130977d85de2Sthorpej 
131077d85de2Sthorpej 		for (i = 0; i < ccio->ccio_ndisks; ++i) {
131177d85de2Sthorpej #ifdef DEBUG
131277d85de2Sthorpej 			if (ccddebug & CCDB_INIT)
131386373f8cSchristos 				printf("ccdioctl: lookedup = %d\n", lookedup);
131477d85de2Sthorpej #endif
13158f6ed30dSdholland 			error = pathbuf_copyin(cpp[i], &pb);
13160a4a6fb0Sdholland 			if (error == 0) {
13175a52f2afSmlelstv 				error = vn_bdev_openpath(pb, &vpp[i], l);
13188f6ed30dSdholland 				pathbuf_destroy(pb);
13195a9cf8fcSriastradh 			}
13208f6ed30dSdholland 			if (error != 0) {
132177d85de2Sthorpej 				for (j = 0; j < lookedup; ++j)
1322a2db2c18Sthorpej 					(void)vn_close(vpp[j], FREAD|FWRITE,
1323a9ca7a37Sad 					    uc);
1324481ff5acSad 				kmem_free(vpp, ccio->ccio_ndisks *
1325481ff5acSad 				    sizeof(*vpp));
1326481ff5acSad 				kmem_free(cpp, ccio->ccio_ndisks *
1327481ff5acSad 				    sizeof(*cpp));
13287686d3bdSmlelstv 
13297686d3bdSmlelstv 				/*
13307686d3bdSmlelstv 				 * No component data is allocated,
13317686d3bdSmlelstv 				 * nothing is to be freed.
13327686d3bdSmlelstv 				*/
13337686d3bdSmlelstv 				cs->sc_nccdisks = 0;
13340272b2abSthorpej 				goto out;
133577d85de2Sthorpej 			}
133677d85de2Sthorpej 			++lookedup;
133777d85de2Sthorpej 		}
133877d85de2Sthorpej 
1339481ff5acSad 		/* Attach the disk. */
1340481ff5acSad 		disk_attach(&cs->sc_dkdev);
1341481ff5acSad 		bufq_alloc(&cs->sc_bufq, "fcfs", 0);
1342481ff5acSad 
134377d85de2Sthorpej 		/*
134477d85de2Sthorpej 		 * Initialize the ccd.  Fills in the softc for us.
134577d85de2Sthorpej 		 */
134695e1ffb1Schristos 		if ((error = ccdinit(cs, cpp, vpp, l)) != 0) {
134777d85de2Sthorpej 			for (j = 0; j < lookedup; ++j)
1348a1bc3740Sthorpej 				(void)vn_close(vpp[j], FREAD|FWRITE,
1349a9ca7a37Sad 				    uc);
1350481ff5acSad 			kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1351481ff5acSad 			kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
1352481ff5acSad 			disk_detach(&cs->sc_dkdev);
1353e38abff0Spgoyette 			mutex_exit(&cs->sc_dvlock);
1354481ff5acSad 			bufq_free(cs->sc_bufq);
1355e38abff0Spgoyette 			return error;
135677d85de2Sthorpej 		}
135777d85de2Sthorpej 
13580272b2abSthorpej 		/* We can free the temporary variables now. */
1359481ff5acSad 		kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
1360481ff5acSad 		kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
13610272b2abSthorpej 
136277d85de2Sthorpej 		/*
136377d85de2Sthorpej 		 * The ccd has been successfully initialized, so
13645b39541eSthorpej 		 * we can place it into the array.  Don't try to
13655b39541eSthorpej 		 * read the disklabel until the disk has been attached,
13665b39541eSthorpej 		 * because space for the disklabel is allocated
13675b39541eSthorpej 		 * in disk_attach();
136877d85de2Sthorpej 		 */
136977d85de2Sthorpej 		ccio->ccio_unit = unit;
137077d85de2Sthorpej 		ccio->ccio_size = cs->sc_size;
13715b39541eSthorpej 
13725b39541eSthorpej 		/* Try and read the disklabel. */
137377d85de2Sthorpej 		ccdgetdisklabel(dev);
1374c5ff2ab7Smlelstv 		disk_set_info(NULL, &cs->sc_dkdev, NULL);
1375c5ff2ab7Smlelstv 
1376c5ff2ab7Smlelstv 		/* discover wedges */
1377c5ff2ab7Smlelstv 		mutex_exit(&cs->sc_dvlock);
1378c5ff2ab7Smlelstv 		dkwedge_discover(&cs->sc_dkdev);
1379c5ff2ab7Smlelstv 		return 0;
138077d85de2Sthorpej 
138177d85de2Sthorpej 	case CCDIOCCLR:
138277d85de2Sthorpej 		/*
138377d85de2Sthorpej 		 * Don't unconfigure if any other partitions are open
138477d85de2Sthorpej 		 * or if both the character and block flavors of this
138577d85de2Sthorpej 		 * partition are open.
138677d85de2Sthorpej 		 */
138777d85de2Sthorpej 		part = DISKPART(dev);
138877d85de2Sthorpej 		pmask = (1 << part);
138977d85de2Sthorpej 		if ((cs->sc_dkdev.dk_openmask & ~pmask) ||
139077d85de2Sthorpej 		    ((cs->sc_dkdev.dk_bopenmask & pmask) &&
1391b8dcfbd3Sthorpej 		    (cs->sc_dkdev.dk_copenmask & pmask))) {
13920272b2abSthorpej 			error = EBUSY;
13930272b2abSthorpej 			goto out;
1394b8dcfbd3Sthorpej 		}
139577d85de2Sthorpej 
1396c5ff2ab7Smlelstv 		/* Delete all of our wedges. */
1397c5ff2ab7Smlelstv 		dkwedge_delall(&cs->sc_dkdev);
1398c5ff2ab7Smlelstv 
1399481ff5acSad 		/* Stop new I/O, wait for in-flight I/O to complete. */
1400481ff5acSad 		mutex_enter(cs->sc_iolock);
1401481ff5acSad 		cs->sc_flags &= ~(CCDF_INITED|CCDF_VLABEL);
1402481ff5acSad 		cs->sc_zap = true;
1403481ff5acSad 		while (disk_isbusy(&cs->sc_dkdev) ||
1404481ff5acSad 		    bufq_peek(cs->sc_bufq) != NULL ||
1405481ff5acSad 		    cs->sc_thread != NULL) {
1406481ff5acSad 			cv_broadcast(&cs->sc_push);
1407481ff5acSad 			(void)cv_timedwait(&cs->sc_stop, cs->sc_iolock, hz);
1408481ff5acSad 		}
1409481ff5acSad 		mutex_exit(cs->sc_iolock);
14102f25411dSthorpej 
141177d85de2Sthorpej 		/*
141277d85de2Sthorpej 		 * Free ccd_softc information and clear entry.
141377d85de2Sthorpej 		 */
14147268bf55Sthorpej 
14157268bf55Sthorpej 		/* Close the components and free their pathnames. */
141677d85de2Sthorpej 		for (i = 0; i < cs->sc_nccdisks; ++i) {
141777d85de2Sthorpej 			/*
141877d85de2Sthorpej 			 * XXX: this close could potentially fail and
141977d85de2Sthorpej 			 * cause Bad Things.  Maybe we need to force
142077d85de2Sthorpej 			 * the close to happen?
142177d85de2Sthorpej 			 */
142277d85de2Sthorpej #ifdef DEBUG
142377d85de2Sthorpej 			if (ccddebug & CCDB_VNODE)
142477d85de2Sthorpej 				vprint("CCDIOCCLR: vnode info",
142577d85de2Sthorpej 				    cs->sc_cinfo[i].ci_vp);
142677d85de2Sthorpej #endif
142777d85de2Sthorpej 			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1428a9ca7a37Sad 			    uc);
1429481ff5acSad 			kmem_free(cs->sc_cinfo[i].ci_path,
1430481ff5acSad 			    cs->sc_cinfo[i].ci_pathlen);
143177d85de2Sthorpej 		}
14327268bf55Sthorpej 
14337686d3bdSmlelstv 		if (cs->sc_nccdisks != 0) {
14347268bf55Sthorpej 			/* Free interleave index. */
1435481ff5acSad 			for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) {
1436481ff5acSad 				kmem_free(cs->sc_itable[i].ii_index,
1437481ff5acSad 				    cs->sc_itable[i].ii_indexsz);
1438481ff5acSad 			}
14397268bf55Sthorpej 			/* Free component info and interleave table. */
1440481ff5acSad 			kmem_free(cs->sc_cinfo, cs->sc_nccdisks *
1441481ff5acSad 			    sizeof(struct ccdcinfo));
1442481ff5acSad 			kmem_free(cs->sc_itable, (cs->sc_nccdisks + 1) *
1443481ff5acSad 			    sizeof(struct ccdiinfo));
14447686d3bdSmlelstv 		}
144577d85de2Sthorpej 
14467d706cf7Ssborrill 		aprint_normal("%s: detached\n", cs->sc_xname);
14477d706cf7Ssborrill 
14487d706cf7Ssborrill 		/* Detach the disk. */
14492af68666Sad 		disk_detach(&cs->sc_dkdev);
1450481ff5acSad 		bufq_free(cs->sc_bufq);
14517686d3bdSmlelstv 
1452948b2359Sriastradh 		/* also releases sc_dvlock */
1453b08b65c6Schristos 		ccdput(cs);
14547686d3bdSmlelstv 
145555252202Sjoerg 		/* Don't break, otherwise cs is read again. */
145655252202Sjoerg 		return 0;
145777d85de2Sthorpej 
145822308629Sjdolecek 	case DIOCGCACHE:
145922308629Sjdolecek 	    {
146022308629Sjdolecek 		int dkcache = 0;
146122308629Sjdolecek 
146222308629Sjdolecek 		/*
146322308629Sjdolecek 		 * We pass this call down to all components and report
146422308629Sjdolecek 		 * intersection of the flags returned by the components.
146522308629Sjdolecek 		 * If any errors out, we return error. CCD components
146622308629Sjdolecek 		 * can not change unless the device is unconfigured, so
146722308629Sjdolecek 		 * device feature flags will remain static. RCE/WCE can change
146822308629Sjdolecek 		 * of course, if set directly on underlying device.
146922308629Sjdolecek 		 */
147022308629Sjdolecek 		for (error = 0, i = 0; i < cs->sc_nccdisks; i++) {
147122308629Sjdolecek 			error = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, &j,
147222308629Sjdolecek 				      flag, uc);
147322308629Sjdolecek 			if (error)
147422308629Sjdolecek 				break;
147522308629Sjdolecek 
147622308629Sjdolecek 			if (i == 0)
147722308629Sjdolecek 				dkcache = j;
147822308629Sjdolecek 			else
14791af402feSjdolecek 				dkcache = DKCACHE_COMBINE(dkcache, j);
148022308629Sjdolecek 		}
148122308629Sjdolecek 
148222308629Sjdolecek 		*((int *)data) = dkcache;
148322308629Sjdolecek 		break;
148422308629Sjdolecek 	    }
148522308629Sjdolecek 
1486f64a29d7Sthorpej 	case DIOCCACHESYNC:
1487f64a29d7Sthorpej 		/*
1488f64a29d7Sthorpej 		 * We pass this call down to all components and report
1489f64a29d7Sthorpej 		 * the first error we encounter.
1490f64a29d7Sthorpej 		 */
1491f64a29d7Sthorpej 		for (error = 0, i = 0; i < cs->sc_nccdisks; i++) {
1492f64a29d7Sthorpej 			j = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, data,
149361e8303eSpooka 				      flag, uc);
1494f64a29d7Sthorpej 			if (j != 0 && error == 0)
1495f64a29d7Sthorpej 				error = j;
1496f64a29d7Sthorpej 		}
1497f64a29d7Sthorpej 		break;
1498f64a29d7Sthorpej 
149977d85de2Sthorpej default:
15000272b2abSthorpej 	error = ENOTTY;
15017686d3bdSmlelstv 		break;
150277d85de2Sthorpej 	}
150377d85de2Sthorpej 
15040272b2abSthorpej  out:
1505481ff5acSad 	mutex_exit(&cs->sc_dvlock);
15060272b2abSthorpej 	return (error);
150777d85de2Sthorpej }
150877d85de2Sthorpej 
1509b8d38500Sthorpej static int
1510b8d38500Sthorpej ccdsize(dev_t dev)
15116f9db1eeShpeyerl {
151277d85de2Sthorpej 	struct ccd_softc *cs;
1513e06bb29bSthorpej 	struct disklabel *lp;
1514e06bb29bSthorpej 	int part, unit, omask, size;
15156f9db1eeShpeyerl 
1516e06bb29bSthorpej 	unit = ccdunit(dev);
1517ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
1518b08b65c6Schristos 		return -1;
151977d85de2Sthorpej 
152077d85de2Sthorpej 	if ((cs->sc_flags & CCDF_INITED) == 0)
152177d85de2Sthorpej 		return (-1);
152277d85de2Sthorpej 
1523e06bb29bSthorpej 	part = DISKPART(dev);
1524e06bb29bSthorpej 	omask = cs->sc_dkdev.dk_openmask & (1 << part);
1525e06bb29bSthorpej 	lp = cs->sc_dkdev.dk_label;
1526e06bb29bSthorpej 
152795e1ffb1Schristos 	if (omask == 0 && ccdopen(dev, 0, S_IFBLK, curlwp))
1528e06bb29bSthorpej 		return (-1);
1529e06bb29bSthorpej 
1530e06bb29bSthorpej 	if (lp->d_partitions[part].p_fstype != FS_SWAP)
153177d85de2Sthorpej 		size = -1;
153277d85de2Sthorpej 	else
1533e06bb29bSthorpej 		size = lp->d_partitions[part].p_size *
1534e06bb29bSthorpej 		    (lp->d_secsize / DEV_BSIZE);
153577d85de2Sthorpej 
153695e1ffb1Schristos 	if (omask == 0 && ccdclose(dev, 0, S_IFBLK, curlwp))
153777d85de2Sthorpej 		return (-1);
153877d85de2Sthorpej 
153977d85de2Sthorpej 	return (size);
15406f9db1eeShpeyerl }
15416f9db1eeShpeyerl 
154277d85de2Sthorpej static void
1543b8d38500Sthorpej ccdgetdefaultlabel(struct ccd_softc *cs, struct disklabel *lp)
154477d85de2Sthorpej {
154577d85de2Sthorpej 	struct ccdgeom *ccg = &cs->sc_geom;
154677d85de2Sthorpej 
1547c8b4ac1bSthorpej 	memset(lp, 0, sizeof(*lp));
154877d85de2Sthorpej 
154953385cb9Smlelstv 	if (cs->sc_size > UINT32_MAX)
155053385cb9Smlelstv 		lp->d_secperunit = UINT32_MAX;
155153385cb9Smlelstv 	else
155277d85de2Sthorpej 		lp->d_secperunit = cs->sc_size;
155377d85de2Sthorpej 	lp->d_secsize = ccg->ccg_secsize;
155477d85de2Sthorpej 	lp->d_nsectors = ccg->ccg_nsectors;
155577d85de2Sthorpej 	lp->d_ntracks = ccg->ccg_ntracks;
155677d85de2Sthorpej 	lp->d_ncylinders = ccg->ccg_ncylinders;
155789d4987eSthorpej 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
155877d85de2Sthorpej 
155977d85de2Sthorpej 	strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
1560c182898bSchristos 	lp->d_type = DKTYPE_CCD;
156177d85de2Sthorpej 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
156277d85de2Sthorpej 	lp->d_rpm = 3600;
156377d85de2Sthorpej 	lp->d_interleave = 1;
156477d85de2Sthorpej 	lp->d_flags = 0;
156577d85de2Sthorpej 
156677d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_offset = 0;
156753385cb9Smlelstv 	lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
156877d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
156977d85de2Sthorpej 	lp->d_npartitions = RAW_PART + 1;
157077d85de2Sthorpej 
157177d85de2Sthorpej 	lp->d_magic = DISKMAGIC;
157277d85de2Sthorpej 	lp->d_magic2 = DISKMAGIC;
15735b39541eSthorpej 	lp->d_checksum = dkcksum(cs->sc_dkdev.dk_label);
1574939e074dSthorpej }
1575939e074dSthorpej 
1576939e074dSthorpej /*
1577939e074dSthorpej  * Read the disklabel from the ccd.  If one is not present, fake one
1578939e074dSthorpej  * up.
1579939e074dSthorpej  */
1580939e074dSthorpej static void
1581b8d38500Sthorpej ccdgetdisklabel(dev_t dev)
1582939e074dSthorpej {
1583939e074dSthorpej 	int unit = ccdunit(dev);
1584b08b65c6Schristos 	struct ccd_softc *cs;
1585d91455ceSdsl 	const char *errstring;
1586b08b65c6Schristos 	struct disklabel *lp;
1587b08b65c6Schristos 	struct cpu_disklabel *clp;
1588939e074dSthorpej 
1589ef781023Schristos 	if ((cs = ccdget(unit, 0)) == NULL)
1590b08b65c6Schristos 		return;
1591b08b65c6Schristos 	lp = cs->sc_dkdev.dk_label;
1592b08b65c6Schristos 	clp = cs->sc_dkdev.dk_cpulabel;
1593481ff5acSad 	KASSERT(mutex_owned(&cs->sc_dvlock));
1594481ff5acSad 
1595c8b4ac1bSthorpej 	memset(clp, 0, sizeof(*clp));
1596939e074dSthorpej 
1597939e074dSthorpej 	ccdgetdefaultlabel(cs, lp);
159877d85de2Sthorpej 
159977d85de2Sthorpej 	/*
160077d85de2Sthorpej 	 * Call the generic disklabel extraction routine.
160177d85de2Sthorpej 	 */
1602481ff5acSad 	cs->sc_flags |= CCDF_RLABEL;
16030b441623Slukem 	if ((cs->sc_flags & CCDF_NOLABEL) != 0)
16040b441623Slukem 		errstring = "CCDF_NOLABEL set; ignoring on-disk label";
16050b441623Slukem 	else
1606d318abdbSchristos 		errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy,
1607d318abdbSchristos 		    cs->sc_dkdev.dk_label, cs->sc_dkdev.dk_cpulabel);
1608d318abdbSchristos 	if (errstring)
160977d85de2Sthorpej 		ccdmakedisklabel(cs);
1610512a0186Senami 	else {
1611512a0186Senami 		int i;
1612512a0186Senami 		struct partition *pp;
1613512a0186Senami 
1614512a0186Senami 		/*
1615512a0186Senami 		 * Sanity check whether the found disklabel is valid.
1616512a0186Senami 		 *
1617512a0186Senami 		 * This is necessary since total size of ccd may vary
1618512a0186Senami 		 * when an interleave is changed even though exactly
1619*d0b020c0Sandvar 		 * same components are used, and old disklabel may used
1620512a0186Senami 		 * if that is found.
1621512a0186Senami 		 */
162249e3024aSmlelstv 		if (lp->d_secperunit < UINT32_MAX ?
162349e3024aSmlelstv 			lp->d_secperunit != cs->sc_size :
162449e3024aSmlelstv 			lp->d_secperunit > cs->sc_size)
1625512a0186Senami 			printf("WARNING: %s: "
16267d706cf7Ssborrill 			    "total sector size in disklabel (%ju) != "
16277d706cf7Ssborrill 			    "the size of ccd (%ju)\n", cs->sc_xname,
16287d706cf7Ssborrill 			    (uintmax_t)lp->d_secperunit,
16297d706cf7Ssborrill 			    (uintmax_t)cs->sc_size);
1630512a0186Senami 		for (i = 0; i < lp->d_npartitions; i++) {
1631512a0186Senami 			pp = &lp->d_partitions[i];
1632512a0186Senami 			if (pp->p_offset + pp->p_size > cs->sc_size)
16339b770726Senami 				printf("WARNING: %s: end of partition `%c' "
16347d706cf7Ssborrill 				    "exceeds the size of ccd (%ju)\n",
16357d706cf7Ssborrill 				    cs->sc_xname, 'a' + i, (uintmax_t)cs->sc_size);
1636512a0186Senami 		}
1637512a0186Senami 	}
163877d85de2Sthorpej 
163977d85de2Sthorpej #ifdef DEBUG
164077d85de2Sthorpej 	/* It's actually extremely common to have unlabeled ccds. */
164177d85de2Sthorpej 	if (ccddebug & CCDB_LABEL)
164277d85de2Sthorpej 		if (errstring != NULL)
164386373f8cSchristos 			printf("%s: %s\n", cs->sc_xname, errstring);
164477d85de2Sthorpej #endif
16454aef0509Sthorpej 
16464aef0509Sthorpej 	/* In-core label now valid. */
1647481ff5acSad 	cs->sc_flags = (cs->sc_flags | CCDF_VLABEL) & ~CCDF_RLABEL;
164877d85de2Sthorpej }
164977d85de2Sthorpej 
165077d85de2Sthorpej /*
165177d85de2Sthorpej  * Take care of things one might want to take care of in the event
165277d85de2Sthorpej  * that a disklabel isn't present.
165377d85de2Sthorpej  */
165477d85de2Sthorpej static void
1655b8d38500Sthorpej ccdmakedisklabel(struct ccd_softc *cs)
165677d85de2Sthorpej {
16575b39541eSthorpej 	struct disklabel *lp = cs->sc_dkdev.dk_label;
165877d85de2Sthorpej 
165977d85de2Sthorpej 	/*
166077d85de2Sthorpej 	 * For historical reasons, if there's no disklabel present
166177d85de2Sthorpej 	 * the raw partition must be marked FS_BSDFFS.
166277d85de2Sthorpej 	 */
166377d85de2Sthorpej 	lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
166477d85de2Sthorpej 
166577d85de2Sthorpej 	strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1666939e074dSthorpej 
1667939e074dSthorpej 	lp->d_checksum = dkcksum(lp);
166877d85de2Sthorpej }
166977d85de2Sthorpej 
167077d85de2Sthorpej #ifdef DEBUG
167177d85de2Sthorpej static void
1672b8d38500Sthorpej printiinfo(struct ccdiinfo *ii)
167377d85de2Sthorpej {
1674169ac5b3Saugustss 	int ix, i;
167577d85de2Sthorpej 
167677d85de2Sthorpej 	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
16774e0e5333Skleink 		printf(" itab[%d]: #dk %d sblk %" PRId64 " soff %" PRId64,
167877d85de2Sthorpej 		    ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
167977d85de2Sthorpej 		for (i = 0; i < ii->ii_ndisk; i++)
168086373f8cSchristos 			printf(" %d", ii->ii_index[i]);
168186373f8cSchristos 		printf("\n");
168277d85de2Sthorpej 	}
168377d85de2Sthorpej }
16846f9db1eeShpeyerl #endif
16857147ed32Shaad 
16869d1eb470Spgoyette MODULE(MODULE_CLASS_DRIVER, ccd, "dk_subr,bufq_fcfs");
16877147ed32Shaad 
16887147ed32Shaad static int
16897147ed32Shaad ccd_modcmd(modcmd_t cmd, void *arg)
16907147ed32Shaad {
1691cd2a35a0Smartin 	int error = 0;
1692cd2a35a0Smartin #ifdef _MODULE
1693cd2a35a0Smartin 	int bmajor = -1, cmajor = -1;
1694cd2a35a0Smartin #endif
1695b4a3a8f3Sjruoho 
16967147ed32Shaad 
16977147ed32Shaad 	switch (cmd) {
16987147ed32Shaad 	case MODULE_CMD_INIT:
1699b4a3a8f3Sjruoho #ifdef _MODULE
1700ef781023Schristos 		ccdattach(0);
17017147ed32Shaad 
1702ef781023Schristos 		error = devsw_attach("ccd", &ccd_bdevsw, &bmajor,
17037147ed32Shaad 		    &ccd_cdevsw, &cmajor);
1704b4a3a8f3Sjruoho #endif
17057147ed32Shaad 		break;
17067147ed32Shaad 
17077147ed32Shaad 	case MODULE_CMD_FINI:
1708b4a3a8f3Sjruoho #ifdef _MODULE
1709ef781023Schristos 		mutex_enter(&ccd_lock);
1710ee984189Spgoyette 		if (!LIST_EMPTY(&ccds)) {
1711ef781023Schristos 			mutex_exit(&ccd_lock);
171237efed97Schristos 			error = EBUSY;
171337efed97Schristos 		} else {
171437efed97Schristos 			mutex_exit(&ccd_lock);
1715e7bed289Sriastradh 			devsw_detach(&ccd_bdevsw, &ccd_cdevsw);
171637efed97Schristos 			ccddetach();
171737efed97Schristos 		}
1718b4a3a8f3Sjruoho #endif
17197147ed32Shaad 		break;
17207147ed32Shaad 
17217147ed32Shaad 	case MODULE_CMD_STAT:
17227147ed32Shaad 		return ENOTTY;
17237147ed32Shaad 
17247147ed32Shaad 	default:
17257147ed32Shaad 		return ENOTTY;
17267147ed32Shaad 	}
17277147ed32Shaad 
17287147ed32Shaad 	return error;
17297147ed32Shaad }
1730b08b65c6Schristos 
1731b08b65c6Schristos static int
1732b08b65c6Schristos ccd_units_sysctl(SYSCTLFN_ARGS)
1733b08b65c6Schristos {
1734b08b65c6Schristos 	struct sysctlnode node;
1735b08b65c6Schristos 	struct ccd_softc *sc;
1736b08b65c6Schristos 	int error, i, nccd, *units;
1737b08b65c6Schristos 	size_t size;
1738b08b65c6Schristos 
1739b08b65c6Schristos 	nccd = 0;
1740b08b65c6Schristos 	mutex_enter(&ccd_lock);
1741b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1742b08b65c6Schristos 		nccd++;
1743b08b65c6Schristos 	mutex_exit(&ccd_lock);
1744b08b65c6Schristos 
1745b08b65c6Schristos 	if (nccd != 0) {
1746b08b65c6Schristos 		size = nccd * sizeof(*units);
1747b08b65c6Schristos 		units = kmem_zalloc(size, KM_SLEEP);
1748b08b65c6Schristos 		i = 0;
1749b08b65c6Schristos 		mutex_enter(&ccd_lock);
1750b08b65c6Schristos 		LIST_FOREACH(sc, &ccds, sc_link) {
1751b08b65c6Schristos 			if (i >= nccd)
1752b08b65c6Schristos 				break;
1753b08b65c6Schristos 			units[i] = sc->sc_unit;
1754b08b65c6Schristos 		}
1755b08b65c6Schristos 		mutex_exit(&ccd_lock);
1756b08b65c6Schristos 	} else {
1757b08b65c6Schristos 		units = NULL;
1758b08b65c6Schristos 		size = 0;
1759b08b65c6Schristos 	}
1760b08b65c6Schristos 
1761b08b65c6Schristos 	node = *rnode;
1762b08b65c6Schristos 	node.sysctl_data = units;
1763b08b65c6Schristos 	node.sysctl_size = size;
1764b08b65c6Schristos 
1765b08b65c6Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1766b08b65c6Schristos 	if (units)
1767b08b65c6Schristos 		kmem_free(units, size);
1768b08b65c6Schristos 	return error;
1769b08b65c6Schristos }
1770b08b65c6Schristos 
1771b08b65c6Schristos static int
1772b08b65c6Schristos ccd_info_sysctl(SYSCTLFN_ARGS)
1773b08b65c6Schristos {
1774b08b65c6Schristos 	struct sysctlnode node;
1775b08b65c6Schristos 	struct ccddiskinfo ccd;
1776b08b65c6Schristos 	struct ccd_softc *sc;
1777c1fde472Spgoyette 	int unit, error;
1778b08b65c6Schristos 
1779b08b65c6Schristos 	if (newp == NULL || newlen != sizeof(int))
1780b08b65c6Schristos 		return EINVAL;
1781b08b65c6Schristos 
1782c1fde472Spgoyette 	error = sysctl_copyin(l, newp, &unit, sizeof unit);
1783c1fde472Spgoyette 	if (error)
1784c1fde472Spgoyette 		return error;
1785b08b65c6Schristos 	newlen = 0;
1786b08b65c6Schristos 	ccd.ccd_ndisks = ~0;
1787b08b65c6Schristos 	mutex_enter(&ccd_lock);
1788b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link) {
1789b08b65c6Schristos 		if (sc->sc_unit == unit) {
1790b08b65c6Schristos 			ccd.ccd_ileave = sc->sc_ileave;
1791b08b65c6Schristos 			ccd.ccd_size = sc->sc_size;
1792b08b65c6Schristos 			ccd.ccd_ndisks = sc->sc_nccdisks;
1793b08b65c6Schristos 			ccd.ccd_flags = sc->sc_flags;
1794b08b65c6Schristos 			break;
1795b08b65c6Schristos 		}
1796b08b65c6Schristos 	}
1797b08b65c6Schristos 	mutex_exit(&ccd_lock);
1798b08b65c6Schristos 
1799b08b65c6Schristos 	if (ccd.ccd_ndisks == ~0)
1800b08b65c6Schristos 		return ENOENT;
1801b08b65c6Schristos 
1802b08b65c6Schristos 	node = *rnode;
1803b08b65c6Schristos 	node.sysctl_data = &ccd;
1804b08b65c6Schristos 	node.sysctl_size = sizeof(ccd);
1805b08b65c6Schristos 
1806b08b65c6Schristos 	return sysctl_lookup(SYSCTLFN_CALL(&node));
1807b08b65c6Schristos }
1808b08b65c6Schristos 
1809b08b65c6Schristos static int
1810b08b65c6Schristos ccd_components_sysctl(SYSCTLFN_ARGS)
1811b08b65c6Schristos {
1812b08b65c6Schristos 	struct sysctlnode node;
1813b08b65c6Schristos 	int error, unit;
1814b08b65c6Schristos 	size_t size;
1815b08b65c6Schristos 	char *names, *p, *ep;
1816b08b65c6Schristos 	struct ccd_softc *sc;
1817b08b65c6Schristos 
1818b08b65c6Schristos 	if (newp == NULL || newlen != sizeof(int))
1819b08b65c6Schristos 		return EINVAL;
1820b08b65c6Schristos 
1821b08b65c6Schristos 	size = 0;
1822c1fde472Spgoyette 	error = sysctl_copyin(l, newp, &unit, sizeof unit);
1823c1fde472Spgoyette 	if (error)
1824c1fde472Spgoyette 		return error;
1825b08b65c6Schristos 	newlen = 0;
1826b08b65c6Schristos 	mutex_enter(&ccd_lock);
1827b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1828b08b65c6Schristos 		if (sc->sc_unit == unit) {
1829b08b65c6Schristos 			for (size_t i = 0; i < sc->sc_nccdisks; i++)
1830b08b65c6Schristos 				size += strlen(sc->sc_cinfo[i].ci_path) + 1;
1831b08b65c6Schristos 			break;
1832b08b65c6Schristos 		}
1833b08b65c6Schristos 	mutex_exit(&ccd_lock);
1834b08b65c6Schristos 
1835b08b65c6Schristos 	if (size == 0)
1836b08b65c6Schristos 		return ENOENT;
1837b08b65c6Schristos 	names = kmem_zalloc(size, KM_SLEEP);
1838b08b65c6Schristos 	p = names;
1839b08b65c6Schristos 	ep = names + size;
1840b08b65c6Schristos 	mutex_enter(&ccd_lock);
1841b08b65c6Schristos 	LIST_FOREACH(sc, &ccds, sc_link)
1842b08b65c6Schristos 		if (sc->sc_unit == unit) {
1843b08b65c6Schristos 			for (size_t i = 0; i < sc->sc_nccdisks; i++) {
1844b08b65c6Schristos 				char *d = sc->sc_cinfo[i].ci_path;
1845b08b65c6Schristos 				while (p < ep && (*p++ = *d++) != '\0')
1846b08b65c6Schristos 					continue;
1847b08b65c6Schristos 			}
1848b08b65c6Schristos 			break;
1849b08b65c6Schristos 		}
1850b08b65c6Schristos 	mutex_exit(&ccd_lock);
1851b08b65c6Schristos 
1852b08b65c6Schristos 	node = *rnode;
1853b08b65c6Schristos 	node.sysctl_data = names;
1854b08b65c6Schristos 	node.sysctl_size = ep - names;
1855b08b65c6Schristos 
1856b08b65c6Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1857b08b65c6Schristos 	kmem_free(names, size);
1858b08b65c6Schristos 	return error;
1859b08b65c6Schristos }
1860b08b65c6Schristos 
1861b08b65c6Schristos SYSCTL_SETUP(sysctl_kern_ccd_setup, "sysctl kern.ccd subtree setup")
1862b08b65c6Schristos {
1863b08b65c6Schristos 	const struct sysctlnode *node = NULL;
1864b08b65c6Schristos 
1865b08b65c6Schristos 	sysctl_createv(clog, 0, NULL, &node,
1866b08b65c6Schristos 	    CTLFLAG_PERMANENT,
1867b08b65c6Schristos 	    CTLTYPE_NODE, "ccd",
1868b08b65c6Schristos 	    SYSCTL_DESCR("ConCatenated Disk state"),
1869b08b65c6Schristos 	    NULL, 0, NULL, 0,
1870b08b65c6Schristos 	    CTL_KERN, CTL_CREATE, CTL_EOL);
1871b08b65c6Schristos 
1872b08b65c6Schristos 	if (node == NULL)
1873b08b65c6Schristos 		return;
1874b08b65c6Schristos 
1875b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1876b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READONLY,
1877b08b65c6Schristos 	    CTLTYPE_STRUCT, "units",
1878b08b65c6Schristos 	    SYSCTL_DESCR("List of ccd unit numbers"),
1879b08b65c6Schristos 	    ccd_units_sysctl, 0, NULL, 0,
1880b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1881b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1882b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1883b08b65c6Schristos 	    CTLTYPE_STRUCT, "info",
1884b08b65c6Schristos 	    SYSCTL_DESCR("Information about a CCD unit"),
1885b08b65c6Schristos 	    ccd_info_sysctl, 0, NULL, 0,
1886b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1887b08b65c6Schristos 	sysctl_createv(clog, 0, &node, NULL,
1888b08b65c6Schristos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1889b08b65c6Schristos 	    CTLTYPE_STRUCT, "components",
1890b08b65c6Schristos 	    SYSCTL_DESCR("Information about CCD components"),
1891b08b65c6Schristos 	    ccd_components_sysctl, 0, NULL, 0,
1892b08b65c6Schristos 	    CTL_CREATE, CTL_EOL);
1893b08b65c6Schristos }
1894