#include "use_ccd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/nlookup.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/disk.h>
#include <sys/dtype.h>
#include <sys/diskslice.h>
#include <sys/devicestat.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/ccdvar.h>
#include <vm/vm_zone.h>
#include <vfs/ufs/dinode.h>
#include <vfs/ufs/fs.h>
#include <sys/buf2.h>
#if defined(CCDDEBUG) && !defined(DEBUG)
#define DEBUG
#endif
#ifdef DEBUG
#define CCDB_FOLLOW 0x01
#define CCDB_INIT 0x02
#define CCDB_IO 0x04
#define CCDB_LABEL 0x08
#define CCDB_VNODE 0x10
static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL |
CCDB_VNODE;
SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, "");
#undef DEBUG
#endif
#define ccdunit(x) dkunit(x)
#define ccdpart(x) dkpart(x)
struct ccdbuf {
struct buf cb_buf;
struct vnode *cb_vp;
struct bio *cb_obio;
int cb_unit;
int cb_comp;
int cb_pflags;
struct ccdbuf *cb_mirror;
};
#define CCDPF_MIRROR_DONE 1
static d_open_t ccdopen;
static d_close_t ccdclose;
static d_strategy_t ccdstrategy;
static d_ioctl_t ccdioctl;
static d_dump_t ccddump;
static struct dev_ops ccd_ops = {
{ "ccd", 0, D_DISK | D_MPSAFE },
.d_open = ccdopen,
.d_close = ccdclose,
.d_read = physread,
.d_write = physwrite,
.d_ioctl = ccdioctl,
.d_strategy = ccdstrategy,
.d_dump = ccddump
};
static void ccdattach (void);
static int ccddetach (void);
static int ccd_modevent (module_t, int, void *);
static void ccdiodone (struct bio *bio);
static void ccdstart (struct ccd_softc *, struct bio *);
static void ccdinterleave (struct ccd_softc *, int);
static void ccdintr (struct ccd_softc *, struct bio *);
static int ccdinit (struct ccddevice *, char **, struct ucred *);
static int ccdlookup (char *, struct vnode **);
static void ccdbuffer (struct ccdbuf **ret, struct ccd_softc *,
struct bio *, off_t, caddr_t, long);
static int ccdlock (struct ccd_softc *);
static void ccdunlock (struct ccd_softc *);
#ifdef DEBUG
static void printiinfo (struct ccdiinfo *);
#endif
struct ccd_softc *ccd_softc;
struct ccddevice *ccddevs;
static int numccd = 0;
static struct ccdbuf *
getccdbuf(void)
{
struct ccdbuf *cbp;
cbp = kmalloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK | M_ZERO);
initbufbio(&cbp->cb_buf);
buf_dep_init(&cbp->cb_buf);
BUF_LOCK(&cbp->cb_buf, LK_EXCLUSIVE);
BUF_KERNPROC(&cbp->cb_buf);
cbp->cb_buf.b_flags = B_PAGING | B_BNOCLIP;
return(cbp);
}
static void
putccdbuf(struct ccdbuf *cbp)
{
BUF_UNLOCK(&cbp->cb_buf);
uninitbufbio(&cbp->cb_buf);
kfree(cbp, M_DEVBUF);
}
static void
ccdattach(void)
{
struct disk_info info;
struct ccd_softc *cs;
int i;
int num = NCCD;
if (num > 1)
kprintf("ccd0-%d: Concatenated disk drivers\n", num-1);
else
kprintf("ccd0: Concatenated disk driver\n");
ccd_softc = kmalloc(num * sizeof(struct ccd_softc), M_DEVBUF,
M_WAITOK | M_ZERO);
ccddevs = kmalloc(num * sizeof(struct ccddevice), M_DEVBUF,
M_WAITOK | M_ZERO);
numccd = num;
bzero(&info, sizeof(info));
info.d_media_blksize = DEV_BSIZE;
for (i = 0; i < numccd; ++i) {
cs = &ccd_softc[i];
cs->sc_dev = disk_create(i, &cs->sc_disk, &ccd_ops);
cs->sc_dev->si_drv1 = cs;
cs->sc_dev->si_iosize_max = 256 * 512;
disk_setdiskinfo(&cs->sc_disk, &info);
}
}
static int
ccddetach(void)
{
struct ccd_softc *cs;
struct dev_ioctl_args ioctl_args;
int i;
int error = 0;
int eval;
bzero(&ioctl_args, sizeof(ioctl_args));
for (i = 0; i < numccd; ++i) {
cs = &ccd_softc[i];
if (cs->sc_dev == NULL)
continue;
ioctl_args.a_head.a_dev = cs->sc_dev;
ioctl_args.a_cmd = CCDIOCCLR;
ioctl_args.a_fflag = FWRITE;
eval = ccdioctl(&ioctl_args);
if (eval && eval != ENXIO) {
kprintf("ccd%d: In use, cannot detach\n", i);
error = EBUSY;
}
}
if (error == 0) {
for (i = 0; i < numccd; ++i) {
cs = &ccd_softc[i];
if (cs->sc_dev == NULL)
continue;
disk_destroy(&cs->sc_disk);
cs->sc_dev = NULL;
}
if (ccd_softc)
kfree(ccd_softc, M_DEVBUF);
if (ccddevs)
kfree(ccddevs, M_DEVBUF);
}
return (error);
}
static int
ccd_modevent(module_t mod, int type, void *data)
{
int error = 0;
switch (type) {
case MOD_LOAD:
ccdattach();
break;
case MOD_UNLOAD:
error = ccddetach();
break;
default:
break;
}
return (error);
}
DEV_MODULE(ccd, ccd_modevent, NULL);
static int
ccdinit(struct ccddevice *ccd, char **cpaths, struct ucred *cred)
{
struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
struct ccdcinfo *ci = NULL;
int ix;
struct vnode *vp;
u_int64_t skip;
u_int64_t size;
u_int64_t minsize;
int maxsecsize;
struct partinfo dpart;
struct ccdgeom *ccg = &cs->sc_geom;
char tmppath[MAXPATHLEN];
int error = 0;
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccdinit: unit %d\n", ccd->ccd_unit);
#endif
cs->sc_size = 0;
cs->sc_ileave = ccd->ccd_interleave;
cs->sc_nccdisks = ccd->ccd_ndev;
cs->sc_cinfo = kmalloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
M_DEVBUF, M_WAITOK);
cs->sc_maxiosize = MAXPHYS;
lockinit(&cs->sc_lock, "ccdlck", 0, 0);
ccdlock(cs);
maxsecsize = 0;
minsize = 0;
for (ix = 0; ix < cs->sc_nccdisks; ix++) {
vp = ccd->ccd_vpp[ix];
ci = &cs->sc_cinfo[ix];
ci->ci_vp = vp;
bzero(tmppath, sizeof(tmppath));
if ((error = copyinstr(cpaths[ix], tmppath,
MAXPATHLEN, &ci->ci_pathlen)) != 0) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccd%d: can't copy path, error = %d\n",
ccd->ccd_unit, error);
#endif
goto fail;
}
ci->ci_path = kmalloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK);
bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
ci->ci_dev = vn_todev(vp);
if (ci->ci_dev->si_iosize_max &&
cs->sc_maxiosize > ci->ci_dev->si_iosize_max) {
cs->sc_maxiosize = ci->ci_dev->si_iosize_max;
}
error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart, FREAD,
cred, NULL);
if (error) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccd%d: %s: ioctl failed, error = %d\n",
ccd->ccd_unit, ci->ci_path, error);
#endif
goto fail;
}
if (dpart.fstype != FS_CCD &&
!kuuid_is_ccd(&dpart.fstype_uuid)) {
kprintf("ccd%d: %s: filesystem type must be 'ccd'\n",
ccd->ccd_unit, ci->ci_path);
error = EFTYPE;
goto fail;
}
if (maxsecsize < dpart.media_blksize)
maxsecsize = dpart.media_blksize;
skip = 16;
if (skip < dpart.reserved_blocks)
skip = dpart.reserved_blocks;
size = dpart.media_blocks - skip;
if (cs->sc_ileave > 1)
size -= size % cs->sc_ileave;
if ((int64_t)size <= 0) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccd%d: %s: size == 0\n",
ccd->ccd_unit, ci->ci_path);
#endif
error = ENODEV;
goto fail;
}
if (minsize == 0 || minsize > size)
minsize = size;
ci->ci_skip = skip;
ci->ci_size = size;
cs->sc_size += size;
}
kprintf("ccd%d: max component iosize is %d total blocks %lld\n",
cs->sc_unit, cs->sc_maxiosize, (long long)cs->sc_size);
if ((cs->sc_ileave > 0) &&
(cs->sc_ileave % (maxsecsize / DEV_BSIZE))) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccd%d: interleave must be at least %d\n",
ccd->ccd_unit, (maxsecsize / DEV_BSIZE));
#endif
error = EINVAL;
goto fail;
}
if (ccd->ccd_flags & CCDF_UNIFORM) {
for (ci = cs->sc_cinfo;
ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
ci->ci_size = minsize;
}
if (ccd->ccd_flags & CCDF_MIRROR) {
if (cs->sc_nccdisks % 2) {
kprintf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit );
error = EINVAL;
goto fail;
}
if (cs->sc_ileave == 0) {
kprintf("ccd%d: an interleave must be specified when mirroring\n", ccd->ccd_unit);
error = EINVAL;
goto fail;
}
cs->sc_size = (cs->sc_nccdisks/2) * minsize;
} else if (ccd->ccd_flags & CCDF_PARITY) {
cs->sc_size = (cs->sc_nccdisks-1) * minsize;
} else {
if (cs->sc_ileave == 0) {
kprintf("ccd%d: an interleave must be specified when using parity\n", ccd->ccd_unit);
error = EINVAL;
goto fail;
}
cs->sc_size = cs->sc_nccdisks * minsize;
}
}
ccdinterleave(cs, ccd->ccd_unit);
ccg->ccg_secsize = maxsecsize;
ccg->ccg_ntracks = 1;
ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize;
ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
devstat_add_entry(&cs->device_stats, "ccd", ccd->ccd_unit,
ccg->ccg_secsize, DEVSTAT_ALL_SUPPORTED,
DEVSTAT_TYPE_STORARRAY |DEVSTAT_TYPE_IF_OTHER,
DEVSTAT_PRIORITY_ARRAY);
cs->sc_flags |= CCDF_INITED;
cs->sc_cflags = ccd->ccd_flags;
cs->sc_unit = ccd->ccd_unit;
return (0);
fail:
while (ci > cs->sc_cinfo) {
ci--;
kfree(ci->ci_path, M_DEVBUF);
}
kfree(cs->sc_cinfo, M_DEVBUF);
cs->sc_cinfo = NULL;
return (error);
}
static void
ccdinterleave(struct ccd_softc *cs, int unit)
{
struct ccdcinfo *ci, *smallci;
struct ccdiinfo *ii;
u_int64_t bn;
u_int64_t lbn;
u_int64_t size;
int icount;
int ix;
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
kprintf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
#endif
icount = cs->sc_nccdisks + 1;
cs->sc_itable = kmalloc(icount * sizeof(struct ccdiinfo),
M_DEVBUF, M_WAITOK|M_ZERO);
if (cs->sc_ileave == 0) {
bn = 0;
ii = cs->sc_itable;
for (ix = 0; ix < cs->sc_nccdisks; ix++) {
ii->ii_index = kmalloc(sizeof(int), M_DEVBUF, M_WAITOK);
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 & CCDB_INIT)
printiinfo(cs->sc_itable);
#endif
return;
}
size = 0;
bn = lbn = 0;
for (ii = cs->sc_itable; ii < &cs->sc_itable[icount]; ++ii) {
ii->ii_index = kmalloc((sizeof(int) * cs->sc_nccdisks),
M_DEVBUF, M_WAITOK);
smallci = NULL;
ci = cs->sc_cinfo;
while (ci < &cs->sc_cinfo[cs->sc_nccdisks]) {
if (ci->ci_size > size &&
(smallci == NULL ||
ci->ci_size < smallci->ci_size)) {
smallci = ci;
}
++ci;
}
if (smallci == NULL) {
ii->ii_ndisk = 0;
break;
}
ii->ii_startblk = bn / cs->sc_ileave;
ii->ii_startoff = lbn;
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;
}
if (ii == &cs->sc_itable[icount])
panic("ccdinterlave software bug! table exhausted");
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
printiinfo(cs->sc_itable);
#endif
}
static int
ccdopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit = ccdunit(dev);
struct ccd_softc *cs;
int error = 0;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdopen(%x, %x)\n", dev, flags);
#endif
if (unit >= numccd)
return (ENXIO);
cs = &ccd_softc[unit];
if ((error = ccdlock(cs)) == 0) {
ccdunlock(cs);
}
return (error);
}
static int
ccdclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit = ccdunit(dev);
struct ccd_softc *cs;
int error = 0;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdclose(%x, %x)\n", dev, flags);
#endif
if (unit >= numccd)
return (ENXIO);
cs = &ccd_softc[unit];
if ((error = ccdlock(cs)) == 0) {
ccdunlock(cs);
}
return (error);
}
static int
ccdstrategy(struct dev_strategy_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bio *bio = ap->a_bio;
int unit = ccdunit(dev);
struct bio *nbio;
struct buf *bp = bio->bio_buf;
struct ccd_softc *cs = &ccd_softc[unit];
u_int64_t pbn;
u_int32_t sz;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdstrategy(%x): unit %d\n", bp, unit);
#endif
if ((cs->sc_flags & CCDF_INITED) == 0) {
bp->b_error = ENXIO;
goto error;
}
if (bp->b_bcount == 0) {
bp->b_resid = 0;
goto done;
}
pbn = bio->bio_offset / cs->sc_geom.ccg_secsize;
sz = howmany(bp->b_bcount, cs->sc_geom.ccg_secsize);
if ((int64_t)pbn < 0)
goto bad;
if (pbn + sz > cs->sc_size) {
if (pbn > cs->sc_size || (bp->b_flags & B_BNOCLIP))
goto bad;
if (pbn == cs->sc_size) {
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_INVAL;
goto done;
}
sz = (long)(cs->sc_size - pbn);
bp->b_bcount = sz * cs->sc_geom.ccg_secsize;
}
nbio = bio;
bp->b_resid = bp->b_bcount;
nbio->bio_driver_info = dev;
ccdstart(cs, nbio);
return(0);
bad:
bp->b_error = EINVAL;
error:
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR | B_INVAL;
done:
biodone(bio);
return(0);
}
static void
ccdstart(struct ccd_softc *cs, struct bio *bio)
{
long bcount, rcount;
struct ccdbuf *cbp[4];
struct buf *bp = bio->bio_buf;
caddr_t addr;
off_t doffset;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdstart(%x, %x)\n", cs, bp);
#endif
devstat_start_transaction(&cs->device_stats);
doffset = bio->bio_offset;
addr = bp->b_data;
for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
ccdbuffer(cbp, cs, bio, doffset, addr, bcount);
rcount = cbp[0]->cb_buf.b_bcount;
if (cs->sc_cflags & CCDF_MIRROR) {
if (cbp[0]->cb_buf.b_cmd != BUF_CMD_READ) {
vn_strategy(cbp[0]->cb_vp,
&cbp[0]->cb_buf.b_bio1);
vn_strategy(cbp[1]->cb_vp,
&cbp[1]->cb_buf.b_bio1);
} else {
int pick = cs->sc_pick;
daddr_t range = cs->sc_size / 16 * cs->sc_geom.ccg_secsize;
if (doffset < cs->sc_blk[pick] - range ||
doffset > cs->sc_blk[pick] + range
) {
cs->sc_pick = pick = 1 - pick;
}
cs->sc_blk[pick] = doffset + rcount;
vn_strategy(cbp[pick]->cb_vp,
&cbp[pick]->cb_buf.b_bio1);
}
} else {
vn_strategy(cbp[0]->cb_vp,
&cbp[0]->cb_buf.b_bio1);
}
doffset += rcount;
addr += rcount;
}
}
static void
ccdbuffer(struct ccdbuf **cb, struct ccd_softc *cs, struct bio *bio,
off_t doffset, caddr_t addr, long bcount)
{
struct ccdcinfo *ci, *ci2 = NULL;
struct ccdbuf *cbp;
u_int64_t bn;
u_int64_t cbn;
u_int64_t cboff;
off_t cbc;
#ifdef DEBUG
if (ccddebug & CCDB_IO)
kprintf("ccdbuffer(%x, %x, %d, %x, %d)\n",
cs, bp, bn, addr, bcount);
#endif
bn = doffset / cs->sc_geom.ccg_secsize;
cbn = bn;
cboff = 0;
if (cs->sc_ileave == 0) {
daddr_t sblk;
sblk = 0;
for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
sblk += ci->ci_size;
cbn -= sblk;
} else {
struct ccdiinfo *ii;
int ccdisk, off;
cboff = cbn % cs->sc_ileave;
cbn = 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 {
if (cs->sc_cflags & CCDF_MIRROR) {
int ndisk2 = ii->ii_ndisk / 2;
ccdisk = ii->ii_index[off % ndisk2];
cbn = ii->ii_startoff + off / ndisk2;
ci2 = &cs->sc_cinfo[ccdisk + ndisk2];
} else if (cs->sc_cflags & CCDF_PARITY) {
int ndisk2 = ii->ii_ndisk - 1;
ccdisk = ii->ii_index[off % ndisk2];
cbn = ii->ii_startoff + off / ndisk2;
if (cbn % ii->ii_ndisk <= ccdisk)
ccdisk++;
} else {
ccdisk = ii->ii_index[off % ii->ii_ndisk];
cbn = ii->ii_startoff + off / ii->ii_ndisk;
}
}
ci = &cs->sc_cinfo[ccdisk];
cbn *= cs->sc_ileave;
}
cbp = getccdbuf();
cbp->cb_buf.b_cmd = bio->bio_buf->b_cmd;
cbp->cb_buf.b_flags |= bio->bio_buf->b_flags;
cbp->cb_buf.b_data = addr;
cbp->cb_vp = ci->ci_vp;
if (cs->sc_ileave == 0)
cbc = dbtob((off_t)(ci->ci_size - cbn));
else
cbc = dbtob((off_t)(cs->sc_ileave - cboff));
if (cbc > cs->sc_maxiosize)
cbc = cs->sc_maxiosize;
cbp->cb_buf.b_bcount = (cbc < bcount) ? cbc : bcount;
cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
cbp->cb_buf.b_bio1.bio_done = ccdiodone;
cbp->cb_buf.b_bio1.bio_caller_info1.ptr = cbp;
cbp->cb_buf.b_bio1.bio_offset = dbtob(cbn + cboff + ci->ci_skip);
cbp->cb_obio = bio;
cbp->cb_unit = cs - ccd_softc;
cbp->cb_comp = ci - cs->sc_cinfo;
#ifdef DEBUG
if (ccddebug & CCDB_IO)
kprintf(" dev %x(u%d): cbp %x off %lld addr %x bcnt %d\n",
ci->ci_dev, ci-cs->sc_cinfo, cbp,
cbp->cb_buf.b_bio1.bio_offset,
cbp->cb_buf.b_data, cbp->cb_buf.b_bcount);
#endif
cb[0] = cbp;
if (cs->sc_cflags & CCDF_MIRROR) {
cbp = getccdbuf();
cbp->cb_buf.b_cmd = bio->bio_buf->b_cmd;
cbp->cb_buf.b_flags |= bio->bio_buf->b_flags;
cbp->cb_buf.b_data = addr;
cbp->cb_vp = ci2->ci_vp;
if (cs->sc_ileave == 0)
cbc = dbtob((off_t)(ci->ci_size - cbn));
else
cbc = dbtob((off_t)(cs->sc_ileave - cboff));
if (cbc > cs->sc_maxiosize)
cbc = cs->sc_maxiosize;
cbp->cb_buf.b_bcount = (cbc < bcount) ? cbc : bcount;
cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
cbp->cb_buf.b_bio1.bio_done = ccdiodone;
cbp->cb_buf.b_bio1.bio_caller_info1.ptr = cbp;
cbp->cb_buf.b_bio1.bio_offset = dbtob(cbn + cboff + ci2->ci_skip);
cbp->cb_obio = bio;
cbp->cb_unit = cs - ccd_softc;
cbp->cb_comp = ci2 - cs->sc_cinfo;
cb[1] = cbp;
cb[0]->cb_mirror = cb[1];
cb[1]->cb_mirror = cb[0];
cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
}
}
static void
ccdintr(struct ccd_softc *cs, struct bio *bio)
{
struct buf *bp = bio->bio_buf;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdintr(%x, %x)\n", cs, bp);
#endif
if (bp->b_flags & B_ERROR)
bp->b_resid = bp->b_bcount;
devstat_end_transaction_buf(&cs->device_stats, bp);
biodone(bio);
}
static void
ccdiodone(struct bio *bio)
{
struct ccdbuf *cbp = bio->bio_caller_info1.ptr;
struct bio *obio = cbp->cb_obio;
struct buf *obp = obio->bio_buf;
int unit = cbp->cb_unit;
struct ccd_softc *sc = &ccd_softc[unit];
int count;
clearbiocache(bio->bio_next);
ccdlock(sc);
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
kprintf("ccdiodone(%x)\n", cbp);
if (ccddebug & CCDB_IO) {
kprintf("ccdiodone: bp %x bcount %d resid %d\n",
obp, obp->b_bcount, obp->b_resid);
kprintf(" dev %x(u%d), cbp %x off %lld addr %x bcnt %d\n",
cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
cbp->cb_buf.b_loffset, cbp->cb_buf.b_data,
cbp->cb_buf.b_bcount);
}
#endif
if (cbp->cb_buf.b_flags & B_ERROR) {
const char *msg = "";
if ((sc->sc_cflags & CCDF_MIRROR) &&
(cbp->cb_buf.b_cmd == BUF_CMD_READ) &&
(cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
msg = ", trying other disk";
sc->sc_pick = 1 - sc->sc_pick;
sc->sc_blk[sc->sc_pick] = obio->bio_offset;
} else {
obp->b_flags |= B_ERROR;
obp->b_error = cbp->cb_buf.b_error ?
cbp->cb_buf.b_error : EIO;
}
kprintf("ccd%d: error %d on component %d "
"offset %jd (ccd offset %jd)%s\n",
unit, obp->b_error, cbp->cb_comp,
(intmax_t)cbp->cb_buf.b_bio2.bio_offset,
(intmax_t)obio->bio_offset,
msg);
}
if (sc->sc_cflags & CCDF_MIRROR) {
if (cbp->cb_buf.b_cmd != BUF_CMD_READ) {
if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
putccdbuf(cbp);
ccdunlock(sc);
return;
}
} else {
if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
if (cbp->cb_buf.b_flags & B_ERROR) {
cbp->cb_mirror->cb_pflags |=
CCDPF_MIRROR_DONE;
vn_strategy(
cbp->cb_mirror->cb_vp,
&cbp->cb_mirror->cb_buf.b_bio1
);
putccdbuf(cbp);
ccdunlock(sc);
return;
} else {
putccdbuf(cbp->cb_mirror);
}
}
}
}
count = cbp->cb_buf.b_bufsize;
putccdbuf(cbp);
obp->b_resid -= count;
if (obp->b_resid < 0)
panic("ccdiodone: count");
ccdunlock(sc);
if (obp->b_resid == 0)
ccdintr(sc, obio);
}
static int
ccdioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit = ccdunit(dev);
int i, j, lookedup = 0, error = 0;
struct ccd_softc *cs;
struct ccd_ioctl *ccio = (struct ccd_ioctl *)ap->a_data;
struct ccddevice ccd;
struct disk_info info;
char **cpp;
struct vnode **vpp;
if (unit >= numccd)
return (ENXIO);
cs = &ccd_softc[unit];
bzero(&ccd, sizeof(ccd));
switch (ap->a_cmd) {
case CCDIOCSET:
if (cs->sc_flags & CCDF_INITED)
return (EBUSY);
if ((ap->a_fflag & FWRITE) == 0)
return (EBADF);
if ((error = ccdlock(cs)) != 0)
return (error);
if (ccio->ccio_ndisks > CCD_MAXNDISKS) {
ccdunlock(cs);
return (EINVAL);
}
ccd.ccd_unit = unit;
ccd.ccd_interleave = ccio->ccio_ileave;
if (ccd.ccd_interleave == 0 &&
((ccio->ccio_flags & CCDF_MIRROR) ||
(ccio->ccio_flags & CCDF_PARITY))) {
kprintf("ccd%d: disabling mirror/parity, interleave is 0\n", unit);
ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY);
}
if ((ccio->ccio_flags & CCDF_MIRROR) &&
(ccio->ccio_flags & CCDF_PARITY)) {
kprintf("ccd%d: can't specify both mirror and parity, using mirror\n", unit);
ccio->ccio_flags &= ~CCDF_PARITY;
}
if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) &&
!(ccio->ccio_flags & CCDF_UNIFORM)) {
kprintf("ccd%d: mirror/parity forces uniform flag\n",
unit);
ccio->ccio_flags |= CCDF_UNIFORM;
}
ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK;
cpp = kmalloc(ccio->ccio_ndisks * sizeof(char *),
M_DEVBUF, M_WAITOK);
vpp = kmalloc(ccio->ccio_ndisks * sizeof(struct vnode *),
M_DEVBUF, M_WAITOK);
error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
ccio->ccio_ndisks * sizeof(char **));
if (error) {
kfree(vpp, M_DEVBUF);
kfree(cpp, M_DEVBUF);
ccdunlock(cs);
return (error);
}
#ifdef DEBUG
if (ccddebug & CCDB_INIT) {
for (i = 0; i < ccio->ccio_ndisks; ++i)
kprintf("ccdioctl: component %d: 0x%x\n",
i, cpp[i]);
}
#endif
for (i = 0; i < ccio->ccio_ndisks; ++i) {
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
kprintf("ccdioctl: lookedup = %d\n", lookedup);
#endif
if ((error = ccdlookup(cpp[i], &vpp[i])) != 0) {
for (j = 0; j < lookedup; ++j)
(void)vn_close(vpp[j], FREAD|FWRITE, NULL);
kfree(vpp, M_DEVBUF);
kfree(cpp, M_DEVBUF);
ccdunlock(cs);
return (error);
}
++lookedup;
}
ccd.ccd_cpp = cpp;
ccd.ccd_vpp = vpp;
ccd.ccd_ndev = ccio->ccio_ndisks;
if ((error = ccdinit(&ccd, cpp, ap->a_cred)) != 0) {
for (j = 0; j < lookedup; ++j)
vn_close(vpp[j], FREAD|FWRITE, NULL);
kfree(vpp, M_DEVBUF);
kfree(cpp, M_DEVBUF);
ccdunlock(cs);
return (error);
}
bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
ccio->ccio_unit = unit;
ccio->ccio_size = cs->sc_size;
bzero(&info, sizeof(info));
info.d_media_blksize = cs->sc_geom.ccg_secsize;
info.d_media_blocks = cs->sc_size;
info.d_nheads = cs->sc_geom.ccg_ntracks;
info.d_secpertrack = cs->sc_geom.ccg_nsectors;
info.d_ncylinders = cs->sc_geom.ccg_ncylinders;
info.d_secpercyl = info.d_nheads * info.d_secpertrack;
disk_setdiskinfo(&cs->sc_disk, &info);
ccdunlock(cs);
break;
case CCDIOCCLR:
if ((cs->sc_flags & CCDF_INITED) == 0)
return (ENXIO);
if ((ap->a_fflag & FWRITE) == 0)
return (EBADF);
if ((error = ccdlock(cs)) != 0)
return (error);
if (dev_drefs(cs->sc_dev) > 1) {
ccdunlock(cs);
return (EBUSY);
}
for (i = 0; i < cs->sc_nccdisks; ++i) {
#ifdef DEBUG
if (ccddebug & CCDB_VNODE)
vprint("CCDIOCCLR: vnode info",
cs->sc_cinfo[i].ci_vp);
#endif
(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, NULL);
kfree(cs->sc_cinfo[i].ci_path, M_DEVBUF);
}
for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
kfree(cs->sc_itable[i].ii_index, M_DEVBUF);
kfree(cs->sc_cinfo, M_DEVBUF);
kfree(cs->sc_itable, M_DEVBUF);
cs->sc_cinfo = NULL;
cs->sc_itable = NULL;
cs->sc_flags &= ~CCDF_INITED;
kfree(ccddevs[unit].ccd_cpp, M_DEVBUF);
kfree(ccddevs[unit].ccd_vpp, M_DEVBUF);
bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
devstat_remove_entry(&cs->device_stats);
ccdunlock(cs);
break;
default:
return (ENOTTY);
}
return (0);
}
static int
ccddump(struct dev_dump_args *ap)
{
return ENXIO;
}
static int
ccdlookup(char *path, struct vnode **vpp)
{
struct nlookupdata nd;
struct vnode *vp;
int error;
*vpp = NULL;
error = nlookup_init(&nd, path, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
if (error)
return (error);
if ((error = vn_open(&nd, NULL, FREAD|FWRITE, 0)) != 0) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
kprintf("ccdlookup: vn_open error = %d\n", error);
#endif
goto done;
}
vp = nd.nl_open_vp;
if (vp->v_opencount > 1) {
error = EBUSY;
goto done;
}
if (!vn_isdisk(vp, &error))
goto done;
#ifdef DEBUG
if (ccddebug & CCDB_VNODE)
vprint("ccdlookup: vnode info", vp);
#endif
vn_unlock(vp);
nd.nl_open_vp = NULL;
nlookup_done(&nd);
*vpp = vp;
return (0);
done:
nlookup_done(&nd);
return (error);
}
static int
ccdlock(struct ccd_softc *cs)
{
lockmgr(&cs->sc_lock, LK_EXCLUSIVE);
return (0);
}
static void
ccdunlock(struct ccd_softc *cs)
{
lockmgr(&cs->sc_lock, LK_RELEASE);
}
#ifdef DEBUG
static void
printiinfo(struct ccdiinfo *ii)
{
int ix, i;
for (ix = 0; ii->ii_ndisk; ix++, ii++) {
kprintf(" 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++)
kprintf(" %d", ii->ii_index[i]);
kprintf("\n");
}
}
#endif