geom_ccd.c revision 38438
1/* $Id: ccd.c,v 1.35 1998/07/04 22:30:13 julian Exp $ */
2
3/*	$NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $	*/
4
5/*
6 * Copyright (c) 1995 Jason R. Thorpe.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed for the NetBSD Project
20 *	by Jason R. Thorpe.
21 * 4. The name of the author may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37/*
38 * Copyright (c) 1988 University of Utah.
39 * Copyright (c) 1990, 1993
40 *	The Regents of the University of California.  All rights reserved.
41 *
42 * This code is derived from software contributed to Berkeley by
43 * the Systems Programming Group of the University of Utah Computer
44 * Science Department.
45 *
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
48 * are met:
49 * 1. Redistributions of source code must retain the above copyright
50 *    notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 *    notice, this list of conditions and the following disclaimer in the
53 *    documentation and/or other materials provided with the distribution.
54 * 3. All advertising materials mentioning features or use of this software
55 *    must display the following acknowledgement:
56 *	This product includes software developed by the University of
57 *	California, Berkeley and its contributors.
58 * 4. Neither the name of the University nor the names of its contributors
59 *    may be used to endorse or promote products derived from this software
60 *    without specific prior written permission.
61 *
62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72 * SUCH DAMAGE.
73 *
74 * from: Utah $Hdr: cd.c 1.6 90/11/28$
75 *
76 *	@(#)cd.c	8.2 (Berkeley) 11/16/93
77 */
78
79/*
80 * "Concatenated" disk driver.
81 *
82 * Dynamic configuration and disklabel support by:
83 *	Jason R. Thorpe <thorpej@nas.nasa.gov>
84 *	Numerical Aerodynamic Simulation Facility
85 *	Mail Stop 258-6
86 *	NASA Ames Research Center
87 *	Moffett Field, CA 94035
88 */
89
90#include "ccd.h"
91#if NCCD > 0
92
93#include <sys/param.h>
94#include <sys/systm.h>
95#include <sys/kernel.h>
96#include <sys/proc.h>
97#include <sys/buf.h>
98#include <sys/malloc.h>
99#include <sys/namei.h>
100#include <sys/conf.h>
101#include <sys/stat.h>
102#include <sys/sysctl.h>
103#include <sys/disklabel.h>
104#include <ufs/ffs/fs.h>
105#include <sys/device.h>
106#undef KERNEL			/* XXX */
107#include <sys/disk.h>
108#define KERNEL
109#include <sys/fcntl.h>
110#include <sys/vnode.h>
111
112#include <sys/ccdvar.h>
113
114#if defined(CCDDEBUG) && !defined(DEBUG)
115#define DEBUG
116#endif
117
118#ifdef DEBUG
119#define CCDB_FOLLOW	0x01
120#define CCDB_INIT	0x02
121#define CCDB_IO		0x04
122#define CCDB_LABEL	0x08
123#define CCDB_VNODE	0x10
124static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL |
125    CCDB_VNODE;
126SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, "");
127#undef DEBUG
128#endif
129
130#define	ccdunit(x)	dkunit(x)
131#define ccdpart(x)	dkpart(x)
132
133/*
134   This is how mirroring works (only writes are special):
135
136   When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s
137   linked together by the cb_mirror field.  "cb_pflags &
138   CCDPF_MIRROR_DONE" is set to 0 on both of them.
139
140   When a component returns to ccdiodone(), it checks if "cb_pflags &
141   CCDPF_MIRROR_DONE" is set or not.  If not, it sets the partner's
142   flag and returns.  If it is, it means its partner has already
143   returned, so it will go to the regular cleanup.
144
145 */
146
147struct ccdbuf {
148	struct buf	cb_buf;		/* new I/O buf */
149	struct buf	*cb_obp;	/* ptr. to original I/O buf */
150	int		cb_unit;	/* target unit */
151	int		cb_comp;	/* target component */
152	int		cb_pflags;	/* mirror/parity status flag */
153	struct ccdbuf	*cb_mirror;	/* mirror counterpart */
154};
155
156/* bits in cb_pflags */
157#define CCDPF_MIRROR_DONE 1	/* if set, mirror counterpart is done */
158
159#define	getccdbuf()		\
160	((struct ccdbuf *)malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK))
161#define putccdbuf(cbp)		\
162	free((caddr_t)(cbp), M_DEVBUF)
163
164#define CCDLABELDEV(dev)	\
165	(makedev(major((dev)), dkmakeminor(ccdunit((dev)), 0, RAW_PART)))
166
167static d_open_t ccdopen;
168static d_read_t	ccdread;
169static d_write_t ccdwrite;
170static d_close_t ccdclose;
171static d_strategy_t ccdstrategy;
172static d_ioctl_t ccdioctl;
173static d_dump_t ccddump;
174static d_psize_t ccdsize;
175
176#define CDEV_MAJOR 74
177#define BDEV_MAJOR 21
178
179static struct cdevsw ccd_cdevsw = {
180	  ccdopen,	ccdclose,	ccdread,	ccdwrite,
181	  ccdioctl,	nostop,		nullreset,	nodevtotty,
182	  seltrue,	nommap,		ccdstrategy,	"ccd",
183	  NULL, 	-1,		ccddump,	ccdsize,
184	  D_DISK,	0,		-1 };
185
186/* Called by main() during pseudo-device attachment */
187static void	ccdattach __P((void *));
188PSEUDO_SET(ccdattach, ccd);
189
190/* called by biodone() at interrupt time */
191static	void ccdiodone __P((struct ccdbuf *cbp));
192
193static	void ccdstart __P((struct ccd_softc *, struct buf *));
194static	void ccdinterleave __P((struct ccd_softc *, int));
195static	void ccdintr __P((struct ccd_softc *, struct buf *));
196static	int ccdinit __P((struct ccddevice *, char **, struct proc *));
197static	int ccdlookup __P((char *, struct proc *p, struct vnode **));
198static	void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *,
199		struct buf *, daddr_t, caddr_t, long));
200static	void ccdgetdisklabel __P((dev_t));
201static	void ccdmakedisklabel __P((struct ccd_softc *));
202static	int ccdlock __P((struct ccd_softc *));
203static	void ccdunlock __P((struct ccd_softc *));
204
205#ifdef DEBUG
206static	void printiinfo __P((struct ccdiinfo *));
207#endif
208
209/* Non-private for the benefit of libkvm. */
210struct	ccd_softc *ccd_softc;
211struct	ccddevice *ccddevs;
212static	int numccd = 0;
213
214static int ccd_devsw_installed = 0;
215
216/*
217 * Number of blocks to untouched in front of a component partition.
218 * This is to avoid violating its disklabel area when it starts at the
219 * beginning of the slice.
220 */
221#if !defined(CCD_OFFSET)
222#define CCD_OFFSET 16
223#endif
224
225/*
226 * Called by main() during pseudo-device attachment.  All we need
227 * to do is allocate enough space for devices to be configured later, and
228 * add devsw entries.
229 */
230static void
231ccdattach(dummy)
232	void *dummy;
233{
234	int i;
235	int num = NCCD;
236
237	if (num > 1)
238		printf("ccd0-%d: Concatenated disk drivers\n", num-1);
239	else
240		printf("ccd0: Concatenated disk driver\n");
241
242	ccd_softc = (struct ccd_softc *)malloc(num * sizeof(struct ccd_softc),
243	    M_DEVBUF, M_NOWAIT);
244	ccddevs = (struct ccddevice *)malloc(num * sizeof(struct ccddevice),
245	    M_DEVBUF, M_NOWAIT);
246	if ((ccd_softc == NULL) || (ccddevs == NULL)) {
247		printf("WARNING: no memory for concatenated disks\n");
248		if (ccd_softc != NULL)
249			free(ccd_softc, M_DEVBUF);
250		if (ccddevs != NULL)
251			free(ccddevs, M_DEVBUF);
252		return;
253	}
254	numccd = num;
255	bzero(ccd_softc, num * sizeof(struct ccd_softc));
256	bzero(ccddevs, num * sizeof(struct ccddevice));
257
258	/* XXX: is this necessary? */
259	for (i = 0; i < numccd; ++i)
260		ccddevs[i].ccd_dk = -1;
261
262	if( ! ccd_devsw_installed ) {
263		cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &ccd_cdevsw);
264		ccd_devsw_installed = 1;
265    	}
266	else {
267		printf("huh?\n");
268	}
269}
270
271static int
272ccdinit(ccd, cpaths, p)
273	struct ccddevice *ccd;
274	char **cpaths;
275	struct proc *p;
276{
277	register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
278	register struct ccdcinfo *ci = NULL;	/* XXX */
279	register size_t size;
280	register int ix;
281	struct vnode *vp;
282	struct vattr va;
283	size_t minsize;
284	int maxsecsize;
285	struct partinfo dpart;
286	struct ccdgeom *ccg = &cs->sc_geom;
287	char tmppath[MAXPATHLEN];
288	int error;
289
290#ifdef DEBUG
291	if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
292		printf("ccdinit: unit %d\n", ccd->ccd_unit);
293#endif
294
295#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
296	cs->sc_dk = ccd->ccd_dk;
297#endif
298	cs->sc_size = 0;
299	cs->sc_ileave = ccd->ccd_interleave;
300	cs->sc_nccdisks = ccd->ccd_ndev;
301
302	/* Allocate space for the component info. */
303	cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
304	    M_DEVBUF, M_WAITOK);
305
306	/*
307	 * Verify that each component piece exists and record
308	 * relevant information about it.
309	 */
310	maxsecsize = 0;
311	minsize = 0;
312	for (ix = 0; ix < cs->sc_nccdisks; ix++) {
313		vp = ccd->ccd_vpp[ix];
314		ci = &cs->sc_cinfo[ix];
315		ci->ci_vp = vp;
316
317		/*
318		 * Copy in the pathname of the component.
319		 */
320		bzero(tmppath, sizeof(tmppath));	/* sanity */
321		if (error = copyinstr(cpaths[ix], tmppath,
322		    MAXPATHLEN, &ci->ci_pathlen)) {
323#ifdef DEBUG
324			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
325				printf("ccd%d: can't copy path, error = %d\n",
326				    ccd->ccd_unit, error);
327#endif
328			while (ci > cs->sc_cinfo) {
329				ci--;
330				free(ci->ci_path, M_DEVBUF);
331			}
332			free(cs->sc_cinfo, M_DEVBUF);
333			return (error);
334		}
335		ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK);
336		bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
337
338		/*
339		 * XXX: Cache the component's dev_t.
340		 */
341		if (error = VOP_GETATTR(vp, &va, p->p_ucred, p)) {
342#ifdef DEBUG
343			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
344				printf("ccd%d: %s: getattr failed %s = %d\n",
345				    ccd->ccd_unit, ci->ci_path,
346				    "error", error);
347#endif
348			while (ci >= cs->sc_cinfo) {
349				free(ci->ci_path, M_DEVBUF);
350				ci--;
351			}
352			free(cs->sc_cinfo, M_DEVBUF);
353			return (error);
354		}
355		ci->ci_dev = va.va_rdev;
356
357		/*
358		 * Get partition information for the component.
359		 */
360		if (error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart,
361		    FREAD, p->p_ucred, p)) {
362#ifdef DEBUG
363			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
364				 printf("ccd%d: %s: ioctl failed, error = %d\n",
365				     ccd->ccd_unit, ci->ci_path, error);
366#endif
367			while (ci >= cs->sc_cinfo) {
368				free(ci->ci_path, M_DEVBUF);
369				ci--;
370			}
371			free(cs->sc_cinfo, M_DEVBUF);
372			return (error);
373		}
374		if (dpart.part->p_fstype == FS_BSDFFS) {
375			maxsecsize =
376			    ((dpart.disklab->d_secsize > maxsecsize) ?
377			    dpart.disklab->d_secsize : maxsecsize);
378			size = dpart.part->p_size - CCD_OFFSET;
379		} else {
380#ifdef DEBUG
381			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
382				printf("ccd%d: %s: incorrect partition type\n",
383				    ccd->ccd_unit, ci->ci_path);
384#endif
385			while (ci >= cs->sc_cinfo) {
386				free(ci->ci_path, M_DEVBUF);
387				ci--;
388			}
389			free(cs->sc_cinfo, M_DEVBUF);
390			return (EFTYPE);
391		}
392
393		/*
394		 * Calculate the size, truncating to an interleave
395		 * boundary if necessary.
396		 */
397
398		if (cs->sc_ileave > 1)
399			size -= size % cs->sc_ileave;
400
401		if (size == 0) {
402#ifdef DEBUG
403			if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
404				printf("ccd%d: %s: size == 0\n",
405				    ccd->ccd_unit, ci->ci_path);
406#endif
407			while (ci >= cs->sc_cinfo) {
408				free(ci->ci_path, M_DEVBUF);
409				ci--;
410			}
411			free(cs->sc_cinfo, M_DEVBUF);
412			return (ENODEV);
413		}
414
415		if (minsize == 0 || size < minsize)
416			minsize = size;
417		ci->ci_size = size;
418		cs->sc_size += size;
419	}
420
421	/*
422	 * Don't allow the interleave to be smaller than
423	 * the biggest component sector.
424	 */
425	if ((cs->sc_ileave > 0) &&
426	    (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
427#ifdef DEBUG
428		if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
429			printf("ccd%d: interleave must be at least %d\n",
430			    ccd->ccd_unit, (maxsecsize / DEV_BSIZE));
431#endif
432		while (ci >= cs->sc_cinfo) {
433			free(ci->ci_path, M_DEVBUF);
434			ci--;
435		}
436		free(cs->sc_cinfo, M_DEVBUF);
437		return (EINVAL);
438	}
439
440	/*
441	 * If uniform interleave is desired set all sizes to that of
442	 * the smallest component.
443	 */
444	if (ccd->ccd_flags & CCDF_UNIFORM) {
445		for (ci = cs->sc_cinfo;
446		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
447			ci->ci_size = minsize;
448		if (ccd->ccd_flags & CCDF_MIRROR) {
449			/*
450			 * Check to see if an even number of components
451			 * have been specified.
452			 */
453			if (cs->sc_nccdisks % 2) {
454				printf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit );
455				while (ci > cs->sc_cinfo) {
456					ci--;
457					free(ci->ci_path, M_DEVBUF);
458				}
459				free(cs->sc_cinfo, M_DEVBUF);
460				return (EINVAL);
461			}
462			cs->sc_size = (cs->sc_nccdisks/2) * minsize;
463		}
464		else if (ccd->ccd_flags & CCDF_PARITY)
465			cs->sc_size = (cs->sc_nccdisks-1) * minsize;
466		else
467			cs->sc_size = cs->sc_nccdisks * minsize;
468	}
469
470	/*
471	 * Construct the interleave table.
472	 */
473	ccdinterleave(cs, ccd->ccd_unit);
474
475	/*
476	 * Create pseudo-geometry based on 1MB cylinders.  It's
477	 * pretty close.
478	 */
479	ccg->ccg_secsize = maxsecsize;
480	ccg->ccg_ntracks = 1;
481	ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize);
482	ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
483
484#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
485	if (ccd->ccd_dk >= 0)
486		dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2);     /* XXX */
487#endif
488
489	cs->sc_flags |= CCDF_INITED;
490	cs->sc_cflags = ccd->ccd_flags;	/* So we can find out later... */
491	cs->sc_unit = ccd->ccd_unit;
492	return (0);
493}
494
495static void
496ccdinterleave(cs, unit)
497	register struct ccd_softc *cs;
498	int unit;
499{
500	register struct ccdcinfo *ci, *smallci;
501	register struct ccdiinfo *ii;
502	register daddr_t bn, lbn;
503	register int ix;
504	u_long size;
505
506#ifdef DEBUG
507	if (ccddebug & CCDB_INIT)
508		printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
509#endif
510	/*
511	 * Allocate an interleave table.
512	 * Chances are this is too big, but we don't care.
513	 */
514	size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
515	cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
516	bzero((caddr_t)cs->sc_itable, size);
517
518	/*
519	 * Trivial case: no interleave (actually interleave of disk size).
520	 * Each table entry represents a single component in its entirety.
521	 */
522	if (cs->sc_ileave == 0) {
523		bn = 0;
524		ii = cs->sc_itable;
525
526		for (ix = 0; ix < cs->sc_nccdisks; ix++) {
527			/* Allocate space for ii_index. */
528			ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK);
529			ii->ii_ndisk = 1;
530			ii->ii_startblk = bn;
531			ii->ii_startoff = 0;
532			ii->ii_index[0] = ix;
533			bn += cs->sc_cinfo[ix].ci_size;
534			ii++;
535		}
536		ii->ii_ndisk = 0;
537#ifdef DEBUG
538		if (ccddebug & CCDB_INIT)
539			printiinfo(cs->sc_itable);
540#endif
541		return;
542	}
543
544	/*
545	 * The following isn't fast or pretty; it doesn't have to be.
546	 */
547	size = 0;
548	bn = lbn = 0;
549	for (ii = cs->sc_itable; ; ii++) {
550		/* Allocate space for ii_index. */
551		ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks),
552		    M_DEVBUF, M_WAITOK);
553
554		/*
555		 * Locate the smallest of the remaining components
556		 */
557		smallci = NULL;
558		for (ci = cs->sc_cinfo;
559		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
560			if (ci->ci_size > size &&
561			    (smallci == NULL ||
562			     ci->ci_size < smallci->ci_size))
563				smallci = ci;
564
565		/*
566		 * Nobody left, all done
567		 */
568		if (smallci == NULL) {
569			ii->ii_ndisk = 0;
570			break;
571		}
572
573		/*
574		 * Record starting logical block and component offset
575		 */
576		ii->ii_startblk = bn / cs->sc_ileave;
577		ii->ii_startoff = lbn;
578
579		/*
580		 * Determine how many disks take part in this interleave
581		 * and record their indices.
582		 */
583		ix = 0;
584		for (ci = cs->sc_cinfo;
585		     ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
586			if (ci->ci_size >= smallci->ci_size)
587				ii->ii_index[ix++] = ci - cs->sc_cinfo;
588		ii->ii_ndisk = ix;
589		bn += ix * (smallci->ci_size - size);
590		lbn = smallci->ci_size / cs->sc_ileave;
591		size = smallci->ci_size;
592	}
593#ifdef DEBUG
594	if (ccddebug & CCDB_INIT)
595		printiinfo(cs->sc_itable);
596#endif
597}
598
599/* ARGSUSED */
600static int
601ccdopen(dev, flags, fmt, p)
602	dev_t dev;
603	int flags, fmt;
604	struct proc *p;
605{
606	int unit = ccdunit(dev);
607	struct ccd_softc *cs;
608	struct disklabel *lp;
609	int error = 0, part, pmask;
610
611#ifdef DEBUG
612	if (ccddebug & CCDB_FOLLOW)
613		printf("ccdopen(%x, %x)\n", dev, flags);
614#endif
615	if (unit >= numccd)
616		return (ENXIO);
617	cs = &ccd_softc[unit];
618
619	if (error = ccdlock(cs))
620		return (error);
621
622	lp = &cs->sc_dkdev.dk_label;
623
624	part = ccdpart(dev);
625	pmask = (1 << part);
626
627	/*
628	 * If we're initialized, check to see if there are any other
629	 * open partitions.  If not, then it's safe to update
630	 * the in-core disklabel.
631	 */
632	if ((cs->sc_flags & CCDF_INITED) && (cs->sc_dkdev.dk_openmask == 0))
633		ccdgetdisklabel(dev);
634
635	/* Check that the partition exists. */
636	if (part != RAW_PART && ((part >= lp->d_npartitions) ||
637	    (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
638		error = ENXIO;
639		goto done;
640	}
641
642	/* Prevent our unit from being unconfigured while open. */
643	switch (fmt) {
644	case S_IFCHR:
645		cs->sc_dkdev.dk_copenmask |= pmask;
646		break;
647
648	case S_IFBLK:
649		cs->sc_dkdev.dk_bopenmask |= pmask;
650		break;
651	}
652	cs->sc_dkdev.dk_openmask =
653	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
654
655 done:
656	ccdunlock(cs);
657	return (0);
658}
659
660/* ARGSUSED */
661static int
662ccdclose(dev, flags, fmt, p)
663	dev_t dev;
664	int flags, fmt;
665	struct proc *p;
666{
667	int unit = ccdunit(dev);
668	struct ccd_softc *cs;
669	int error = 0, part;
670
671#ifdef DEBUG
672	if (ccddebug & CCDB_FOLLOW)
673		printf("ccdclose(%x, %x)\n", dev, flags);
674#endif
675
676	if (unit >= numccd)
677		return (ENXIO);
678	cs = &ccd_softc[unit];
679
680	if (error = ccdlock(cs))
681		return (error);
682
683	part = ccdpart(dev);
684
685	/* ...that much closer to allowing unconfiguration... */
686	switch (fmt) {
687	case S_IFCHR:
688		cs->sc_dkdev.dk_copenmask &= ~(1 << part);
689		break;
690
691	case S_IFBLK:
692		cs->sc_dkdev.dk_bopenmask &= ~(1 << part);
693		break;
694	}
695	cs->sc_dkdev.dk_openmask =
696	    cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask;
697
698	ccdunlock(cs);
699	return (0);
700}
701
702static int
703ccdread(dev_t dev, struct uio *uio, int ioflag)
704{
705	return (physio(ccdstrategy, NULL, dev, 1, minphys, uio));
706}
707
708static int
709ccdwrite(dev_t dev, struct uio *uio, int ioflag)
710{
711	return (physio(ccdstrategy, NULL, dev, 0, minphys, uio));
712}
713
714static void
715ccdstrategy(bp)
716	register struct buf *bp;
717{
718	register int unit = ccdunit(bp->b_dev);
719	register struct ccd_softc *cs = &ccd_softc[unit];
720	register int s;
721	int wlabel;
722	struct disklabel *lp;
723
724#ifdef DEBUG
725	if (ccddebug & CCDB_FOLLOW)
726		printf("ccdstrategy(%x): unit %d\n", bp, unit);
727#endif
728	if ((cs->sc_flags & CCDF_INITED) == 0) {
729		bp->b_error = ENXIO;
730		bp->b_flags |= B_ERROR;
731		goto done;
732	}
733
734	/* If it's a nil transfer, wake up the top half now. */
735	if (bp->b_bcount == 0)
736		goto done;
737
738	lp = &cs->sc_dkdev.dk_label;
739
740	/*
741	 * Do bounds checking and adjust transfer.  If there's an
742	 * error, the bounds check will flag that for us.
743	 */
744	wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
745	if (ccdpart(bp->b_dev) != RAW_PART)
746		if (bounds_check_with_label(bp, lp, wlabel) <= 0)
747			goto done;
748
749	bp->b_resid = bp->b_bcount;
750
751	/*
752	 * "Start" the unit.
753	 */
754	s = splbio();
755	ccdstart(cs, bp);
756	splx(s);
757	return;
758done:
759	biodone(bp);
760}
761
762static void
763ccdstart(cs, bp)
764	register struct ccd_softc *cs;
765	register struct buf *bp;
766{
767	register long bcount, rcount;
768	struct ccdbuf *cbp[4];
769	/* XXX! : 2 reads and 2 writes for RAID 4/5 */
770	caddr_t addr;
771	daddr_t bn;
772	struct partition *pp;
773
774#ifdef DEBUG
775	if (ccddebug & CCDB_FOLLOW)
776		printf("ccdstart(%x, %x)\n", cs, bp);
777#endif
778
779#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
780	/*
781	 * Instrumentation (not very meaningful)
782	 */
783	cs->sc_nactive++;
784	if (cs->sc_dk >= 0) {
785		dk_busy |= 1 << cs->sc_dk;
786		dk_xfer[cs->sc_dk]++;
787		dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
788	}
789#endif
790
791	/*
792	 * Translate the partition-relative block number to an absolute.
793	 */
794	bn = bp->b_blkno;
795	if (ccdpart(bp->b_dev) != RAW_PART) {
796		pp = &cs->sc_dkdev.dk_label.d_partitions[ccdpart(bp->b_dev)];
797		bn += pp->p_offset;
798	}
799
800	/*
801	 * Allocate component buffers and fire off the requests
802	 */
803	addr = bp->b_data;
804	for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
805		ccdbuffer(cbp, cs, bp, bn, addr, bcount);
806		rcount = cbp[0]->cb_buf.b_bcount;
807		if ((cbp[0]->cb_buf.b_flags & B_READ) == 0)
808			cbp[0]->cb_buf.b_vp->v_numoutput++;
809		VOP_STRATEGY(cbp[0]->cb_buf.b_vp, &cbp[0]->cb_buf);
810		if (cs->sc_cflags & CCDF_MIRROR &&
811		    (cbp[0]->cb_buf.b_flags & B_READ) == 0) {
812			/* mirror, start another write */
813			cbp[1]->cb_buf.b_vp->v_numoutput++;
814			VOP_STRATEGY(cbp[1]->cb_buf.b_vp, &cbp[1]->cb_buf);
815		}
816		bn += btodb(rcount);
817		addr += rcount;
818	}
819}
820
821/*
822 * Build a component buffer header.
823 */
824static void
825ccdbuffer(cb, cs, bp, bn, addr, bcount)
826	register struct ccdbuf **cb;
827	register struct ccd_softc *cs;
828	struct buf *bp;
829	daddr_t bn;
830	caddr_t addr;
831	long bcount;
832{
833	register struct ccdcinfo *ci, *ci2 = NULL;	/* XXX */
834	register struct ccdbuf *cbp;
835	register daddr_t cbn, cboff;
836
837#ifdef DEBUG
838	if (ccddebug & CCDB_IO)
839		printf("ccdbuffer(%x, %x, %d, %x, %d)\n",
840		       cs, bp, bn, addr, bcount);
841#endif
842	/*
843	 * Determine which component bn falls in.
844	 */
845	cbn = bn;
846	cboff = 0;
847
848	/*
849	 * Serially concatenated
850	 */
851	if (cs->sc_ileave == 0) {
852		register daddr_t sblk;
853
854		sblk = 0;
855		for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
856			sblk += ci->ci_size;
857		cbn -= sblk;
858	}
859	/*
860	 * Interleaved
861	 */
862	else {
863		register struct ccdiinfo *ii;
864		int ccdisk, off;
865
866		cboff = cbn % cs->sc_ileave;
867		cbn /= cs->sc_ileave;
868		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
869			if (ii->ii_startblk > cbn)
870				break;
871		ii--;
872		off = cbn - ii->ii_startblk;
873		if (ii->ii_ndisk == 1) {
874			ccdisk = ii->ii_index[0];
875			cbn = ii->ii_startoff + off;
876		} else {
877			if (cs->sc_cflags & CCDF_MIRROR) {
878				ccdisk = ii->ii_index[off % (ii->ii_ndisk/2)];
879				cbn = ii->ii_startoff + off / (ii->ii_ndisk/2);
880				/* mirrored data */
881				ci2 = &cs->sc_cinfo[ccdisk + ii->ii_ndisk/2];
882			}
883			else if (cs->sc_cflags & CCDF_PARITY) {
884				ccdisk = ii->ii_index[off % (ii->ii_ndisk-1)];
885				cbn = ii->ii_startoff + off / (ii->ii_ndisk-1);
886				if (cbn % ii->ii_ndisk <= ccdisk)
887					ccdisk++;
888			}
889			else {
890				ccdisk = ii->ii_index[off % ii->ii_ndisk];
891				cbn = ii->ii_startoff + off / ii->ii_ndisk;
892			}
893		}
894		cbn *= cs->sc_ileave;
895		ci = &cs->sc_cinfo[ccdisk];
896	}
897
898	/*
899	 * Fill in the component buf structure.
900	 */
901	cbp = getccdbuf();
902	bzero(cbp, sizeof (struct ccdbuf));
903	cbp->cb_buf.b_flags = bp->b_flags | B_CALL;
904	cbp->cb_buf.b_iodone = (void (*)(struct buf *))ccdiodone;
905	cbp->cb_buf.b_proc = bp->b_proc;
906	cbp->cb_buf.b_dev = ci->ci_dev;		/* XXX */
907	cbp->cb_buf.b_blkno = cbn + cboff + CCD_OFFSET;
908	cbp->cb_buf.b_offset = dbtob(cbn + cboff + CCD_OFFSET);
909	cbp->cb_buf.b_data = addr;
910	cbp->cb_buf.b_vp = ci->ci_vp;
911	LIST_INIT(&cbp->cb_buf.b_dep);
912	if (cs->sc_ileave == 0)
913		cbp->cb_buf.b_bcount = dbtob(ci->ci_size - cbn);
914	else
915		cbp->cb_buf.b_bcount = dbtob(cs->sc_ileave - cboff);
916	if (cbp->cb_buf.b_bcount > bcount)
917		cbp->cb_buf.b_bcount = bcount;
918
919 	cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
920
921	/*
922	 * context for ccdiodone
923	 */
924	cbp->cb_obp = bp;
925	cbp->cb_unit = cs - ccd_softc;
926	cbp->cb_comp = ci - cs->sc_cinfo;
927
928#ifdef DEBUG
929	if (ccddebug & CCDB_IO)
930		printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
931		       ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->cb_buf.b_blkno,
932		       cbp->cb_buf.b_data, cbp->cb_buf.b_bcount);
933#endif
934	cb[0] = cbp;
935	if (cs->sc_cflags & CCDF_MIRROR &&
936	    (cbp->cb_buf.b_flags & B_READ) == 0) {
937		/* mirror, start one more write */
938		cbp = getccdbuf();
939		bzero(cbp, sizeof (struct ccdbuf));
940		*cbp = *cb[0];
941		cbp->cb_buf.b_dev = ci2->ci_dev;
942		cbp->cb_buf.b_vp = ci2->ci_vp;
943		LIST_INIT(&cbp->cb_buf.b_dep);
944		cbp->cb_comp = ci2 - cs->sc_cinfo;
945		cb[1] = cbp;
946		/* link together the ccdbuf's and clear "mirror done" flag */
947		cb[0]->cb_mirror = cb[1];
948		cb[1]->cb_mirror = cb[0];
949		cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
950		cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
951	}
952}
953
954static void
955ccdintr(cs, bp)
956	register struct ccd_softc *cs;
957	register struct buf *bp;
958{
959#ifdef DEBUG
960	if (ccddebug & CCDB_FOLLOW)
961		printf("ccdintr(%x, %x)\n", cs, bp);
962#endif
963	/*
964	 * Request is done for better or worse, wakeup the top half.
965	 */
966#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
967	--cs->sc_nactive;
968#ifdef DIAGNOSTIC
969	if (cs->sc_nactive < 0)
970		panic("ccdintr: ccd%d: sc_nactive < 0", cs->sc_unit);
971#endif
972
973	if (cs->sc_nactive == 0 && cs->sc_dk >= 0)
974		dk_busy &= ~(1 << cs->sc_dk);
975#endif
976	if (bp->b_flags & B_ERROR)
977		bp->b_resid = bp->b_bcount;
978	biodone(bp);
979}
980
981/*
982 * Called at interrupt time.
983 * Mark the component as done and if all components are done,
984 * take a ccd interrupt.
985 */
986static void
987ccdiodone(cbp)
988	struct ccdbuf *cbp;
989{
990	register struct buf *bp = cbp->cb_obp;
991	register int unit = cbp->cb_unit;
992	int count, s;
993
994	s = splbio();
995#ifdef DEBUG
996	if (ccddebug & CCDB_FOLLOW)
997		printf("ccdiodone(%x)\n", cbp);
998	if (ccddebug & CCDB_IO) {
999		printf("ccdiodone: bp %x bcount %d resid %d\n",
1000		       bp, bp->b_bcount, bp->b_resid);
1001		printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
1002		       cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
1003		       cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
1004		       cbp->cb_buf.b_bcount);
1005	}
1006#endif
1007
1008	if (cbp->cb_buf.b_flags & B_ERROR) {
1009		bp->b_flags |= B_ERROR;
1010		bp->b_error = cbp->cb_buf.b_error ? cbp->cb_buf.b_error : EIO;
1011#ifdef DEBUG
1012		printf("ccd%d: error %d on component %d\n",
1013		       unit, bp->b_error, cbp->cb_comp);
1014#endif
1015	}
1016
1017	if (ccd_softc[unit].sc_cflags & CCDF_MIRROR &&
1018	    (cbp->cb_buf.b_flags & B_READ) == 0)
1019		if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1020			/* I'm done before my counterpart, so just set
1021			   partner's flag and return */
1022			cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
1023			putccdbuf(cbp);
1024			splx(s);
1025			return;
1026		}
1027
1028	count = cbp->cb_buf.b_bcount;
1029	putccdbuf(cbp);
1030
1031	/*
1032	 * If all done, "interrupt".
1033	 */
1034	bp->b_resid -= count;
1035	if (bp->b_resid < 0)
1036		panic("ccdiodone: count");
1037	if (bp->b_resid == 0)
1038		ccdintr(&ccd_softc[unit], bp);
1039	splx(s);
1040}
1041
1042static int
1043ccdioctl(dev, cmd, data, flag, p)
1044	dev_t dev;
1045	u_long cmd;
1046	caddr_t data;
1047	int flag;
1048	struct proc *p;
1049{
1050	int unit = ccdunit(dev);
1051	int i, j, lookedup = 0, error = 0;
1052	int part, pmask, s;
1053	struct ccd_softc *cs;
1054	struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
1055	struct ccddevice ccd;
1056	char **cpp;
1057	struct vnode **vpp;
1058#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1059	extern int dkn;
1060#endif
1061
1062	if (unit >= numccd)
1063		return (ENXIO);
1064	cs = &ccd_softc[unit];
1065
1066	bzero(&ccd, sizeof(ccd));
1067
1068	switch (cmd) {
1069	case CCDIOCSET:
1070		if (cs->sc_flags & CCDF_INITED)
1071			return (EBUSY);
1072
1073		if ((flag & FWRITE) == 0)
1074			return (EBADF);
1075
1076		if (error = ccdlock(cs))
1077			return (error);
1078
1079		/* Fill in some important bits. */
1080		ccd.ccd_unit = unit;
1081		ccd.ccd_interleave = ccio->ccio_ileave;
1082		if (ccd.ccd_interleave == 0 &&
1083		    ((ccio->ccio_flags & CCDF_MIRROR) ||
1084		     (ccio->ccio_flags & CCDF_PARITY))) {
1085			printf("ccd%d: disabling mirror/parity, interleave is 0\n", unit);
1086			ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY);
1087		}
1088		if ((ccio->ccio_flags & CCDF_MIRROR) &&
1089		    (ccio->ccio_flags & CCDF_PARITY)) {
1090			printf("ccd%d: can't specify both mirror and parity, using mirror\n", unit);
1091			ccio->ccio_flags &= ~CCDF_PARITY;
1092		}
1093		if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) &&
1094		    !(ccio->ccio_flags & CCDF_UNIFORM)) {
1095			printf("ccd%d: mirror/parity forces uniform flag\n",
1096			       unit);
1097			ccio->ccio_flags |= CCDF_UNIFORM;
1098		}
1099		ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK;
1100
1101		/*
1102		 * Allocate space for and copy in the array of
1103		 * componet pathnames and device numbers.
1104		 */
1105		cpp = malloc(ccio->ccio_ndisks * sizeof(char *),
1106		    M_DEVBUF, M_WAITOK);
1107		vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *),
1108		    M_DEVBUF, M_WAITOK);
1109
1110		error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
1111		    ccio->ccio_ndisks * sizeof(char **));
1112		if (error) {
1113			free(vpp, M_DEVBUF);
1114			free(cpp, M_DEVBUF);
1115			ccdunlock(cs);
1116			return (error);
1117		}
1118
1119#ifdef DEBUG
1120		if (ccddebug & CCDB_INIT)
1121			for (i = 0; i < ccio->ccio_ndisks; ++i)
1122				printf("ccdioctl: component %d: 0x%x\n",
1123				    i, cpp[i]);
1124#endif
1125
1126		for (i = 0; i < ccio->ccio_ndisks; ++i) {
1127#ifdef DEBUG
1128			if (ccddebug & CCDB_INIT)
1129				printf("ccdioctl: lookedup = %d\n", lookedup);
1130#endif
1131			if (error = ccdlookup(cpp[i], p, &vpp[i])) {
1132				for (j = 0; j < lookedup; ++j)
1133					(void)vn_close(vpp[j], FREAD|FWRITE,
1134					    p->p_ucred, p);
1135				free(vpp, M_DEVBUF);
1136				free(cpp, M_DEVBUF);
1137				ccdunlock(cs);
1138				return (error);
1139			}
1140			++lookedup;
1141		}
1142		ccd.ccd_cpp = cpp;
1143		ccd.ccd_vpp = vpp;
1144		ccd.ccd_ndev = ccio->ccio_ndisks;
1145
1146#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1147		/*
1148		 * Assign disk index first so that init routine
1149		 * can use it (saves having the driver drag around
1150		 * the ccddevice pointer just to set up the dk_*
1151		 * info in the open routine).
1152		 */
1153		if (dkn < DK_NDRIVE)
1154			ccd.ccd_dk = dkn++;
1155		else
1156			ccd.ccd_dk = -1;
1157#endif
1158
1159		/*
1160		 * Initialize the ccd.  Fills in the softc for us.
1161		 */
1162		if (error = ccdinit(&ccd, cpp, p)) {
1163#ifdef WORKING_DISK_STATISTICS		/* XXX !! */
1164			if (ccd.ccd_dk >= 0)
1165				--dkn;
1166#endif
1167			for (j = 0; j < lookedup; ++j)
1168				(void)vn_close(vpp[j], FREAD|FWRITE,
1169				    p->p_ucred, p);
1170			bzero(&ccd_softc[unit], sizeof(struct ccd_softc));
1171			free(vpp, M_DEVBUF);
1172			free(cpp, M_DEVBUF);
1173			ccdunlock(cs);
1174			return (error);
1175		}
1176
1177		/*
1178		 * The ccd has been successfully initialized, so
1179		 * we can place it into the array and read the disklabel.
1180		 */
1181		bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1182		ccio->ccio_unit = unit;
1183		ccio->ccio_size = cs->sc_size;
1184		ccdgetdisklabel(dev);
1185
1186		ccdunlock(cs);
1187
1188		break;
1189
1190	case CCDIOCCLR:
1191		if ((cs->sc_flags & CCDF_INITED) == 0)
1192			return (ENXIO);
1193
1194		if ((flag & FWRITE) == 0)
1195			return (EBADF);
1196
1197		if (error = ccdlock(cs))
1198			return (error);
1199
1200		/*
1201		 * Don't unconfigure if any other partitions are open
1202		 * or if both the character and block flavors of this
1203		 * partition are open.
1204		 */
1205		part = ccdpart(dev);
1206		pmask = (1 << part);
1207		if ((cs->sc_dkdev.dk_openmask & ~pmask) ||
1208		    ((cs->sc_dkdev.dk_bopenmask & pmask) &&
1209		    (cs->sc_dkdev.dk_copenmask & pmask))) {
1210			ccdunlock(cs);
1211			return (EBUSY);
1212		}
1213
1214		/*
1215		 * Free ccd_softc information and clear entry.
1216		 */
1217
1218		/* Close the components and free their pathnames. */
1219		for (i = 0; i < cs->sc_nccdisks; ++i) {
1220			/*
1221			 * XXX: this close could potentially fail and
1222			 * cause Bad Things.  Maybe we need to force
1223			 * the close to happen?
1224			 */
1225#ifdef DEBUG
1226			if (ccddebug & CCDB_VNODE)
1227				vprint("CCDIOCCLR: vnode info",
1228				    cs->sc_cinfo[i].ci_vp);
1229#endif
1230			(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
1231			    p->p_ucred, p);
1232			free(cs->sc_cinfo[i].ci_path, M_DEVBUF);
1233		}
1234
1235		/* Free interleave index. */
1236		for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
1237			free(cs->sc_itable[i].ii_index, M_DEVBUF);
1238
1239		/* Free component info and interleave table. */
1240		free(cs->sc_cinfo, M_DEVBUF);
1241		free(cs->sc_itable, M_DEVBUF);
1242		cs->sc_flags &= ~CCDF_INITED;
1243
1244		/*
1245		 * Free ccddevice information and clear entry.
1246		 */
1247		free(ccddevs[unit].ccd_cpp, M_DEVBUF);
1248		free(ccddevs[unit].ccd_vpp, M_DEVBUF);
1249		ccd.ccd_dk = -1;
1250		bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1251
1252		/* This must be atomic. */
1253		s = splhigh();
1254		ccdunlock(cs);
1255		bzero(cs, sizeof(struct ccd_softc));
1256		splx(s);
1257
1258		break;
1259
1260	case DIOCGDINFO:
1261		if ((cs->sc_flags & CCDF_INITED) == 0)
1262			return (ENXIO);
1263
1264		*(struct disklabel *)data = cs->sc_dkdev.dk_label;
1265		break;
1266
1267	case DIOCGPART:
1268		if ((cs->sc_flags & CCDF_INITED) == 0)
1269			return (ENXIO);
1270
1271		((struct partinfo *)data)->disklab = &cs->sc_dkdev.dk_label;
1272		((struct partinfo *)data)->part =
1273		    &cs->sc_dkdev.dk_label.d_partitions[ccdpart(dev)];
1274		break;
1275
1276	case DIOCWDINFO:
1277	case DIOCSDINFO:
1278		if ((cs->sc_flags & CCDF_INITED) == 0)
1279			return (ENXIO);
1280
1281		if ((flag & FWRITE) == 0)
1282			return (EBADF);
1283
1284		if (error = ccdlock(cs))
1285			return (error);
1286
1287		cs->sc_flags |= CCDF_LABELLING;
1288
1289		error = setdisklabel(&cs->sc_dkdev.dk_label,
1290		    (struct disklabel *)data, 0);
1291				/*, &cs->sc_dkdev.dk_cpulabel); */
1292		if (error == 0) {
1293			if (cmd == DIOCWDINFO)
1294				error = writedisklabel(CCDLABELDEV(dev),
1295				    ccdstrategy, &cs->sc_dkdev.dk_label);
1296				/*
1297				    &cs->sc_dkdev.dk_cpulabel); */
1298		}
1299
1300		cs->sc_flags &= ~CCDF_LABELLING;
1301
1302		ccdunlock(cs);
1303
1304		if (error)
1305			return (error);
1306		break;
1307
1308	case DIOCWLABEL:
1309		if ((cs->sc_flags & CCDF_INITED) == 0)
1310			return (ENXIO);
1311
1312		if ((flag & FWRITE) == 0)
1313			return (EBADF);
1314		if (*(int *)data != 0)
1315			cs->sc_flags |= CCDF_WLABEL;
1316		else
1317			cs->sc_flags &= ~CCDF_WLABEL;
1318		break;
1319
1320	default:
1321		return (ENOTTY);
1322	}
1323
1324	return (0);
1325}
1326
1327static int
1328ccdsize(dev)
1329	dev_t dev;
1330{
1331	struct ccd_softc *cs;
1332	int part, size;
1333
1334	if (ccdopen(dev, 0, S_IFBLK, curproc))
1335		return (-1);
1336
1337	cs = &ccd_softc[ccdunit(dev)];
1338	part = ccdpart(dev);
1339
1340	if ((cs->sc_flags & CCDF_INITED) == 0)
1341		return (-1);
1342
1343	if (cs->sc_dkdev.dk_label.d_partitions[part].p_fstype != FS_SWAP)
1344		size = -1;
1345	else
1346		size = cs->sc_dkdev.dk_label.d_partitions[part].p_size;
1347
1348	if (ccdclose(dev, 0, S_IFBLK, curproc))
1349		return (-1);
1350
1351	return (size);
1352}
1353
1354static int
1355ccddump(dev)
1356	dev_t dev;
1357{
1358
1359	/* Not implemented. */
1360	return ENXIO;
1361}
1362
1363/*
1364 * Lookup the provided name in the filesystem.  If the file exists,
1365 * is a valid block device, and isn't being used by anyone else,
1366 * set *vpp to the file's vnode.
1367 */
1368static int
1369ccdlookup(path, p, vpp)
1370	char *path;
1371	struct proc *p;
1372	struct vnode **vpp;	/* result */
1373{
1374	struct nameidata nd;
1375	struct vnode *vp;
1376	struct vattr va;
1377	int error;
1378
1379	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p);
1380	if (error = vn_open(&nd, FREAD|FWRITE, 0)) {
1381#ifdef DEBUG
1382		if (ccddebug & CCDB_FOLLOW|CCDB_INIT)
1383			printf("ccdlookup: vn_open error = %d\n", error);
1384#endif
1385		return (error);
1386	}
1387	vp = nd.ni_vp;
1388
1389	if (vp->v_usecount > 1) {
1390		VOP_UNLOCK(vp, 0, p);
1391		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1392		return (EBUSY);
1393	}
1394
1395	if (error = VOP_GETATTR(vp, &va, p->p_ucred, p)) {
1396#ifdef DEBUG
1397		if (ccddebug & CCDB_FOLLOW|CCDB_INIT)
1398			printf("ccdlookup: getattr error = %d\n", error);
1399#endif
1400		VOP_UNLOCK(vp, 0, p);
1401		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1402		return (error);
1403	}
1404
1405	/* XXX: eventually we should handle VREG, too. */
1406	if (va.va_type != VBLK) {
1407		VOP_UNLOCK(vp, 0, p);
1408		(void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
1409		return (ENOTBLK);
1410	}
1411
1412#ifdef DEBUG
1413	if (ccddebug & CCDB_VNODE)
1414		vprint("ccdlookup: vnode info", vp);
1415#endif
1416
1417	VOP_UNLOCK(vp, 0, p);
1418	*vpp = vp;
1419	return (0);
1420}
1421
1422/*
1423 * Read the disklabel from the ccd.  If one is not present, fake one
1424 * up.
1425 */
1426static void
1427ccdgetdisklabel(dev)
1428	dev_t dev;
1429{
1430	int unit = ccdunit(dev);
1431	struct ccd_softc *cs = &ccd_softc[unit];
1432	char *errstring;
1433	struct disklabel *lp = &cs->sc_dkdev.dk_label;
1434	struct ccdgeom *ccg = &cs->sc_geom;
1435
1436	bzero(lp, sizeof(*lp));
1437
1438	lp->d_secperunit = cs->sc_size;
1439	lp->d_secsize = ccg->ccg_secsize;
1440	lp->d_nsectors = ccg->ccg_nsectors;
1441	lp->d_ntracks = ccg->ccg_ntracks;
1442	lp->d_ncylinders = ccg->ccg_ncylinders;
1443	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1444
1445	strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
1446	lp->d_type = DTYPE_CCD;
1447	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1448	lp->d_rpm = 3600;
1449	lp->d_interleave = 1;
1450	lp->d_flags = 0;
1451
1452	lp->d_partitions[RAW_PART].p_offset = 0;
1453	lp->d_partitions[RAW_PART].p_size = cs->sc_size;
1454	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1455	lp->d_npartitions = RAW_PART + 1;
1456
1457	lp->d_bbsize = BBSIZE;				/* XXX */
1458	lp->d_sbsize = SBSIZE;				/* XXX */
1459
1460	lp->d_magic = DISKMAGIC;
1461	lp->d_magic2 = DISKMAGIC;
1462	lp->d_checksum = dkcksum(&cs->sc_dkdev.dk_label);
1463
1464	/*
1465	 * Call the generic disklabel extraction routine.
1466	 */
1467	if (errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy,
1468	    &cs->sc_dkdev.dk_label))
1469		ccdmakedisklabel(cs);
1470
1471#ifdef DEBUG
1472	/* It's actually extremely common to have unlabeled ccds. */
1473	if (ccddebug & CCDB_LABEL)
1474		if (errstring != NULL)
1475			printf("ccd%d: %s\n", unit, errstring);
1476#endif
1477}
1478
1479/*
1480 * Take care of things one might want to take care of in the event
1481 * that a disklabel isn't present.
1482 */
1483static void
1484ccdmakedisklabel(cs)
1485	struct ccd_softc *cs;
1486{
1487	struct disklabel *lp = &cs->sc_dkdev.dk_label;
1488
1489	/*
1490	 * For historical reasons, if there's no disklabel present
1491	 * the raw partition must be marked FS_BSDFFS.
1492	 */
1493	lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
1494
1495	strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1496}
1497
1498/*
1499 * Wait interruptibly for an exclusive lock.
1500 *
1501 * XXX
1502 * Several drivers do this; it should be abstracted and made MP-safe.
1503 */
1504static int
1505ccdlock(cs)
1506	struct ccd_softc *cs;
1507{
1508	int error;
1509
1510	while ((cs->sc_flags & CCDF_LOCKED) != 0) {
1511		cs->sc_flags |= CCDF_WANTED;
1512		if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0)
1513			return (error);
1514	}
1515	cs->sc_flags |= CCDF_LOCKED;
1516	return (0);
1517}
1518
1519/*
1520 * Unlock and wake up any waiters.
1521 */
1522static void
1523ccdunlock(cs)
1524	struct ccd_softc *cs;
1525{
1526
1527	cs->sc_flags &= ~CCDF_LOCKED;
1528	if ((cs->sc_flags & CCDF_WANTED) != 0) {
1529		cs->sc_flags &= ~CCDF_WANTED;
1530		wakeup(cs);
1531	}
1532}
1533
1534#ifdef DEBUG
1535static void
1536printiinfo(ii)
1537	struct ccdiinfo *ii;
1538{
1539	register int ix, i;
1540
1541	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
1542		printf(" itab[%d]: #dk %d sblk %d soff %d",
1543		       ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
1544		for (i = 0; i < ii->ii_ndisk; i++)
1545			printf(" %d", ii->ii_index[i]);
1546		printf("\n");
1547	}
1548}
1549#endif
1550
1551#endif /* NCCD > 0 */
1552
1553/* Local Variables: */
1554/* c-argdecl-indent: 8 */
1555/* c-continued-statement-offset: 8 */
1556/* c-indent-level: 8 */
1557/* End: */
1558