diff --git a/sys/dev/ccd.c b/sys/dev/ccd.c new file mode 100644 index 000000000000..92a655b657f3 --- /dev/null +++ b/sys/dev/ccd.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah Hdr: ccd.c 1.6 90/11/28 + * from: @(#)cd.c 7.4 (Berkeley) 5/7/91 + * $Id: ccd.c,v 1.1 1994/06/24 14:11:02 hpeyerl Exp $ + */ + +/* + * "Concatenated" disk driver. + */ +#include "ccd.h" +#if NCCD > 0 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef DEBUG +int ccddebug = 0x00; +#define CDB_FOLLOW 0x01 +#define CDB_INIT 0x02 +#define CDB_IO 0x04 +#endif + +struct buf ccdbuf[NCCD]; +struct buf *ccdbuffer(); +char *ccddevtostr(); +int ccdiodone(); + +#define ccdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ + +#define getcbuf() \ + ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) +#define putcbuf(bp) \ + free((caddr_t)(bp), M_DEVBUF) + +struct ccd_softc { + int sc_flags; /* flags */ + size_t sc_size; /* size of ccd */ + int sc_ileave; /* interleave */ + int sc_nccdisks; /* number of components */ + struct ccdcinfo sc_cinfo[NCCDISKS]; /* component info */ + struct ccdiinfo *sc_itable; /* interleave table */ + int sc_usecnt; /* number of requests active */ + struct buf *sc_bp; /* "current" request */ + int sc_dk; /* disk index */ +} ccd_softc[NCCD]; + +/* sc_flags */ +#define CDF_ALIVE 0x01 +#define CDF_INITED 0x02 + +/* + * ccdattach() is called at boot time in new systems. We do + * nothing here since old systems will not call this. + */ + +void +ccdattach(n) + int n; +{ +} + +ccdinit(ccd) + struct ccddevice *ccd; +{ + register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit]; + register struct ccdcinfo *ci; + register size_t size; + register int ix; + size_t minsize; + dev_t dev; + +#ifdef DEBUG + if (ccddebug & (CDB_FOLLOW|CDB_INIT)) + printf("ccdinit: unit %d\n", ccd->ccd_unit); +#endif + cs->sc_dk = ccd->ccd_dk; + cs->sc_size = 0; + cs->sc_ileave = ccd->ccd_interleave; + cs->sc_nccdisks = 0; + /* + * Verify that each component piece exists and record + * relevant information about it. + */ + minsize = 0; + for (ix = 0; ix < NCCDISKS; ix++) { + if ((dev = ccd->ccd_dev[ix]) == NODEV) + break; + ci = &cs->sc_cinfo[ix]; + ci->ci_dev = dev; + /* + * Calculate size (truncated to interleave boundary + * if necessary. + */ + if (bdevsw[major(dev)].d_psize) { + size = (size_t) (*bdevsw[major(dev)].d_psize)(dev); + if ((int)size < 0) + size = 0; + } else + size = 0; + if (cs->sc_ileave > 1) + size -= size % cs->sc_ileave; + if (size == 0) { + printf("ccd%d: not configured (component %s missing)\n", + ccd->ccd_unit, ccddevtostr(ci->ci_dev)); + return(0); + } + if (minsize == 0 || size < minsize) + minsize = size; + ci->ci_size = size; + cs->sc_size += size; + cs->sc_nccdisks++; + } + /* + * If uniform interleave is desired set all sizes to that of + * the smallest component. + */ + if (ccd->ccd_flags & CDF_UNIFORM) { + for (ci = cs->sc_cinfo; + ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) + ci->ci_size = minsize; + cs->sc_size = cs->sc_nccdisks * minsize; + } + /* + * Construct the interleave table + */ + if (!ccdinterleave(cs)) + return(0); + if (ccd->ccd_dk >= 0) + dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */ + printf("ccd%d: %d components ", ccd->ccd_unit, cs->sc_nccdisks); + for (ix = 0; ix < cs->sc_nccdisks; ix++) + printf("%c%s%c", + ix == 0 ? '(' : ' ', + ccddevtostr(cs->sc_cinfo[ix].ci_dev), + ix == cs->sc_nccdisks - 1 ? ')' : ','); + printf(", %d blocks ", cs->sc_size); + if (cs->sc_ileave) + printf("interleaved at %d blocks\n", cs->sc_ileave); + else + printf("concatenated\n"); + cs->sc_flags = CDF_ALIVE | CDF_INITED; + return(1); +} + +/* + * XXX not really ccd specific. + */ +char * +ccddevtostr(dev) + dev_t dev; +{ + static char dbuf[5]; + + dbuf[1] = 'd'; + switch (major(dev)) { + case 2: + dbuf[0] = 'r'; + break; + case 4: + dbuf[0] = 's'; + break; + case 5: + dbuf[0] = 'c'; + break; + default: + dbuf[0] = dbuf[1] = '?'; + break; + } + dbuf[2] = (minor(dev) >> 3) + '0'; + dbuf[3] = (minor(dev) & 7) + 'a'; + dbuf[4] = '\0'; + return (dbuf); +} + +ccdinterleave(cs) + register struct ccd_softc *cs; +{ + register struct ccdcinfo *ci, *smallci; + register struct ccdiinfo *ii; + register daddr_t bn, lbn; + register int ix; + u_long size; + +#ifdef DEBUG + if (ccddebug & CDB_INIT) + printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave); +#endif + /* + * Allocate an interleave table. + * Chances are this is too big, but we don't care. + */ + size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); + cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK); + bzero((caddr_t)cs->sc_itable, size); + /* + * Trivial case: no interleave (actually interleave of disk size). + * Each table entry represent a single component in its entirety. + */ + if (cs->sc_ileave == 0) { + bn = 0; + ii = cs->sc_itable; + for (ix = 0; ix < cs->sc_nccdisks; ix++) { + ii->ii_ndisk = 1; + ii->ii_startblk = bn; + ii->ii_startoff = 0; + ii->ii_index[0] = ix; + bn += cs->sc_cinfo[ix].ci_size; + ii++; + } + ii->ii_ndisk = 0; +#ifdef DEBUG + if (ccddebug & CDB_INIT) + printiinfo(cs->sc_itable); +#endif + return(1); + } + /* + * The following isn't fast or pretty; it doesn't have to be. + */ + size = 0; + bn = lbn = 0; + for (ii = cs->sc_itable; ; ii++) { + /* + * Locate the smallest of the remaining components + */ + smallci = NULL; + for (ci = cs->sc_cinfo; + ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) + if (ci->ci_size > size && + (smallci == NULL || + ci->ci_size < smallci->ci_size)) + smallci = ci; + /* + * Nobody left, all done + */ + if (smallci == NULL) { + ii->ii_ndisk = 0; + break; + } + /* + * Record starting logical block and component offset + */ + ii->ii_startblk = bn / cs->sc_ileave; + ii->ii_startoff = lbn; + /* + * Determine how many disks take part in this interleave + * and record their indices. + */ + ix = 0; + for (ci = cs->sc_cinfo; + ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) + if (ci->ci_size >= smallci->ci_size) + ii->ii_index[ix++] = ci - cs->sc_cinfo; + ii->ii_ndisk = ix; + bn += ix * (smallci->ci_size - size); + lbn = smallci->ci_size / cs->sc_ileave; + size = smallci->ci_size; + } +#ifdef DEBUG + if (ccddebug & CDB_INIT) + printiinfo(cs->sc_itable); +#endif + return(1); +} + +#ifdef DEBUG +printiinfo(ii) + struct ccdiinfo *ii; +{ + register int ix, i; + + for (ix = 0; ii->ii_ndisk; ix++, ii++) { + printf(" itab[%d]: #dk %d sblk %d soff %d", + ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); + for (i = 0; i < ii->ii_ndisk; i++) + printf(" %d", ii->ii_index[i]); + printf("\n"); + } +} +#endif + +ccdopen(dev, flags) + dev_t dev; +{ + int unit = ccdunit(dev); + register struct ccd_softc *cs = &ccd_softc[unit]; + +#ifdef DEBUG + if (ccddebug & CDB_FOLLOW) + printf("ccdopen(%x, %x)\n", dev, flags); +#endif + if (unit >= NCCD || (cs->sc_flags & CDF_ALIVE) == 0) + return(ENXIO); + return(0); +} + +void +ccdstrategy(bp) + register struct buf *bp; +{ + register int unit = ccdunit(bp->b_dev); + register struct ccd_softc *cs = &ccd_softc[unit]; + register daddr_t bn; + register int sz, s; + +#ifdef DEBUG + if (ccddebug & CDB_FOLLOW) + printf("ccdstrategy(%x): unit %d\n", bp, unit); +#endif + if ((cs->sc_flags & CDF_INITED) == 0) { + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto done; + } + bn = bp->b_blkno; + sz = howmany(bp->b_bcount, DEV_BSIZE); + if (bn < 0 || bn + sz > cs->sc_size) { + sz = cs->sc_size - bn; + if (sz == 0) { + bp->b_resid = bp->b_bcount; + goto done; + } + if (sz < 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto done; + } + bp->b_bcount = dbtob(sz); + } + bp->b_resid = bp->b_bcount; + /* + * "Start" the unit. + * XXX: the use of sc_bp is just to retain the "traditional" + * interface to the start routine. + */ + s = splbio(); + cs->sc_bp = bp; + ccdstart(unit); + splx(s); + return; +done: + biodone(bp); +} + +ccdstart(unit) + int unit; +{ + register struct ccd_softc *cs = &ccd_softc[unit]; + register struct buf *bp = cs->sc_bp; + register long bcount, rcount; + struct buf *cbp; + caddr_t addr; + daddr_t bn; + +#ifdef DEBUG + if (ccddebug & CDB_FOLLOW) + printf("ccdstart(%d)\n", unit); +#endif + /* + * Instumentation (not real meaningful) + */ + cs->sc_usecnt++; + if (cs->sc_dk >= 0) { + dk_busy |= 1 << cs->sc_dk; + dk_xfer[cs->sc_dk]++; + dk_wds[cs->sc_dk] += bp->b_bcount >> 6; + } + /* + * Allocate component buffers and fire off the requests + */ + bn = bp->b_blkno; + addr = bp->b_un.b_addr; + for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { + cbp = ccdbuffer(cs, bp, bn, addr, bcount); + rcount = cbp->b_bcount; + (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp); + bn += btodb(rcount); + addr += rcount; + } +} + +/* + * Build a component buffer header. + */ +struct buf * +ccdbuffer(cs, bp, bn, addr, bcount) + register struct ccd_softc *cs; + struct buf *bp; + daddr_t bn; + caddr_t addr; + long bcount; +{ + register struct ccdcinfo *ci; + register struct buf *cbp; + register daddr_t cbn, cboff; + +#ifdef DEBUG + if (ccddebug & CDB_IO) + printf("ccdbuffer(%x, %x, %d, %x, %d)\n", + cs, bp, bn, addr, bcount); +#endif + /* + * Determine which component bn falls in. + */ + cbn = bn; + cboff = 0; + /* + * Serially concatenated + */ + if (cs->sc_ileave == 0) { + register daddr_t sblk; + + sblk = 0; + for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) + sblk += ci->ci_size; + cbn -= sblk; + } + /* + * Interleaved + */ + else { + register struct ccdiinfo *ii; + int ccdisk, off; + + cboff = cbn % cs->sc_ileave; + cbn /= cs->sc_ileave; + for (ii = cs->sc_itable; ii->ii_ndisk; ii++) + if (ii->ii_startblk > cbn) + break; + ii--; + off = cbn - ii->ii_startblk; + if (ii->ii_ndisk == 1) { + ccdisk = ii->ii_index[0]; + cbn = ii->ii_startoff + off; + } else { + ccdisk = ii->ii_index[off % ii->ii_ndisk]; + cbn = ii->ii_startoff + off / ii->ii_ndisk; + } + cbn *= cs->sc_ileave; + ci = &cs->sc_cinfo[ccdisk]; + } + /* + * Fill in the component buf structure. + */ + cbp = getcbuf(); + cbp->b_flags = bp->b_flags | B_CALL; + cbp->b_iodone = ccdiodone; + cbp->b_proc = bp->b_proc; + cbp->b_dev = ci->ci_dev; + cbp->b_blkno = cbn + cboff; + cbp->b_un.b_addr = addr; + cbp->b_vp = 0; + if (cs->sc_ileave == 0) + cbp->b_bcount = dbtob(ci->ci_size - cbn); + else + cbp->b_bcount = dbtob(cs->sc_ileave - cboff); + if (cbp->b_bcount > bcount) + cbp->b_bcount = bcount; + /* + * XXX: context for ccdiodone + */ + cbp->b_saveaddr = (caddr_t)bp; + cbp->b_pfcent = ((cs - ccd_softc) << 16) | (ci - cs->sc_cinfo); +#ifdef DEBUG + if (ccddebug & CDB_IO) + printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n", + ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno, + cbp->b_un.b_addr, cbp->b_bcount); +#endif + return(cbp); +} + +ccdintr(unit) + int unit; +{ + register struct ccd_softc *cs = &ccd_softc[unit]; + register struct buf *bp = cs->sc_bp; + +#ifdef DEBUG + if (ccddebug & CDB_FOLLOW) + printf("ccdintr(%d): bp %x\n", unit, bp); +#endif + /* + * Request is done for better or worse, wakeup the top half. + */ + if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0) + dk_busy &= ~(1 << cs->sc_dk); + if (bp->b_flags & B_ERROR) + bp->b_resid = bp->b_bcount; + biodone(bp); +} + +/* + * Called by biodone at interrupt time. + * Mark the component as done and if all components are done, + * take a ccd interrupt. + */ +ccdiodone(cbp) + register struct buf *cbp; +{ + register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */ + register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */ + int count, s; + + s = splbio(); +#ifdef DEBUG + if (ccddebug & CDB_FOLLOW) + printf("ccdiodone(%x)\n", cbp); + if (ccddebug & CDB_IO) { + printf("ccdiodone: bp %x bcount %d resid %d\n", + bp, bp->b_bcount, bp->b_resid); + printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n", + cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp, + cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount); + } +#endif + + if (cbp->b_flags & B_ERROR) { + bp->b_flags |= B_ERROR; + bp->b_error = biowait(cbp); +#ifdef DEBUG + printf("ccd%d: error %d on component %d\n", + unit, bp->b_error, cbp->b_pfcent & 0xFFFF); +#endif + } + count = cbp->b_bcount; + putcbuf(cbp); + + /* + * If all done, "interrupt". + * Again, sc_bp is only used to preserve the traditional interface. + */ + bp->b_resid -= count; + if (bp->b_resid < 0) + panic("ccdiodone: count"); + if (bp->b_resid == 0) { + ccd_softc[unit].sc_bp = bp; + ccdintr(unit); + } + splx(s); +} + +ccdioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + return(EINVAL); +} + +ccdsize(dev) + dev_t dev; +{ + int unit = ccdunit(dev); + register struct ccd_softc *cs = &ccd_softc[unit]; + + if (unit >= NCCD || (cs->sc_flags & CDF_INITED) == 0) + return(-1); + return(cs->sc_size); +} + +ccddump(dev) +{ + return(ENXIO); +} +#endif diff --git a/sys/dev/ccdvar.h b/sys/dev/ccdvar.h new file mode 100644 index 000000000000..42d6020080b0 --- /dev/null +++ b/sys/dev/ccdvar.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah Hdr: cdvar.h 1.1 90/07/09 + * from: @(#)cdvar.h 7.2 (Berkeley) 11/4/90 + * $Id: ccdvar.h,v 1.1 1994/06/24 14:11:04 hpeyerl Exp $ + */ + +#define NCCDISKS 8 /* max # of component disks */ + +/* + * A concatenated disk is described at config time by this structure. + */ +struct ccddevice { + int ccd_unit; /* logical unit of this ccd */ + int ccd_interleave; /* interleave (DEV_BSIZE blocks) */ + int ccd_flags; /* misc. information */ + int ccd_dk; /* disk number */ + dev_t ccd_dev[NCCDISKS]; /* component devices */ +}; + +/* ccd_flags */ +#define CDF_SWAP 0x01 /* interleave should be dmmax */ +#define CDF_UNIFORM 0x02 /* use LCD of sizes for uniform interleave */ + +/* + * Component info table. + * Describes a single component of a concatenated disk. + */ +struct ccdcinfo { + dev_t ci_dev; /* devno */ + size_t ci_size; /* size */ +}; + +/* + * Interleave description table. + * Computed at boot time to speed irregular-interleave lookups. + * The idea is that we interleave in "groups". First we interleave + * evenly over all component disks up to the size of the smallest + * component (the first group), then we interleave evenly over all + * remaining disks up to the size of the next-smallest (second group), + * and so on. + * + * Each table entry describes the interleave characteristics of one + * of these groups. For example if a concatenated disk consisted of + * three components of 5, 3, and 7 DEV_BSIZE blocks interleaved at + * DEV_BSIZE (1), the table would have three entries: + * + * ndisk startblk startoff dev + * 3 0 0 0, 1, 2 + * 2 9 3 0, 2 + * 1 13 5 2 + * 0 - - - + * + * which says that the first nine blocks (0-8) are interleaved over + * 3 disks (0, 1, 2) starting at block offset 0 on any component disk, + * the next 4 blocks (9-12) are interleaved over 2 disks (0, 2) starting + * at component block 3, and the remaining blocks (13-14) are on disk + * 2 starting at offset 5. + */ +struct ccdiinfo { + int ii_ndisk; /* # of disks range is interleaved over */ + daddr_t ii_startblk; /* starting scaled block # for range */ + daddr_t ii_startoff; /* starting component offset (block #) */ + char ii_index[NCCDISKS];/* ordered list of components in range */ +}; + +#ifdef KERNEL +extern struct ccddevice ccddevice[]; +#endif