geom_ccd.c revision 33363
1130803Smarcel/* $Id: ccd.c,v 1.28 1998/01/31 03:19:06 eivind Exp $ */
2130803Smarcel
3130803Smarcel/*	$NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $	*/
4130803Smarcel
5130803Smarcel/*
6130803Smarcel * Copyright (c) 1995 Jason R. Thorpe.
7130803Smarcel * All rights reserved.
8130803Smarcel *
9130803Smarcel * Redistribution and use in source and binary forms, with or without
10130803Smarcel * modification, are permitted provided that the following conditions
11130803Smarcel * are met:
12130803Smarcel * 1. Redistributions of source code must retain the above copyright
13130803Smarcel *    notice, this list of conditions and the following disclaimer.
14130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright
15130803Smarcel *    notice, this list of conditions and the following disclaimer in the
16130803Smarcel *    documentation and/or other materials provided with the distribution.
17130803Smarcel * 3. All advertising materials mentioning features or use of this software
18130803Smarcel *    must display the following acknowledgement:
19130803Smarcel *	This product includes software developed for the NetBSD Project
20130803Smarcel *	by Jason R. Thorpe.
21130803Smarcel * 4. The name of the author may not be used to endorse or promote products
22130803Smarcel *    derived from this software without specific prior written permission.
23130803Smarcel *
24130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25130803Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26130803Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27130803Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28130803Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29130803Smarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30130803Smarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31130803Smarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32130803Smarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34130803Smarcel * SUCH DAMAGE.
35130803Smarcel */
36130803Smarcel
37130803Smarcel/*
38130803Smarcel * Copyright (c) 1988 University of Utah.
39130803Smarcel * Copyright (c) 1990, 1993
40130803Smarcel *	The Regents of the University of California.  All rights reserved.
41130803Smarcel *
42130803Smarcel * This code is derived from software contributed to Berkeley by
43130803Smarcel * the Systems Programming Group of the University of Utah Computer
44130803Smarcel * Science Department.
45130803Smarcel *
46130803Smarcel * Redistribution and use in source and binary forms, with or without
47130803Smarcel * modification, are permitted provided that the following conditions
48130803Smarcel * are met:
49130803Smarcel * 1. Redistributions of source code must retain the above copyright
50130803Smarcel *    notice, this list of conditions and the following disclaimer.
51130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright
52130803Smarcel *    notice, this list of conditions and the following disclaimer in the
53130803Smarcel *    documentation and/or other materials provided with the distribution.
54130803Smarcel * 3. All advertising materials mentioning features or use of this software
55130803Smarcel *    must display the following acknowledgement:
56130803Smarcel *	This product includes software developed by the University of
57130803Smarcel *	California, Berkeley and its contributors.
58130803Smarcel * 4. Neither the name of the University nor the names of its contributors
59130803Smarcel *    may be used to endorse or promote products derived from this software
60130803Smarcel *    without specific prior written permission.
61130803Smarcel *
62130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65130803Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70130803Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72130803Smarcel * SUCH DAMAGE.
73130803Smarcel *
74130803Smarcel * from: Utah $Hdr: cd.c 1.6 90/11/28$
75130803Smarcel *
76130803Smarcel *	@(#)cd.c	8.2 (Berkeley) 11/16/93
77130803Smarcel */
78130803Smarcel
79130803Smarcel/*
80130803Smarcel * "Concatenated" disk driver.
81130803Smarcel *
82130803Smarcel * Dynamic configuration and disklabel support by:
83130803Smarcel *	Jason R. Thorpe <thorpej@nas.nasa.gov>
84130803Smarcel *	Numerical Aerodynamic Simulation Facility
85130803Smarcel *	Mail Stop 258-6
86130803Smarcel *	NASA Ames Research Center
87130803Smarcel *	Moffett Field, CA 94035
88130803Smarcel */
89130803Smarcel
90130803Smarcel#include "ccd.h"
91130803Smarcel#if NCCD > 0
92130803Smarcel
93130803Smarcel#include <sys/param.h>
94130803Smarcel#include <sys/systm.h>
95130803Smarcel#include <sys/kernel.h>
96130803Smarcel#include <sys/proc.h>
97130803Smarcel#include <sys/buf.h>
98130803Smarcel#include <sys/malloc.h>
99130803Smarcel#include <sys/namei.h>
100130803Smarcel#include <sys/conf.h>
101130803Smarcel#include <sys/stat.h>
102130803Smarcel#include <sys/sysctl.h>
103130803Smarcel#include <sys/disklabel.h>
104130803Smarcel#include <ufs/ffs/fs.h>
105130803Smarcel#include <sys/device.h>
106130803Smarcel#undef KERNEL			/* XXX */
107130803Smarcel#include <sys/disk.h>
108130803Smarcel#define KERNEL
109130803Smarcel#include <sys/fcntl.h>
110130803Smarcel#include <sys/vnode.h>
111130803Smarcel
112130803Smarcel#include <sys/ccdvar.h>
113130803Smarcel
114130803Smarcel#if defined(CCDDEBUG) && !defined(DEBUG)
115130803Smarcel#define DEBUG
116130803Smarcel#endif
117130803Smarcel
118130803Smarcel#ifdef DEBUG
119130803Smarcel#define CCDB_FOLLOW	0x01
120130803Smarcel#define CCDB_INIT	0x02
121130803Smarcel#define CCDB_IO		0x04
122130803Smarcel#define CCDB_LABEL	0x08
123130803Smarcel#define CCDB_VNODE	0x10
124130803Smarcelstatic int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL |
125130803Smarcel    CCDB_VNODE;
126130803SmarcelSYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, "");
127130803Smarcel#undef DEBUG
128130803Smarcel#endif
129130803Smarcel
130130803Smarcel#define	ccdunit(x)	dkunit(x)
131130803Smarcel#define ccdpart(x)	dkpart(x)
132130803Smarcel
133130803Smarcel/*
134130803Smarcel   This is how mirroring works (only writes are special):
135130803Smarcel
136130803Smarcel   When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s
137130803Smarcel   linked together by the cb_mirror field.  "cb_pflags &
138130803Smarcel   CCDPF_MIRROR_DONE" is set to 0 on both of them.
139130803Smarcel
140130803Smarcel   When a component returns to ccdiodone(), it checks if "cb_pflags &
141130803Smarcel   CCDPF_MIRROR_DONE" is set or not.  If not, it sets the partner's
142130803Smarcel   flag and returns.  If it is, it means its partner has already
143130803Smarcel   returned, so it will go to the regular cleanup.
144130803Smarcel
145130803Smarcel */
146130803Smarcel
147130803Smarcelstruct ccdbuf {
148130803Smarcel	struct buf	cb_buf;		/* new I/O buf */
149130803Smarcel	struct buf	*cb_obp;	/* ptr. to original I/O buf */
150130803Smarcel	int		cb_unit;	/* target unit */
151130803Smarcel	int		cb_comp;	/* target component */
152130803Smarcel	int		cb_pflags;	/* mirror/parity status flag */
153130803Smarcel	struct ccdbuf	*cb_mirror;	/* mirror counterpart */
154130803Smarcel};
155130803Smarcel
156130803Smarcel/* bits in cb_pflags */
157130803Smarcel#define CCDPF_MIRROR_DONE 1	/* if set, mirror counterpart is done */
158130803Smarcel
159130803Smarcel#define	getccdbuf()		\
160130803Smarcel	((struct ccdbuf *)malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK))
161130803Smarcel#define putccdbuf(cbp)		\
162130803Smarcel	free((caddr_t)(cbp), M_DEVBUF)
163130803Smarcel
164130803Smarcel#define CCDLABELDEV(dev)	\
165130803Smarcel	(makedev(major((dev)), dkmakeminor(ccdunit((dev)), 0, RAW_PART)))
166130803Smarcel
167130803Smarcelstatic d_open_t ccdopen;
168130803Smarcelstatic d_close_t ccdclose;
169130803Smarcelstatic d_strategy_t ccdstrategy;
170130803Smarcelstatic d_ioctl_t ccdioctl;
171130803Smarcelstatic d_dump_t ccddump;
172130803Smarcelstatic d_psize_t ccdsize;
173130803Smarcel
174130803Smarcel#define CDEV_MAJOR 74
175130803Smarcel#define BDEV_MAJOR 21
176130803Smarcel
177130803Smarcelstatic struct cdevsw ccd_cdevsw;
178130803Smarcelstatic struct bdevsw ccd_bdevsw = {
179130803Smarcel  ccdopen, ccdclose, ccdstrategy, ccdioctl,
180130803Smarcel  ccddump, ccdsize, 0,
181130803Smarcel  "ccd", &ccd_cdevsw, -1
182130803Smarcel};
183130803Smarcel
184130803Smarcel/* Called by main() during pseudo-device attachment */
185130803Smarcelstatic void	ccdattach __P((void *));
186130803SmarcelPSEUDO_SET(ccdattach, ccd);
187130803Smarcel
188130803Smarcel/* called by biodone() at interrupt time */
189130803Smarcelstatic	void ccdiodone __P((struct ccdbuf *cbp));
190130803Smarcel
191130803Smarcelstatic	void ccdstart __P((struct ccd_softc *, struct buf *));
192130803Smarcelstatic	void ccdinterleave __P((struct ccd_softc *, int));
193130803Smarcelstatic	void ccdintr __P((struct ccd_softc *, struct buf *));
194130803Smarcelstatic	int ccdinit __P((struct ccddevice *, char **, struct proc *));
195130803Smarcelstatic	int ccdlookup __P((char *, struct proc *p, struct vnode **));
196130803Smarcelstatic	void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *,
197130803Smarcel		struct buf *, daddr_t, caddr_t, long));
198130803Smarcelstatic	void ccdgetdisklabel __P((dev_t));
199130803Smarcelstatic	void ccdmakedisklabel __P((struct ccd_softc *));
200130803Smarcelstatic	int ccdlock __P((struct ccd_softc *));
201130803Smarcelstatic	void ccdunlock __P((struct ccd_softc *));
202130803Smarcel
203130803Smarcel#ifdef DEBUG
204130803Smarcelstatic	void printiinfo __P((struct ccdiinfo *));
205130803Smarcel#endif
206130803Smarcel
207130803Smarcel/* Non-private for the benefit of libkvm. */
208130803Smarcelstruct	ccd_softc *ccd_softc;
209130803Smarcelstruct	ccddevice *ccddevs;
210130803Smarcelstatic	int numccd = 0;
211130803Smarcel
212130803Smarcelstatic int ccd_devsw_installed = 0;
213130803Smarcel
214130803Smarcel/*
215130803Smarcel * Number of blocks to untouched in front of a component partition.
216130803Smarcel * This is to avoid violating its disklabel area when it starts at the
217130803Smarcel * beginning of the slice.
218130803Smarcel */
219130803Smarcel#if !defined(CCD_OFFSET)
220130803Smarcel#define CCD_OFFSET 16
221130803Smarcel#endif
222130803Smarcel
223130803Smarcel/*
224130803Smarcel * Called by main() during pseudo-device attachment.  All we need
225130803Smarcel * to do is allocate enough space for devices to be configured later, and
226130803Smarcel * add devsw entries.
227130803Smarcel */
228130803Smarcelstatic void
229130803Smarcelccdattach(dummy)
230130803Smarcel	void *dummy;
231130803Smarcel{
232130803Smarcel	int i;
233130803Smarcel	int num = NCCD;
234130803Smarcel
235130803Smarcel	if (num > 1)
236130803Smarcel		printf("ccd0-%d: Concatenated disk drivers\n", num-1);
237130803Smarcel	else
238130803Smarcel		printf("ccd0: Concatenated disk driver\n");
239130803Smarcel
240130803Smarcel	ccd_softc = (struct ccd_softc *)malloc(num * sizeof(struct ccd_softc),
241130803Smarcel	    M_DEVBUF, M_NOWAIT);
242130803Smarcel	ccddevs = (struct ccddevice *)malloc(num * sizeof(struct ccddevice),
243130803Smarcel	    M_DEVBUF, M_NOWAIT);
244130803Smarcel	if ((ccd_softc == NULL) || (ccddevs == NULL)) {
245130803Smarcel		printf("WARNING: no memory for concatenated disks\n");
246130803Smarcel		if (ccd_softc != NULL)
247130803Smarcel			free(ccd_softc, M_DEVBUF);
248130803Smarcel		if (ccddevs != NULL)
249130803Smarcel			free(ccddevs, M_DEVBUF);
250130803Smarcel		return;
251130803Smarcel	}
252130803Smarcel	numccd = num;
253130803Smarcel	bzero(ccd_softc, num * sizeof(struct ccd_softc));
254130803Smarcel	bzero(ccddevs, num * sizeof(struct ccddevice));
255130803Smarcel
256130803Smarcel	/* XXX: is this necessary? */
257130803Smarcel	for (i = 0; i < numccd; ++i)
258130803Smarcel		ccddevs[i].ccd_dk = -1;
259130803Smarcel
260130803Smarcel	if( ! ccd_devsw_installed ) {
261130803Smarcel		bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &ccd_bdevsw);
262130803Smarcel		ccd_devsw_installed = 1;
263130803Smarcel    	}
264130803Smarcel	else {
265130803Smarcel		printf("huh?\n");
266130803Smarcel	}
267130803Smarcel}
268130803Smarcel
269130803Smarcelstatic int
270130803Smarcelccdinit(ccd, cpaths, p)
271130803Smarcel	struct ccddevice *ccd;
272130803Smarcel	char **cpaths;
273130803Smarcel	struct proc *p;
274130803Smarcel{
275130803Smarcel	register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
276130803Smarcel	register struct ccdcinfo *ci = NULL;	/* XXX */
277130803Smarcel	register size_t size;
278130803Smarcel	register int ix;
279130803Smarcel	struct vnode *vp;
280130803Smarcel	struct vattr va;
281130803Smarcel	size_t minsize;
282130803Smarcel	int maxsecsize;
283130803Smarcel	struct partinfo dpart;
284130803Smarcel	struct ccdgeom *ccg = &cs->sc_geom;
285130803Smarcel	char tmppath[MAXPATHLEN];
286130803Smarcel	int error;
287130803Smarcel
288130803Smarcel#ifdef DEBUG
289130803Smarcel	if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
290130803Smarcel		printf("ccdinit: unit %d\n", ccd->ccd_unit);
291130803Smarcel#endif
292130803Smarcel
293130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
294130803Smarcel	cs->sc_dk = ccd->ccd_dk;
295130803Smarcel#endif
296130803Smarcel	cs->sc_size = 0;
297130803Smarcel	cs->sc_ileave = ccd->ccd_interleave;
298130803Smarcel	cs->sc_nccdisks = ccd->ccd_ndev;
299130803Smarcel
300130803Smarcel	/* Allocate space for the component info. */
301130803Smarcel	cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
302130803Smarcel	    M_DEVBUF, M_WAITOK);
303130803Smarcel
304130803Smarcel	/*
305130803Smarcel	 * Verify that each component piece exists and record
306130803Smarcel	 * relevant information about it.
307130803Smarcel	 */
308130803Smarcel	maxsecsize = 0;
309130803Smarcel	minsize = 0;
310130803Smarcel	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
311130803Smarcel		vp = ccd->ccd_vpp[ix];
312130803Smarcel		ci = &cs->sc_cinfo[ix];
313130803Smarcel		ci->ci_vp = vp;
314130803Smarcel
315130803Smarcel		/*
316130803Smarcel		 * Copy in the pathname of the component.
317130803Smarcel		 */
318130803Smarcel		bzero(tmppath, sizeof(tmppath));	/* sanity */
319130803Smarcel		if (error = copyinstr(cpaths[ix], tmppath,
320130803Smarcel		    MAXPATHLEN, &ci->ci_pathlen)) {
321130803Smarcel#ifdef DEBUG
322130803Smarcel			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
323130803Smarcel				printf("ccd%d: can't copy path, error = %d\n",
324130803Smarcel				    ccd->ccd_unit, error);
325130803Smarcel#endif
326130803Smarcel			while (ci > cs->sc_cinfo) {
327130803Smarcel				ci--;
328130803Smarcel				free(ci->ci_path, M_DEVBUF);
329130803Smarcel			}
330130803Smarcel			free(cs->sc_cinfo, M_DEVBUF);
331130803Smarcel			return (error);
332130803Smarcel		}
333130803Smarcel		ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK);
334130803Smarcel		bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
335130803Smarcel
336130803Smarcel		/*
337130803Smarcel		 * XXX: Cache the component's dev_t.
338130803Smarcel		 */
339130803Smarcel		if (error = VOP_GETATTR(vp, &va, p->p_ucred, p)) {
340130803Smarcel#ifdef DEBUG
341130803Smarcel			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
342130803Smarcel				printf("ccd%d: %s: getattr failed %s = %d\n",
343130803Smarcel				    ccd->ccd_unit, ci->ci_path,
344130803Smarcel				    "error", error);
345130803Smarcel#endif
346130803Smarcel			while (ci >= cs->sc_cinfo) {
347130803Smarcel				free(ci->ci_path, M_DEVBUF);
348130803Smarcel				ci--;
349130803Smarcel			}
350130803Smarcel			free(cs->sc_cinfo, M_DEVBUF);
351130803Smarcel			return (error);
352130803Smarcel		}
353130803Smarcel		ci->ci_dev = va.va_rdev;
354130803Smarcel
355130803Smarcel		/*
356130803Smarcel		 * Get partition information for the component.
357130803Smarcel		 */
358130803Smarcel		if (error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart,
359130803Smarcel		    FREAD, p->p_ucred, p)) {
360130803Smarcel#ifdef DEBUG
361130803Smarcel			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
362130803Smarcel				 printf("ccd%d: %s: ioctl failed, error = %d\n",
363130803Smarcel				     ccd->ccd_unit, ci->ci_path, error);
364130803Smarcel#endif
365130803Smarcel			while (ci >= cs->sc_cinfo) {
366130803Smarcel				free(ci->ci_path, M_DEVBUF);
367130803Smarcel				ci--;
368130803Smarcel			}
369130803Smarcel			free(cs->sc_cinfo, M_DEVBUF);
370130803Smarcel			return (error);
371130803Smarcel		}
372130803Smarcel		if (dpart.part->p_fstype == FS_BSDFFS) {
373130803Smarcel			maxsecsize =
374130803Smarcel			    ((dpart.disklab->d_secsize > maxsecsize) ?
375130803Smarcel			    dpart.disklab->d_secsize : maxsecsize);
376130803Smarcel			size = dpart.part->p_size - CCD_OFFSET;
377130803Smarcel		} else {
378130803Smarcel#ifdef DEBUG
379130803Smarcel			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
380130803Smarcel				printf("ccd%d: %s: incorrect partition type\n",
381130803Smarcel				    ccd->ccd_unit, ci->ci_path);
382130803Smarcel#endif
383130803Smarcel			while (ci >= cs->sc_cinfo) {
384130803Smarcel				free(ci->ci_path, M_DEVBUF);
385130803Smarcel				ci--;
386130803Smarcel			}
387130803Smarcel			free(cs->sc_cinfo, M_DEVBUF);
388130803Smarcel			return (EFTYPE);
389130803Smarcel		}
390130803Smarcel
391130803Smarcel		/*
392130803Smarcel		 * Calculate the size, truncating to an interleave
393130803Smarcel		 * boundary if necessary.
394130803Smarcel		 */
395130803Smarcel
396130803Smarcel		if (cs->sc_ileave > 1)
397130803Smarcel			size -= size % cs->sc_ileave;
398130803Smarcel
399130803Smarcel		if (size == 0) {
400130803Smarcel#ifdef DEBUG
401130803Smarcel			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
402130803Smarcel				printf("ccd%d: %s: size == 0\n",
403130803Smarcel				    ccd->ccd_unit, ci->ci_path);
404130803Smarcel#endif
405130803Smarcel			while (ci >= cs->sc_cinfo) {
406130803Smarcel				free(ci->ci_path, M_DEVBUF);
407130803Smarcel				ci--;
408130803Smarcel			}
409130803Smarcel			free(cs->sc_cinfo, M_DEVBUF);
410130803Smarcel			return (ENODEV);
411130803Smarcel		}
412130803Smarcel
413130803Smarcel		if (minsize == 0 || size < minsize)
414130803Smarcel			minsize = size;
415130803Smarcel		ci->ci_size = size;
416130803Smarcel		cs->sc_size += size;
417130803Smarcel	}
418130803Smarcel
419130803Smarcel	/*
420130803Smarcel	 * Don't allow the interleave to be smaller than
421130803Smarcel	 * the biggest component sector.
422130803Smarcel	 */
423130803Smarcel	if ((cs->sc_ileave > 0) &&
424130803Smarcel	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
425130803Smarcel#ifdef DEBUG
426130803Smarcel		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
427130803Smarcel			printf("ccd%d: interleave must be at least %d\n",
428130803Smarcel			    ccd->ccd_unit, (maxsecsize / DEV_BSIZE));
429130803Smarcel#endif
430130803Smarcel		while (ci >= cs->sc_cinfo) {
431130803Smarcel			free(ci->ci_path, M_DEVBUF);
432130803Smarcel			ci--;
433130803Smarcel		}
434130803Smarcel		free(cs->sc_cinfo, M_DEVBUF);
435130803Smarcel		return (EINVAL);
436130803Smarcel	}
437130803Smarcel
438130803Smarcel	/*
439130803Smarcel	 * If uniform interleave is desired set all sizes to that of
440130803Smarcel	 * the smallest component.
441130803Smarcel	 */
442130803Smarcel	if (ccd->ccd_flags & CCDF_UNIFORM) {
443130803Smarcel		for (ci = cs->sc_cinfo;
444130803Smarcel		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
445130803Smarcel			ci->ci_size = minsize;
446130803Smarcel		if (ccd->ccd_flags & CCDF_MIRROR) {
447130803Smarcel			/*
448130803Smarcel			 * Check to see if an even number of components
449130803Smarcel			 * have been specified.
450130803Smarcel			 */
451130803Smarcel			if (cs->sc_nccdisks % 2) {
452130803Smarcel				printf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit );
453130803Smarcel				while (ci > cs->sc_cinfo) {
454130803Smarcel					ci--;
455130803Smarcel					free(ci->ci_path, M_DEVBUF);
456130803Smarcel				}
457130803Smarcel				free(cs->sc_cinfo, M_DEVBUF);
458130803Smarcel				return (EINVAL);
459130803Smarcel			}
460130803Smarcel			cs->sc_size = (cs->sc_nccdisks/2) * minsize;
461130803Smarcel		}
462130803Smarcel		else if (ccd->ccd_flags & CCDF_PARITY)
463130803Smarcel			cs->sc_size = (cs->sc_nccdisks-1) * minsize;
464130803Smarcel		else
465130803Smarcel			cs->sc_size = cs->sc_nccdisks * minsize;
466130803Smarcel	}
467130803Smarcel
468130803Smarcel	/*
469130803Smarcel	 * Construct the interleave table.
470130803Smarcel	 */
471130803Smarcel	ccdinterleave(cs, ccd->ccd_unit);
472130803Smarcel
473130803Smarcel	/*
474130803Smarcel	 * Create pseudo-geometry based on 1MB cylinders.  It's
475130803Smarcel	 * pretty close.
476130803Smarcel	 */
477130803Smarcel	ccg->ccg_secsize = maxsecsize;
478130803Smarcel	ccg->ccg_ntracks = 1;
479130803Smarcel	ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize);
480130803Smarcel	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
481130803Smarcel
482130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
483130803Smarcel	if (ccd->ccd_dk >= 0)
484130803Smarcel		dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2);     /* XXX */
485130803Smarcel#endif
486130803Smarcel
487130803Smarcel	cs->sc_flags |= CCDF_INITED;
488130803Smarcel	cs->sc_cflags = ccd->ccd_flags;	/* So we can find out later... */
489130803Smarcel	cs->sc_unit = ccd->ccd_unit;
490130803Smarcel	return (0);
491130803Smarcel}
492130803Smarcel
493130803Smarcelstatic void
494130803Smarcelccdinterleave(cs, unit)
495130803Smarcel	register struct ccd_softc *cs;
496130803Smarcel	int unit;
497130803Smarcel{
498130803Smarcel	register struct ccdcinfo *ci, *smallci;
499130803Smarcel	register struct ccdiinfo *ii;
500130803Smarcel	register daddr_t bn, lbn;
501130803Smarcel	register int ix;
502130803Smarcel	u_long size;
503130803Smarcel
504130803Smarcel#ifdef DEBUG
505130803Smarcel	if (ccddebug & CCDB_INIT)
506130803Smarcel		printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
507130803Smarcel#endif
508130803Smarcel	/*
509130803Smarcel	 * Allocate an interleave table.
510130803Smarcel	 * Chances are this is too big, but we don't care.
511130803Smarcel	 */
512130803Smarcel	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
513130803Smarcel	cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
514130803Smarcel	bzero((caddr_t)cs->sc_itable, size);
515130803Smarcel
516130803Smarcel	/*
517130803Smarcel	 * Trivial case: no interleave (actually interleave of disk size).
518130803Smarcel	 * Each table entry represents a single component in its entirety.
519130803Smarcel	 */
520130803Smarcel	if (cs->sc_ileave == 0) {
521130803Smarcel		bn = 0;
522130803Smarcel		ii = cs->sc_itable;
523130803Smarcel
524130803Smarcel		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
525130803Smarcel			/* Allocate space for ii_index. */
526130803Smarcel			ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK);
527130803Smarcel			ii->ii_ndisk = 1;
528130803Smarcel			ii->ii_startblk = bn;
529130803Smarcel			ii->ii_startoff = 0;
530130803Smarcel			ii->ii_index[0] = ix;
531130803Smarcel			bn += cs->sc_cinfo[ix].ci_size;
532130803Smarcel			ii++;
533130803Smarcel		}
534130803Smarcel		ii->ii_ndisk = 0;
535130803Smarcel#ifdef DEBUG
536130803Smarcel		if (ccddebug & CCDB_INIT)
537130803Smarcel			printiinfo(cs->sc_itable);
538130803Smarcel#endif
539130803Smarcel		return;
540130803Smarcel	}
541130803Smarcel
542130803Smarcel	/*
543130803Smarcel	 * The following isn't fast or pretty; it doesn't have to be.
544130803Smarcel	 */
545130803Smarcel	size = 0;
546130803Smarcel	bn = lbn = 0;
547130803Smarcel	for (ii = cs->sc_itable; ; ii++) {
548130803Smarcel		/* Allocate space for ii_index. */
549130803Smarcel		ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks),
550130803Smarcel		    M_DEVBUF, M_WAITOK);
551130803Smarcel
552130803Smarcel		/*
553130803Smarcel		 * Locate the smallest of the remaining components
554130803Smarcel		 */
555130803Smarcel		smallci = NULL;
556130803Smarcel		for (ci = cs->sc_cinfo;
557130803Smarcel		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
558130803Smarcel			if (ci->ci_size > size &&
559130803Smarcel			    (smallci == NULL ||
560130803Smarcel			     ci->ci_size < smallci->ci_size))
561130803Smarcel				smallci = ci;
562130803Smarcel
563130803Smarcel		/*
564130803Smarcel		 * Nobody left, all done
565130803Smarcel		 */
566130803Smarcel		if (smallci == NULL) {
567130803Smarcel			ii->ii_ndisk = 0;
568130803Smarcel			break;
569130803Smarcel		}
570130803Smarcel
571130803Smarcel		/*
572130803Smarcel		 * Record starting logical block and component offset
573130803Smarcel		 */
574130803Smarcel		ii->ii_startblk = bn / cs->sc_ileave;
575130803Smarcel		ii->ii_startoff = lbn;
576130803Smarcel
577130803Smarcel		/*
578130803Smarcel		 * Determine how many disks take part in this interleave
579130803Smarcel		 * and record their indices.
580130803Smarcel		 */
581130803Smarcel		ix = 0;
582130803Smarcel		for (ci = cs->sc_cinfo;
583130803Smarcel		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
584130803Smarcel			if (ci->ci_size >= smallci->ci_size)
585130803Smarcel				ii->ii_index[ix++] = ci - cs->sc_cinfo;
586130803Smarcel		ii->ii_ndisk = ix;
587130803Smarcel		bn += ix * (smallci->ci_size - size);
588130803Smarcel		lbn = smallci->ci_size / cs->sc_ileave;
589130803Smarcel		size = smallci->ci_size;
590130803Smarcel	}
591130803Smarcel#ifdef DEBUG
592130803Smarcel	if (ccddebug & CCDB_INIT)
593130803Smarcel		printiinfo(cs->sc_itable);
594130803Smarcel#endif
595130803Smarcel}
596130803Smarcel
597130803Smarcel/* ARGSUSED */
598130803Smarcelstatic int
599130803Smarcelccdopen(dev, flags, fmt, p)
600130803Smarcel	dev_t dev;
601130803Smarcel	int flags, fmt;
602130803Smarcel	struct proc *p;
603130803Smarcel{
604130803Smarcel	int unit = ccdunit(dev);
605130803Smarcel	struct ccd_softc *cs;
606130803Smarcel	struct disklabel *lp;
607130803Smarcel	int error = 0, part, pmask;
608130803Smarcel
609130803Smarcel#ifdef DEBUG
610130803Smarcel	if (ccddebug & CCDB_FOLLOW)
611130803Smarcel		printf("ccdopen(%x, %x)\n", dev, flags);
612130803Smarcel#endif
613130803Smarcel	if (unit >= numccd)
614130803Smarcel		return (ENXIO);
615130803Smarcel	cs = &ccd_softc[unit];
616130803Smarcel
617130803Smarcel	if (error = ccdlock(cs))
618130803Smarcel		return (error);
619130803Smarcel
620130803Smarcel	lp = &cs->sc_dkdev.dk_label;
621130803Smarcel
622130803Smarcel	part = ccdpart(dev);
623130803Smarcel	pmask = (1 << part);
624130803Smarcel
625130803Smarcel	/*
626130803Smarcel	 * If we're initialized, check to see if there are any other
627130803Smarcel	 * open partitions.  If not, then it's safe to update
628130803Smarcel	 * the in-core disklabel.
629130803Smarcel	 */
630130803Smarcel	if ((cs->sc_flags & CCDF_INITED) && (cs->sc_dkdev.dk_openmask == 0))
631130803Smarcel		ccdgetdisklabel(dev);
632130803Smarcel
633130803Smarcel	/* Check that the partition exists. */
634130803Smarcel	if (part != RAW_PART && ((part >= lp->d_npartitions) ||
635130803Smarcel	    (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
636130803Smarcel		error = ENXIO;
637130803Smarcel		goto done;
638130803Smarcel	}
639130803Smarcel
640130803Smarcel	/* Prevent our unit from being unconfigured while open. */
641130803Smarcel	switch (fmt) {
642130803Smarcel	case S_IFCHR:
643130803Smarcel		cs->sc_dkdev.dk_copenmask |= pmask;
644130803Smarcel		break;
645130803Smarcel
646130803Smarcel	case S_IFBLK:
647130803Smarcel		cs->sc_dkdev.dk_bopenmask |= pmask;
648130803Smarcel		break;
649130803Smarcel	}
650130803Smarcel	cs->sc_dkdev.dk_openmask =
651130803Smarcel	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
652130803Smarcel
653130803Smarcel done:
654130803Smarcel	ccdunlock(cs);
655130803Smarcel	return (0);
656130803Smarcel}
657130803Smarcel
658130803Smarcel/* ARGSUSED */
659130803Smarcelstatic int
660130803Smarcelccdclose(dev, flags, fmt, p)
661130803Smarcel	dev_t dev;
662130803Smarcel	int flags, fmt;
663130803Smarcel	struct proc *p;
664130803Smarcel{
665130803Smarcel	int unit = ccdunit(dev);
666130803Smarcel	struct ccd_softc *cs;
667130803Smarcel	int error = 0, part;
668130803Smarcel
669130803Smarcel#ifdef DEBUG
670130803Smarcel	if (ccddebug & CCDB_FOLLOW)
671130803Smarcel		printf("ccdclose(%x, %x)\n", dev, flags);
672130803Smarcel#endif
673130803Smarcel
674130803Smarcel	if (unit >= numccd)
675130803Smarcel		return (ENXIO);
676130803Smarcel	cs = &ccd_softc[unit];
677130803Smarcel
678130803Smarcel	if (error = ccdlock(cs))
679130803Smarcel		return (error);
680130803Smarcel
681130803Smarcel	part = ccdpart(dev);
682130803Smarcel
683130803Smarcel	/* ...that much closer to allowing unconfiguration... */
684130803Smarcel	switch (fmt) {
685130803Smarcel	case S_IFCHR:
686130803Smarcel		cs->sc_dkdev.dk_copenmask &= ~(1 << part);
687130803Smarcel		break;
688130803Smarcel
689130803Smarcel	case S_IFBLK:
690130803Smarcel		cs->sc_dkdev.dk_bopenmask &= ~(1 << part);
691130803Smarcel		break;
692130803Smarcel	}
693130803Smarcel	cs->sc_dkdev.dk_openmask =
694130803Smarcel	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
695130803Smarcel
696130803Smarcel	ccdunlock(cs);
697130803Smarcel	return (0);
698130803Smarcel}
699130803Smarcel
700130803Smarcelstatic void
701130803Smarcelccdstrategy(bp)
702130803Smarcel	register struct buf *bp;
703130803Smarcel{
704130803Smarcel	register int unit = ccdunit(bp->b_dev);
705130803Smarcel	register struct ccd_softc *cs = &ccd_softc[unit];
706130803Smarcel	register int s;
707130803Smarcel	int wlabel;
708130803Smarcel	struct disklabel *lp;
709130803Smarcel
710130803Smarcel#ifdef DEBUG
711130803Smarcel	if (ccddebug & CCDB_FOLLOW)
712130803Smarcel		printf("ccdstrategy(%x): unit %d\n", bp, unit);
713130803Smarcel#endif
714130803Smarcel	if ((cs->sc_flags & CCDF_INITED) == 0) {
715130803Smarcel		bp->b_error = ENXIO;
716130803Smarcel		bp->b_flags |= B_ERROR;
717130803Smarcel		goto done;
718130803Smarcel	}
719130803Smarcel
720130803Smarcel	/* If it's a nil transfer, wake up the top half now. */
721130803Smarcel	if (bp->b_bcount == 0)
722130803Smarcel		goto done;
723130803Smarcel
724130803Smarcel	lp = &cs->sc_dkdev.dk_label;
725130803Smarcel
726130803Smarcel	/*
727130803Smarcel	 * Do bounds checking and adjust transfer.  If there's an
728130803Smarcel	 * error, the bounds check will flag that for us.
729130803Smarcel	 */
730130803Smarcel	wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
731130803Smarcel	if (ccdpart(bp->b_dev) != RAW_PART)
732130803Smarcel		if (bounds_check_with_label(bp, lp, wlabel) <= 0)
733130803Smarcel			goto done;
734130803Smarcel
735130803Smarcel	bp->b_resid = bp->b_bcount;
736130803Smarcel
737130803Smarcel	/*
738130803Smarcel	 * "Start" the unit.
739130803Smarcel	 */
740130803Smarcel	s = splbio();
741130803Smarcel	ccdstart(cs, bp);
742130803Smarcel	splx(s);
743130803Smarcel	return;
744130803Smarceldone:
745130803Smarcel	biodone(bp);
746130803Smarcel}
747130803Smarcel
748130803Smarcelstatic void
749130803Smarcelccdstart(cs, bp)
750130803Smarcel	register struct ccd_softc *cs;
751130803Smarcel	register struct buf *bp;
752130803Smarcel{
753130803Smarcel	register long bcount, rcount;
754130803Smarcel	struct ccdbuf *cbp[4];
755130803Smarcel	/* XXX! : 2 reads and 2 writes for RAID 4/5 */
756130803Smarcel	caddr_t addr;
757130803Smarcel	daddr_t bn;
758130803Smarcel	struct partition *pp;
759130803Smarcel
760130803Smarcel#ifdef DEBUG
761130803Smarcel	if (ccddebug & CCDB_FOLLOW)
762130803Smarcel		printf("ccdstart(%x, %x)\n", cs, bp);
763130803Smarcel#endif
764130803Smarcel
765130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
766130803Smarcel	/*
767130803Smarcel	 * Instrumentation (not very meaningful)
768130803Smarcel	 */
769130803Smarcel	cs->sc_nactive++;
770130803Smarcel	if (cs->sc_dk >= 0) {
771130803Smarcel		dk_busy |= 1 << cs->sc_dk;
772130803Smarcel		dk_xfer[cs->sc_dk]++;
773130803Smarcel		dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
774130803Smarcel	}
775130803Smarcel#endif
776130803Smarcel
777130803Smarcel	/*
778130803Smarcel	 * Translate the partition-relative block number to an absolute.
779130803Smarcel	 */
780130803Smarcel	bn = bp->b_blkno;
781130803Smarcel	if (ccdpart(bp->b_dev) != RAW_PART) {
782130803Smarcel		pp = &cs->sc_dkdev.dk_label.d_partitions[ccdpart(bp->b_dev)];
783130803Smarcel		bn += pp->p_offset;
784130803Smarcel	}
785130803Smarcel
786130803Smarcel	/*
787130803Smarcel	 * Allocate component buffers and fire off the requests
788130803Smarcel	 */
789130803Smarcel	addr = bp->b_data;
790130803Smarcel	for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
791130803Smarcel		ccdbuffer(cbp, cs, bp, bn, addr, bcount);
792130803Smarcel		rcount = cbp[0]->cb_buf.b_bcount;
793130803Smarcel		if ((cbp[0]->cb_buf.b_flags & B_READ) == 0)
794130803Smarcel			cbp[0]->cb_buf.b_vp->v_numoutput++;
795130803Smarcel		VOP_STRATEGY(&cbp[0]->cb_buf);
796130803Smarcel		if (cs->sc_cflags & CCDF_MIRROR &&
797130803Smarcel		    (cbp[0]->cb_buf.b_flags & B_READ) == 0) {
798130803Smarcel			/* mirror, start another write */
799130803Smarcel			cbp[1]->cb_buf.b_vp->v_numoutput++;
800130803Smarcel			VOP_STRATEGY(&cbp[1]->cb_buf);
801130803Smarcel		}
802130803Smarcel		bn += btodb(rcount);
803130803Smarcel		addr += rcount;
804130803Smarcel	}
805130803Smarcel}
806130803Smarcel
807130803Smarcel/*
808130803Smarcel * Build a component buffer header.
809130803Smarcel */
810130803Smarcelstatic void
811130803Smarcelccdbuffer(cb, cs, bp, bn, addr, bcount)
812130803Smarcel	register struct ccdbuf **cb;
813130803Smarcel	register struct ccd_softc *cs;
814130803Smarcel	struct buf *bp;
815130803Smarcel	daddr_t bn;
816130803Smarcel	caddr_t addr;
817130803Smarcel	long bcount;
818130803Smarcel{
819130803Smarcel	register struct ccdcinfo *ci, *ci2 = NULL;	/* XXX */
820130803Smarcel	register struct ccdbuf *cbp;
821130803Smarcel	register daddr_t cbn, cboff;
822130803Smarcel
823130803Smarcel#ifdef DEBUG
824130803Smarcel	if (ccddebug & CCDB_IO)
825130803Smarcel		printf("ccdbuffer(%x, %x, %d, %x, %d)\n",
826130803Smarcel		       cs, bp, bn, addr, bcount);
827130803Smarcel#endif
828130803Smarcel	/*
829130803Smarcel	 * Determine which component bn falls in.
830130803Smarcel	 */
831130803Smarcel	cbn = bn;
832130803Smarcel	cboff = 0;
833130803Smarcel
834130803Smarcel	/*
835130803Smarcel	 * Serially concatenated
836130803Smarcel	 */
837130803Smarcel	if (cs->sc_ileave == 0) {
838130803Smarcel		register daddr_t sblk;
839130803Smarcel
840130803Smarcel		sblk = 0;
841130803Smarcel		for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
842130803Smarcel			sblk += ci->ci_size;
843130803Smarcel		cbn -= sblk;
844130803Smarcel	}
845130803Smarcel	/*
846130803Smarcel	 * Interleaved
847130803Smarcel	 */
848130803Smarcel	else {
849130803Smarcel		register struct ccdiinfo *ii;
850130803Smarcel		int ccdisk, off;
851130803Smarcel
852130803Smarcel		cboff = cbn % cs->sc_ileave;
853130803Smarcel		cbn /= cs->sc_ileave;
854130803Smarcel		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
855130803Smarcel			if (ii->ii_startblk > cbn)
856130803Smarcel				break;
857130803Smarcel		ii--;
858130803Smarcel		off = cbn - ii->ii_startblk;
859130803Smarcel		if (ii->ii_ndisk == 1) {
860130803Smarcel			ccdisk = ii->ii_index[0];
861130803Smarcel			cbn = ii->ii_startoff + off;
862130803Smarcel		} else {
863130803Smarcel			if (cs->sc_cflags & CCDF_MIRROR) {
864130803Smarcel				ccdisk = ii->ii_index[off % (ii->ii_ndisk/2)];
865130803Smarcel				cbn = ii->ii_startoff + off / (ii->ii_ndisk/2);
866130803Smarcel				/* mirrored data */
867130803Smarcel				ci2 = &cs->sc_cinfo[ccdisk + ii->ii_ndisk/2];
868130803Smarcel			}
869130803Smarcel			else if (cs->sc_cflags & CCDF_PARITY) {
870130803Smarcel				ccdisk = ii->ii_index[off % (ii->ii_ndisk-1)];
871130803Smarcel				cbn = ii->ii_startoff + off / (ii->ii_ndisk-1);
872130803Smarcel				if (cbn % ii->ii_ndisk <= ccdisk)
873130803Smarcel					ccdisk++;
874130803Smarcel			}
875130803Smarcel			else {
876130803Smarcel				ccdisk = ii->ii_index[off % ii->ii_ndisk];
877130803Smarcel				cbn = ii->ii_startoff + off / ii->ii_ndisk;
878130803Smarcel			}
879130803Smarcel		}
880130803Smarcel		cbn *= cs->sc_ileave;
881130803Smarcel		ci = &cs->sc_cinfo[ccdisk];
882130803Smarcel	}
883130803Smarcel
884130803Smarcel	/*
885130803Smarcel	 * Fill in the component buf structure.
886130803Smarcel	 */
887130803Smarcel	cbp = getccdbuf();
888130803Smarcel	cbp->cb_buf.b_flags = bp->b_flags | B_CALL;
889130803Smarcel	cbp->cb_buf.b_iodone = (void (*)(struct buf *))ccdiodone;
890130803Smarcel	cbp->cb_buf.b_proc = bp->b_proc;
891130803Smarcel	cbp->cb_buf.b_dev = ci->ci_dev;		/* XXX */
892130803Smarcel	cbp->cb_buf.b_blkno = cbn + cboff + CCD_OFFSET;
893130803Smarcel	cbp->cb_buf.b_data = addr;
894130803Smarcel	cbp->cb_buf.b_vp = ci->ci_vp;
895130803Smarcel	if (cs->sc_ileave == 0)
896130803Smarcel		cbp->cb_buf.b_bcount = dbtob(ci->ci_size - cbn);
897130803Smarcel	else
898130803Smarcel		cbp->cb_buf.b_bcount = dbtob(cs->sc_ileave - cboff);
899130803Smarcel	if (cbp->cb_buf.b_bcount > bcount)
900130803Smarcel		cbp->cb_buf.b_bcount = bcount;
901130803Smarcel
902130803Smarcel 	cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
903130803Smarcel
904130803Smarcel	/*
905130803Smarcel	 * context for ccdiodone
906130803Smarcel	 */
907130803Smarcel	cbp->cb_obp = bp;
908130803Smarcel	cbp->cb_unit = cs - ccd_softc;
909130803Smarcel	cbp->cb_comp = ci - cs->sc_cinfo;
910130803Smarcel
911130803Smarcel#ifdef DEBUG
912130803Smarcel	if (ccddebug & CCDB_IO)
913130803Smarcel		printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
914130803Smarcel		       ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->cb_buf.b_blkno,
915130803Smarcel		       cbp->cb_buf.b_data, cbp->cb_buf.b_bcount);
916130803Smarcel#endif
917130803Smarcel	cb[0] = cbp;
918130803Smarcel	if (cs->sc_cflags & CCDF_MIRROR &&
919130803Smarcel	    (cbp->cb_buf.b_flags & B_READ) == 0) {
920130803Smarcel		/* mirror, start one more write */
921130803Smarcel		cbp = getccdbuf();
922130803Smarcel		*cbp = *cb[0];
923130803Smarcel		cbp->cb_buf.b_dev = ci2->ci_dev;
924130803Smarcel		cbp->cb_buf.b_vp = ci2->ci_vp;
925130803Smarcel		cbp->cb_comp = ci2 - cs->sc_cinfo;
926130803Smarcel		cb[1] = cbp;
927130803Smarcel		/* link together the ccdbuf's and clear "mirror done" flag */
928130803Smarcel		cb[0]->cb_mirror = cb[1];
929130803Smarcel		cb[1]->cb_mirror = cb[0];
930130803Smarcel		cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
931130803Smarcel		cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
932130803Smarcel	}
933130803Smarcel}
934130803Smarcel
935130803Smarcelstatic void
936130803Smarcelccdintr(cs, bp)
937130803Smarcel	register struct ccd_softc *cs;
938130803Smarcel	register struct buf *bp;
939130803Smarcel{
940130803Smarcel
941130803Smarcel      s = splbio();
942130803Smarcel#ifdef DEBUG
943130803Smarcel	if (ccddebug & CCDB_FOLLOW)
944130803Smarcel		printf("ccdintr(%x, %x)\n", cs, bp);
945130803Smarcel#endif
946130803Smarcel	/*
947130803Smarcel	 * Request is done for better or worse, wakeup the top half.
948130803Smarcel	 */
949130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
950130803Smarcel	--cs->sc_nactive;
951130803Smarcel#ifdef DIAGNOSTIC
952130803Smarcel	if (cs->sc_nactive < 0)
953130803Smarcel		panic("ccdintr: ccd%d: sc_nactive < 0", cs->sc_unit);
954130803Smarcel#endif
955130803Smarcel
956130803Smarcel	if (cs->sc_nactive == 0 && cs->sc_dk >= 0)
957130803Smarcel		dk_busy &= ~(1 << cs->sc_dk);
958130803Smarcel#endif
959130803Smarcel	if (bp->b_flags & B_ERROR)
960130803Smarcel		bp->b_resid = bp->b_bcount;
961130803Smarcel	biodone(bp);
962130803Smarcel}
963130803Smarcel
964130803Smarcel/*
965130803Smarcel * Called at interrupt time.
966130803Smarcel * Mark the component as done and if all components are done,
967130803Smarcel * take a ccd interrupt.
968130803Smarcel */
969130803Smarcelstatic void
970130803Smarcelccdiodone(cbp)
971130803Smarcel	struct ccdbuf *cbp;
972130803Smarcel{
973130803Smarcel	register struct buf *bp = cbp->cb_obp;
974130803Smarcel	register int unit = cbp->cb_unit;
975130803Smarcel	int count, s;
976130803Smarcel
977130803Smarcel	s = splbio();
978130803Smarcel#ifdef DEBUG
979130803Smarcel	if (ccddebug & CCDB_FOLLOW)
980130803Smarcel		printf("ccdiodone(%x)\n", cbp);
981130803Smarcel	if (ccddebug & CCDB_IO) {
982130803Smarcel		printf("ccdiodone: bp %x bcount %d resid %d\n",
983130803Smarcel		       bp, bp->b_bcount, bp->b_resid);
984130803Smarcel		printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
985130803Smarcel		       cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
986130803Smarcel		       cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
987130803Smarcel		       cbp->cb_buf.b_bcount);
988130803Smarcel	}
989130803Smarcel#endif
990130803Smarcel
991130803Smarcel	if (cbp->cb_buf.b_flags & B_ERROR) {
992130803Smarcel		bp->b_flags |= B_ERROR;
993130803Smarcel		bp->b_error = cbp->cb_buf.b_error ? cbp->cb_buf.b_error : EIO;
994130803Smarcel#ifdef DEBUG
995130803Smarcel		printf("ccd%d: error %d on component %d\n",
996130803Smarcel		       unit, bp->b_error, cbp->cb_comp);
997130803Smarcel#endif
998130803Smarcel	}
999130803Smarcel
1000130803Smarcel	if (ccd_softc[unit].sc_cflags & CCDF_MIRROR &&
1001130803Smarcel	    (cbp->cb_buf.b_flags & B_READ) == 0)
1002130803Smarcel		if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1003130803Smarcel			/* I'm done before my counterpart, so just set
1004130803Smarcel			   partner's flag and return */
1005130803Smarcel			cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
1006130803Smarcel			putccdbuf(cbp);
1007130803Smarcel			splx(s);
1008130803Smarcel			return;
1009130803Smarcel		}
1010130803Smarcel
1011130803Smarcel	count = cbp->cb_buf.b_bcount;
1012130803Smarcel	putccdbuf(cbp);
1013130803Smarcel
1014130803Smarcel	/*
1015130803Smarcel	 * If all done, "interrupt".
1016130803Smarcel	 */
1017130803Smarcel	bp->b_resid -= count;
1018130803Smarcel	if (bp->b_resid < 0)
1019130803Smarcel		panic("ccdiodone: count");
1020130803Smarcel	if (bp->b_resid == 0)
1021130803Smarcel		ccdintr(&ccd_softc[unit], bp);
1022130803Smarcel	splx(s);
1023130803Smarcel}
1024130803Smarcel
1025130803Smarcelstatic int
1026130803Smarcelccdioctl(dev, cmd, data, flag, p)
1027130803Smarcel	dev_t dev;
1028130803Smarcel	int cmd;
1029130803Smarcel	caddr_t data;
1030130803Smarcel	int flag;
1031130803Smarcel	struct proc *p;
1032130803Smarcel{
1033130803Smarcel	int unit = ccdunit(dev);
1034130803Smarcel	int i, j, lookedup = 0, error = 0;
1035130803Smarcel	int part, pmask, s;
1036130803Smarcel	struct ccd_softc *cs;
1037130803Smarcel	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
1038130803Smarcel	struct ccddevice ccd;
1039130803Smarcel	char **cpp;
1040130803Smarcel	struct vnode **vpp;
1041130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1042130803Smarcel	extern int dkn;
1043130803Smarcel#endif
1044130803Smarcel
1045130803Smarcel	if (unit >= numccd)
1046130803Smarcel		return (ENXIO);
1047130803Smarcel	cs = &ccd_softc[unit];
1048130803Smarcel
1049130803Smarcel	bzero(&ccd, sizeof(ccd));
1050130803Smarcel
1051130803Smarcel	switch (cmd) {
1052130803Smarcel	case CCDIOCSET:
1053130803Smarcel		if (cs->sc_flags & CCDF_INITED)
1054130803Smarcel			return (EBUSY);
1055130803Smarcel
1056130803Smarcel		if ((flag & FWRITE) == 0)
1057130803Smarcel			return (EBADF);
1058130803Smarcel
1059130803Smarcel		if (error = ccdlock(cs))
1060130803Smarcel			return (error);
1061130803Smarcel
1062130803Smarcel		/* Fill in some important bits. */
1063130803Smarcel		ccd.ccd_unit = unit;
1064130803Smarcel		ccd.ccd_interleave = ccio->ccio_ileave;
1065130803Smarcel		if (ccd.ccd_interleave == 0 &&
1066130803Smarcel		    ((ccio->ccio_flags & CCDF_MIRROR) ||
1067130803Smarcel		     (ccio->ccio_flags & CCDF_PARITY))) {
1068130803Smarcel			printf("ccd%d: disabling mirror/parity, interleave is 0\n", unit);
1069130803Smarcel			ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY);
1070130803Smarcel		}
1071130803Smarcel		if ((ccio->ccio_flags & CCDF_MIRROR) &&
1072130803Smarcel		    (ccio->ccio_flags & CCDF_PARITY)) {
1073130803Smarcel			printf("ccd%d: can't specify both mirror and parity, using mirror\n", unit);
1074130803Smarcel			ccio->ccio_flags &= ~CCDF_PARITY;
1075130803Smarcel		}
1076130803Smarcel		if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) &&
1077130803Smarcel		    !(ccio->ccio_flags & CCDF_UNIFORM)) {
1078130803Smarcel			printf("ccd%d: mirror/parity forces uniform flag\n",
1079130803Smarcel			       unit);
1080130803Smarcel			ccio->ccio_flags |= CCDF_UNIFORM;
1081130803Smarcel		}
1082130803Smarcel		ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK;
1083130803Smarcel
1084130803Smarcel		/*
1085130803Smarcel		 * Allocate space for and copy in the array of
1086130803Smarcel		 * componet pathnames and device numbers.
1087130803Smarcel		 */
1088130803Smarcel		cpp = malloc(ccio->ccio_ndisks * sizeof(char *),
1089130803Smarcel		    M_DEVBUF, M_WAITOK);
1090130803Smarcel		vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *),
1091130803Smarcel		    M_DEVBUF, M_WAITOK);
1092130803Smarcel
1093130803Smarcel		error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
1094130803Smarcel		    ccio->ccio_ndisks * sizeof(char **));
1095130803Smarcel		if (error) {
1096130803Smarcel			free(vpp, M_DEVBUF);
1097130803Smarcel			free(cpp, M_DEVBUF);
1098130803Smarcel			ccdunlock(cs);
1099130803Smarcel			return (error);
1100130803Smarcel		}
1101130803Smarcel
1102130803Smarcel#ifdef DEBUG
1103130803Smarcel		if (ccddebug & CCDB_INIT)
1104130803Smarcel			for (i = 0; i < ccio->ccio_ndisks; ++i)
1105130803Smarcel				printf("ccdioctl: component %d: 0x%x\n",
1106130803Smarcel				    i, cpp[i]);
1107130803Smarcel#endif
1108130803Smarcel
1109130803Smarcel		for (i = 0; i < ccio->ccio_ndisks; ++i) {
1110130803Smarcel#ifdef DEBUG
1111130803Smarcel			if (ccddebug & CCDB_INIT)
1112130803Smarcel				printf("ccdioctl: lookedup = %d\n", lookedup);
1113130803Smarcel#endif
1114130803Smarcel			if (error = ccdlookup(cpp[i], p, &vpp[i])) {
1115130803Smarcel				for (j = 0; j < lookedup; ++j)
1116130803Smarcel					(void)vn_close(vpp[j], FREAD|FWRITE,
1117130803Smarcel					    p->p_ucred, p);
1118130803Smarcel				free(vpp, M_DEVBUF);
1119130803Smarcel				free(cpp, M_DEVBUF);
1120130803Smarcel				ccdunlock(cs);
1121130803Smarcel				return (error);
1122130803Smarcel			}
1123130803Smarcel			++lookedup;
1124130803Smarcel		}
1125130803Smarcel		ccd.ccd_cpp = cpp;
1126130803Smarcel		ccd.ccd_vpp = vpp;
1127130803Smarcel		ccd.ccd_ndev = ccio->ccio_ndisks;
1128130803Smarcel
1129130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1130130803Smarcel		/*
1131130803Smarcel		 * Assign disk index first so that init routine
1132130803Smarcel		 * can use it (saves having the driver drag around
1133130803Smarcel		 * the ccddevice pointer just to set up the dk_*
1134130803Smarcel		 * info in the open routine).
1135130803Smarcel		 */
1136130803Smarcel		if (dkn < DK_NDRIVE)
1137130803Smarcel			ccd.ccd_dk = dkn++;
1138130803Smarcel		else
1139130803Smarcel			ccd.ccd_dk = -1;
1140130803Smarcel#endif
1141130803Smarcel
1142130803Smarcel		/*
1143130803Smarcel		 * Initialize the ccd.  Fills in the softc for us.
1144130803Smarcel		 */
1145130803Smarcel		if (error = ccdinit(&ccd, cpp, p)) {
1146130803Smarcel#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1147130803Smarcel			if (ccd.ccd_dk >= 0)
1148130803Smarcel				--dkn;
1149130803Smarcel#endif
1150130803Smarcel			for (j = 0; j < lookedup; ++j)
1151130803Smarcel				(void)vn_close(vpp[j], FREAD|FWRITE,
1152130803Smarcel				    p->p_ucred, p);
1153130803Smarcel			bzero(&ccd_softc[unit], sizeof(struct ccd_softc));
1154130803Smarcel			free(vpp, M_DEVBUF);
1155130803Smarcel			free(cpp, M_DEVBUF);
1156130803Smarcel			ccdunlock(cs);
1157130803Smarcel			return (error);
1158130803Smarcel		}
1159130803Smarcel
1160130803Smarcel		/*
1161130803Smarcel		 * The ccd has been successfully initialized, so
1162130803Smarcel		 * we can place it into the array and read the disklabel.
1163130803Smarcel		 */
1164130803Smarcel		bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1165130803Smarcel		ccio->ccio_unit = unit;
1166130803Smarcel		ccio->ccio_size = cs->sc_size;
1167130803Smarcel		ccdgetdisklabel(dev);
1168130803Smarcel
1169130803Smarcel		ccdunlock(cs);
1170130803Smarcel
1171130803Smarcel		break;
1172130803Smarcel
1173130803Smarcel	case CCDIOCCLR:
1174130803Smarcel		if ((cs->sc_flags & CCDF_INITED) == 0)
1175130803Smarcel			return (ENXIO);
1176130803Smarcel
1177130803Smarcel		if ((flag & FWRITE) == 0)
1178130803Smarcel			return (EBADF);
1179130803Smarcel
1180130803Smarcel		if (error = ccdlock(cs))
1181130803Smarcel			return (error);
1182130803Smarcel
1183130803Smarcel		/*
1184130803Smarcel		 * Don't unconfigure if any other partitions are open
1185130803Smarcel		 * or if both the character and block flavors of this
1186130803Smarcel		 * partition are open.
1187130803Smarcel		 */
1188130803Smarcel		part = ccdpart(dev);
1189130803Smarcel		pmask = (1 << part);
1190130803Smarcel		if ((cs->sc_dkdev.dk_openmask & ~pmask) ||
1191130803Smarcel		    ((cs->sc_dkdev.dk_bopenmask & pmask) &&
1192130803Smarcel		    (cs->sc_dkdev.dk_copenmask & pmask))) {
1193130803Smarcel			ccdunlock(cs);
1194130803Smarcel			return (EBUSY);
1195130803Smarcel		}
1196130803Smarcel
1197130803Smarcel		/*
1198130803Smarcel		 * Free ccd_softc information and clear entry.
1199130803Smarcel		 */
1200130803Smarcel
1201130803Smarcel		/* Close the components and free their pathnames. */
1202130803Smarcel		for (i = 0; i < cs->sc_nccdisks; ++i) {
1203130803Smarcel			/*
1204130803Smarcel			 * XXX: this close could potentially fail and
1205130803Smarcel			 * cause Bad Things.  Maybe we need to force
1206130803Smarcel			 * the close to happen?
1207130803Smarcel			 */
1208130803Smarcel#ifdef DEBUG
1209130803Smarcel			if (ccddebug & CCDB_VNODE)
1210130803Smarcel				vprint("CCDIOCCLR: vnode info",
1211130803Smarcel				    cs->sc_cinfo[i].ci_vp);
1212130803Smarcel#endif
1213130803Smarcel			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1214130803Smarcel			    p->p_ucred, p);
1215130803Smarcel			free(cs->sc_cinfo[i].ci_path, M_DEVBUF);
1216130803Smarcel		}
1217130803Smarcel
1218130803Smarcel		/* Free interleave index. */
1219130803Smarcel		for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
1220130803Smarcel			free(cs->sc_itable[i].ii_index, M_DEVBUF);
1221130803Smarcel
1222130803Smarcel		/* Free component info and interleave table. */
1223130803Smarcel		free(cs->sc_cinfo, M_DEVBUF);
1224130803Smarcel		free(cs->sc_itable, M_DEVBUF);
1225130803Smarcel		cs->sc_flags &= ~CCDF_INITED;
1226130803Smarcel
1227130803Smarcel		/*
1228130803Smarcel		 * Free ccddevice information and clear entry.
1229130803Smarcel		 */
1230130803Smarcel		free(ccddevs[unit].ccd_cpp, M_DEVBUF);
1231130803Smarcel		free(ccddevs[unit].ccd_vpp, M_DEVBUF);
1232130803Smarcel		ccd.ccd_dk = -1;
1233130803Smarcel		bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1234130803Smarcel
1235130803Smarcel		/* This must be atomic. */
1236130803Smarcel		s = splhigh();
1237130803Smarcel		ccdunlock(cs);
1238130803Smarcel		bzero(cs, sizeof(struct ccd_softc));
1239130803Smarcel		splx(s);
1240130803Smarcel
1241130803Smarcel		break;
1242130803Smarcel
1243130803Smarcel	case DIOCGDINFO:
1244130803Smarcel		if ((cs->sc_flags & CCDF_INITED) == 0)
1245130803Smarcel			return (ENXIO);
1246130803Smarcel
1247130803Smarcel		*(struct disklabel *)data = cs->sc_dkdev.dk_label;
1248130803Smarcel		break;
1249130803Smarcel
1250130803Smarcel	case DIOCGPART:
1251130803Smarcel		if ((cs->sc_flags & CCDF_INITED) == 0)
1252130803Smarcel			return (ENXIO);
1253130803Smarcel
1254130803Smarcel		((struct partinfo *)data)->disklab = &cs->sc_dkdev.dk_label;
1255130803Smarcel		((struct partinfo *)data)->part =
1256130803Smarcel		    &cs->sc_dkdev.dk_label.d_partitions[ccdpart(dev)];
1257130803Smarcel		break;
1258130803Smarcel
1259130803Smarcel	case DIOCWDINFO:
1260130803Smarcel	case DIOCSDINFO:
1261130803Smarcel		if ((cs->sc_flags & CCDF_INITED) == 0)
1262130803Smarcel			return (ENXIO);
1263130803Smarcel
1264130803Smarcel		if ((flag & FWRITE) == 0)
1265130803Smarcel			return (EBADF);
1266130803Smarcel
1267130803Smarcel		if (error = ccdlock(cs))
1268130803Smarcel			return (error);
1269130803Smarcel
1270130803Smarcel		cs->sc_flags |= CCDF_LABELLING;
1271130803Smarcel
1272130803Smarcel		error = setdisklabel(&cs->sc_dkdev.dk_label,
1273130803Smarcel		    (struct disklabel *)data, 0);
1274130803Smarcel				/*, &cs->sc_dkdev.dk_cpulabel); */
1275130803Smarcel		if (error == 0) {
1276130803Smarcel			if (cmd == DIOCWDINFO)
1277130803Smarcel				error = writedisklabel(CCDLABELDEV(dev),
1278130803Smarcel				    ccdstrategy, &cs->sc_dkdev.dk_label);
1279130803Smarcel				/*
1280130803Smarcel				    &cs->sc_dkdev.dk_cpulabel); */
1281130803Smarcel		}
1282130803Smarcel
1283130803Smarcel		cs->sc_flags &= ~CCDF_LABELLING;
1284130803Smarcel
1285130803Smarcel		ccdunlock(cs);
1286130803Smarcel
1287130803Smarcel		if (error)
1288130803Smarcel			return (error);
1289130803Smarcel		break;
1290130803Smarcel
1291130803Smarcel	case DIOCWLABEL:
1292130803Smarcel		if ((cs->sc_flags & CCDF_INITED) == 0)
1293130803Smarcel			return (ENXIO);
1294130803Smarcel
1295130803Smarcel		if ((flag & FWRITE) == 0)
1296130803Smarcel			return (EBADF);
1297130803Smarcel		if (*(int *)data != 0)
1298130803Smarcel			cs->sc_flags |= CCDF_WLABEL;
1299130803Smarcel		else
1300130803Smarcel			cs->sc_flags &= ~CCDF_WLABEL;
1301130803Smarcel		break;
1302130803Smarcel
1303130803Smarcel	default:
1304130803Smarcel		return (ENOTTY);
1305130803Smarcel	}
1306130803Smarcel
1307130803Smarcel	return (0);
1308130803Smarcel}
1309130803Smarcel
1310130803Smarcelstatic int
1311130803Smarcelccdsize(dev)
1312130803Smarcel	dev_t dev;
1313130803Smarcel{
1314130803Smarcel	struct ccd_softc *cs;
1315130803Smarcel	int part, size;
1316130803Smarcel
1317130803Smarcel	if (ccdopen(dev, 0, S_IFBLK, curproc))
1318130803Smarcel		return (-1);
1319130803Smarcel
1320130803Smarcel	cs = &ccd_softc[ccdunit(dev)];
1321130803Smarcel	part = ccdpart(dev);
1322130803Smarcel
1323130803Smarcel	if ((cs->sc_flags & CCDF_INITED) == 0)
1324130803Smarcel		return (-1);
1325130803Smarcel
1326130803Smarcel	if (cs->sc_dkdev.dk_label.d_partitions[part].p_fstype != FS_SWAP)
1327130803Smarcel		size = -1;
1328130803Smarcel	else
1329130803Smarcel		size = cs->sc_dkdev.dk_label.d_partitions[part].p_size;
1330130803Smarcel
1331130803Smarcel	if (ccdclose(dev, 0, S_IFBLK, curproc))
1332130803Smarcel		return (-1);
1333130803Smarcel
1334130803Smarcel	return (size);
1335130803Smarcel}
1336130803Smarcel
1337130803Smarcelstatic int
1338130803Smarcelccddump(dev)
1339130803Smarcel	dev_t dev;
1340130803Smarcel{
1341130803Smarcel
1342130803Smarcel	/* Not implemented. */
1343130803Smarcel	return ENXIO;
1344130803Smarcel}
1345130803Smarcel
1346130803Smarcel/*
1347130803Smarcel * Lookup the provided name in the filesystem.  If the file exists,
1348130803Smarcel * is a valid block device, and isn't being used by anyone else,
1349130803Smarcel * set *vpp to the file's vnode.
1350130803Smarcel */
1351130803Smarcelstatic int
1352130803Smarcelccdlookup(path, p, vpp)
1353130803Smarcel	char *path;
1354130803Smarcel	struct proc *p;
1355130803Smarcel	struct vnode **vpp;	/* result */
1356130803Smarcel{
1357130803Smarcel	struct nameidata nd;
1358130803Smarcel	struct vnode *vp;
1359130803Smarcel	struct vattr va;
1360130803Smarcel	int error;
1361130803Smarcel
1362130803Smarcel	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p);
1363130803Smarcel	if (error = vn_open(&nd, FREAD|FWRITE, 0)) {
1364130803Smarcel#ifdef DEBUG
1365130803Smarcel		if (ccddebug & CCDB_FOLLOW|CCDB_INIT)
1366130803Smarcel			printf("ccdlookup: vn_open error = %d\n", error);
1367130803Smarcel#endif
1368130803Smarcel		return (error);
1369130803Smarcel	}
1370130803Smarcel	vp = nd.ni_vp;
1371130803Smarcel
1372130803Smarcel	if (vp->v_usecount > 1) {
1373130803Smarcel		VOP_UNLOCK(vp, 0, p);
1374130803Smarcel		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1375130803Smarcel		return (EBUSY);
1376130803Smarcel	}
1377130803Smarcel
1378130803Smarcel	if (error = VOP_GETATTR(vp, &va, p->p_ucred, p)) {
1379130803Smarcel#ifdef DEBUG
1380130803Smarcel		if (ccddebug & CCDB_FOLLOW|CCDB_INIT)
1381130803Smarcel			printf("ccdlookup: getattr error = %d\n", error);
1382130803Smarcel#endif
1383130803Smarcel		VOP_UNLOCK(vp, 0, p);
1384130803Smarcel		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1385130803Smarcel		return (error);
1386130803Smarcel	}
1387130803Smarcel
1388130803Smarcel	/* XXX: eventually we should handle VREG, too. */
1389130803Smarcel	if (va.va_type != VBLK) {
1390130803Smarcel		VOP_UNLOCK(vp, 0, p);
1391130803Smarcel		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1392130803Smarcel		return (ENOTBLK);
1393130803Smarcel	}
1394130803Smarcel
1395130803Smarcel#ifdef DEBUG
1396130803Smarcel	if (ccddebug & CCDB_VNODE)
1397130803Smarcel		vprint("ccdlookup: vnode info", vp);
1398130803Smarcel#endif
1399130803Smarcel
1400130803Smarcel	VOP_UNLOCK(vp, 0, p);
1401130803Smarcel	*vpp = vp;
1402130803Smarcel	return (0);
1403130803Smarcel}
1404130803Smarcel
1405130803Smarcel/*
1406130803Smarcel * Read the disklabel from the ccd.  If one is not present, fake one
1407130803Smarcel * up.
1408130803Smarcel */
1409130803Smarcelstatic void
1410130803Smarcelccdgetdisklabel(dev)
1411130803Smarcel	dev_t dev;
1412130803Smarcel{
1413130803Smarcel	int unit = ccdunit(dev);
1414130803Smarcel	struct ccd_softc *cs = &ccd_softc[unit];
1415130803Smarcel	char *errstring;
1416130803Smarcel	struct disklabel *lp = &cs->sc_dkdev.dk_label;
1417130803Smarcel	struct ccdgeom *ccg = &cs->sc_geom;
1418130803Smarcel
1419130803Smarcel	bzero(lp, sizeof(*lp));
1420130803Smarcel
1421130803Smarcel	lp->d_secperunit = cs->sc_size;
1422130803Smarcel	lp->d_secsize = ccg->ccg_secsize;
1423130803Smarcel	lp->d_nsectors = ccg->ccg_nsectors;
1424130803Smarcel	lp->d_ntracks = ccg->ccg_ntracks;
1425130803Smarcel	lp->d_ncylinders = ccg->ccg_ncylinders;
1426130803Smarcel	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1427130803Smarcel
1428130803Smarcel	strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
1429130803Smarcel	lp->d_type = DTYPE_CCD;
1430130803Smarcel	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1431130803Smarcel	lp->d_rpm = 3600;
1432130803Smarcel	lp->d_interleave = 1;
1433130803Smarcel	lp->d_flags = 0;
1434130803Smarcel
1435130803Smarcel	lp->d_partitions[RAW_PART].p_offset = 0;
1436130803Smarcel	lp->d_partitions[RAW_PART].p_size = cs->sc_size;
1437130803Smarcel	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1438130803Smarcel	lp->d_npartitions = RAW_PART + 1;
1439130803Smarcel
1440130803Smarcel	lp->d_bbsize = BBSIZE;				/* XXX */
1441130803Smarcel	lp->d_sbsize = SBSIZE;				/* XXX */
1442130803Smarcel
1443130803Smarcel	lp->d_magic = DISKMAGIC;
1444130803Smarcel	lp->d_magic2 = DISKMAGIC;
1445130803Smarcel	lp->d_checksum = dkcksum(&cs->sc_dkdev.dk_label);
1446130803Smarcel
1447130803Smarcel	/*
1448130803Smarcel	 * Call the generic disklabel extraction routine.
1449130803Smarcel	 */
1450130803Smarcel	if (errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy,
1451130803Smarcel	    &cs->sc_dkdev.dk_label))
1452130803Smarcel		ccdmakedisklabel(cs);
1453130803Smarcel
1454130803Smarcel#ifdef DEBUG
1455130803Smarcel	/* It's actually extremely common to have unlabeled ccds. */
1456130803Smarcel	if (ccddebug & CCDB_LABEL)
1457130803Smarcel		if (errstring != NULL)
1458130803Smarcel			printf("ccd%d: %s\n", unit, errstring);
1459130803Smarcel#endif
1460130803Smarcel}
1461130803Smarcel
1462130803Smarcel/*
1463130803Smarcel * Take care of things one might want to take care of in the event
1464130803Smarcel * that a disklabel isn't present.
1465130803Smarcel */
1466130803Smarcelstatic void
1467130803Smarcelccdmakedisklabel(cs)
1468130803Smarcel	struct ccd_softc *cs;
1469130803Smarcel{
1470130803Smarcel	struct disklabel *lp = &cs->sc_dkdev.dk_label;
1471130803Smarcel
1472130803Smarcel	/*
1473130803Smarcel	 * For historical reasons, if there's no disklabel present
1474130803Smarcel	 * the raw partition must be marked FS_BSDFFS.
1475130803Smarcel	 */
1476130803Smarcel	lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
1477130803Smarcel
1478130803Smarcel	strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1479130803Smarcel}
1480130803Smarcel
1481130803Smarcel/*
1482130803Smarcel * Wait interruptibly for an exclusive lock.
1483130803Smarcel *
1484130803Smarcel * XXX
1485130803Smarcel * Several drivers do this; it should be abstracted and made MP-safe.
1486130803Smarcel */
1487130803Smarcelstatic int
1488130803Smarcelccdlock(cs)
1489130803Smarcel	struct ccd_softc *cs;
1490130803Smarcel{
1491130803Smarcel	int error;
1492130803Smarcel
1493130803Smarcel	while ((cs->sc_flags & CCDF_LOCKED) != 0) {
1494130803Smarcel		cs->sc_flags |= CCDF_WANTED;
1495130803Smarcel		if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0)
1496130803Smarcel			return (error);
1497130803Smarcel	}
1498130803Smarcel	cs->sc_flags |= CCDF_LOCKED;
1499130803Smarcel	return (0);
1500130803Smarcel}
1501130803Smarcel
1502130803Smarcel/*
1503130803Smarcel * Unlock and wake up any waiters.
1504130803Smarcel */
1505130803Smarcelstatic void
1506130803Smarcelccdunlock(cs)
1507130803Smarcel	struct ccd_softc *cs;
1508130803Smarcel{
1509130803Smarcel
1510130803Smarcel	cs->sc_flags &= ~CCDF_LOCKED;
1511130803Smarcel	if ((cs->sc_flags & CCDF_WANTED) != 0) {
1512130803Smarcel		cs->sc_flags &= ~CCDF_WANTED;
1513130803Smarcel		wakeup(cs);
1514130803Smarcel	}
1515130803Smarcel}
1516130803Smarcel
1517130803Smarcel#ifdef DEBUG
1518130803Smarcelstatic void
1519130803Smarcelprintiinfo(ii)
1520130803Smarcel	struct ccdiinfo *ii;
1521130803Smarcel{
1522130803Smarcel	register int ix, i;
1523130803Smarcel
1524130803Smarcel	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
1525130803Smarcel		printf(" itab[%d]: #dk %d sblk %d soff %d",
1526130803Smarcel		       ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
1527130803Smarcel		for (i = 0; i < ii->ii_ndisk; i++)
1528130803Smarcel			printf(" %d", ii->ii_index[i]);
1529130803Smarcel		printf("\n");
1530130803Smarcel	}
1531130803Smarcel}
1532130803Smarcel#endif
1533130803Smarcel
1534130803Smarcel#endif /* NCCD > 0 */
1535130803Smarcel
1536130803Smarcel/* Local Variables: */
1537130803Smarcel/* c-argdecl-indent: 8 */
1538130803Smarcel/* c-continued-statement-offset: 8 */
1539130803Smarcel/* c-indent-level: 8 */
1540130803Smarcel/* End: */
1541130803Smarcel