From 3e425b968d4b5478e0e1edda78ca13e0e91fa7fe Mon Sep 17 00:00:00 2001 From: Julian Elischer Date: Sun, 19 Apr 1998 23:32:49 +0000 Subject: Add changes and code to implement a functional DEVFS. This code will be turned on with the TWO options DEVFS and SLICE. (see LINT) Two labels PRE_DEVFS_SLICE and POST_DEVFS_SLICE will deliniate these changes. /dev will be automatically mounted by init (thanks phk) on bootup. See /sys/dev/slice/slice.4 for more info. All code should act the same without these options enabled. Mike Smith, Poul Henning Kamp, Soeren, and a few dozen others This code does not support the following: bad144 handling. Persistance. (My head is still hurting from the last time we discussed this) ATAPI flopies are not handled by the SLICE code yet. When this code is running, all major numbers are arbitrary and COULD be dynamically assigned. (this is not done, for POLA only) Minor numbers for disk slices ARE arbitray and dynamically assigned. --- sys/dev/slice/disklabel.c | 827 ++++++++++++++++++++++++++++++++++++++++++ sys/dev/slice/mbr.c | 837 +++++++++++++++++++++++++++++++++++++++++++ sys/dev/slice/slice.4 | 152 ++++++++ sys/dev/slice/slice.h | 195 ++++++++++ sys/dev/slice/slice_base.c | 671 ++++++++++++++++++++++++++++++++++ sys/dev/slice/slice_device.c | 388 ++++++++++++++++++++ sys/dev/slice/slices.thought | 124 +++++++ 7 files changed, 3194 insertions(+) create mode 100644 sys/dev/slice/disklabel.c create mode 100644 sys/dev/slice/mbr.c create mode 100644 sys/dev/slice/slice.4 create mode 100644 sys/dev/slice/slice.h create mode 100644 sys/dev/slice/slice_base.c create mode 100644 sys/dev/slice/slice_device.c create mode 100644 sys/dev/slice/slices.thought (limited to 'sys/dev/slice') diff --git a/sys/dev/slice/disklabel.c b/sys/dev/slice/disklabel.c new file mode 100644 index 0000000000000..cb2119b2f1894 --- /dev/null +++ b/sys/dev/slice/disklabel.c @@ -0,0 +1,827 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. + * + * $Id: $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +struct private_data { + u_int32_t flags; + u_int8_t rflags; + u_int8_t wflags; + int savedoflags; + struct slice *slice_down; + struct disklabel disklabel; + struct subdev { + int part; + struct slice *slice; + struct slicelimits limit; + struct private_data *pd; + u_int32_t offset; /* all disklabel supports */ + } subdevs[MAXPARTITIONS]; +}; + +static sl_h_constructor_t dkl_constructor; /* constructor (from device) */ +static sl_h_IO_req_t dkl_IOreq; /* IO req downward (to device) */ +static sl_h_ioctl_t dkl_ioctl; /* ioctl req downward (to device) */ +static sl_h_open_t dkl_open; /* downwards travelling open */ +static sl_h_close_t dkl_close; /* downwards travelling close */ +static sl_h_claim_t dkl_claim; /* upwards travelling claim */ +static sl_h_revoke_t dkl_revoke;/* upwards travelling revokation */ +static sl_h_verify_t dkl_verify;/* things changed, are we stil valid? */ +static sl_h_upconfig_t dkl_upconfig;/* config requests from below */ + +static struct slice_handler slicetype = { + "disklabel", + 0, + NULL, + 0, + &dkl_constructor, /* constructor */ + &dkl_IOreq, + &dkl_ioctl, + &dkl_open, + &dkl_close, + &dkl_revoke, /* revoke */ + &dkl_claim, /* claim */ + &dkl_verify, /* verify */ + &dkl_upconfig /* subslice manipulation */ +}; + +static void +sd_drvinit(void *unused) +{ + sl_newtype(&slicetype); +} + +SYSINIT(sddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sd_drvinit, NULL); + +/*- + * Given a slice, extract out our table of information + */ +/*- + * Attempt to read a disk label from a slice. + * The label must be partly set up before this: secpercyl, secsize + * and anything required in the strategy routine (e.g., dummy bounds for the + * partition containing the label) must be filled in before calling us. + * Returns NULL on success and an error string on failure. + */ +static int +dkl_extract_table(sl_p slice, struct disklabel * lp) +{ + int error = EINVAL; + struct buf *bp; + struct disklabel *dlp; + struct partition *dp; + int part; + int slice_offset; /* XXX */ + + RR; + /* start off with a known result */ + bzero(lp, sizeof(*lp)); + if (error = slice_readblock(slice, LABELSECTOR, &bp)) + return (error); + /* + * Step through the block looking for the label. + * It may not be at the front (Though I have never seen this). + * When found, copy it to the destination supplied. + */ + error = EINVAL; + for (dlp = (struct disklabel *) bp->b_data; + dlp <= (struct disklabel *) ((char *) bp->b_data + + slice->limits.blksize + - sizeof(*dlp)); + dlp = (struct disklabel *) ((char *) dlp + sizeof(long))) { + if ((dlp->d_magic != DISKMAGIC) || + (dlp->d_magic2 != DISKMAGIC) || + (dlp->d_npartitions > MAXPARTITIONS) || + dkcksum(dlp)) + continue; + error = 0; + bcopy(dlp, lp, sizeof(*lp)); + /* + * disklabels are done relative to the base of the disk, + * rather than the local partition, (DUH!) + * so use partition 2 (c) to get the base, + * and subtract it from all non-0 offsets. + */ + dp = lp->d_partitions; + slice_offset = dp[2].p_offset; + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + /* + * We could be reloading, in which case skip + * entries already set up. + */ + if (dp->p_size == 0) + continue; + if( dp->p_offset < slice_offset ) { + printf("slice before 'c'\n"); + dp->p_size = 0; + continue; + } + dp->p_offset -= slice_offset; + } + break; + } + +done: + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + return (error); +} +/* + * given a table, write it to disk. + */ +static int +dkl_insert_table(sl_p slice, struct disklabel * lp) +{ + int error = EINVAL; + struct buf *bp; + struct disklabel *dlp; + struct partition *dp; + int part; + int slice_offset; /* XXX */ + + RR; + /* start off with a known result */ + if (error = slice_readblock(slice, LABELSECTOR, &bp)) + return (error); + /* + * Step through the block looking for the label. + * It may not be at the front (Though I have never seen this). + * When found, replace it witht he new one. + */ + error = EINVAL; + for (dlp = (struct disklabel *) bp->b_data; + dlp <= (struct disklabel *) ((char *) bp->b_data + + slice->limits.blksize + - sizeof(*dlp)); + dlp = (struct disklabel *) ((char *) dlp + sizeof(long))) { + if ((dlp->d_magic != DISKMAGIC) || + (dlp->d_magic2 != DISKMAGIC) || + (dlp->d_npartitions > MAXPARTITIONS) || + dkcksum(dlp)) + continue; + error = 0; + } + if (error) { + /* + * We didn't find one.. + * so clear the block and place the new disklabel + * at the start. + */ + bzero(bp->b_data, slice->limits.blksize); + dlp = (struct disklabel *) bp->b_data; + } + /* + * old disklabels are done relative to the base of the disk, + * rather than the local partition, (DUH!) + * so use partition 2 (c) to get the base, + * and subtract it from all non-0 offsets. + */ + dp = dlp->d_partitions; + slice_offset = dp[2].p_offset; + bcopy(lp, dlp, sizeof(*lp)); + slice_offset -= dp[2].p_offset; /* size we adjust by? */ + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + if (dp->p_size == 0) + continue; + dp->p_offset += slice_offset; + } + error = slice_writeblock(slice, LABELSECTOR, bp); +quit: + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + return (error); +} + + + +/*- + * look at a slice and figure out if we should be interested in it. (Is it + * ours?) + */ +static int +dkl_claim(struct slice * slice, struct slice * lower, void *ID) +{ + struct disklabel disklabel; + struct disklabel *dl, *dl0; + int error; + RR; + + /*- + * Try load a valid disklabel table. + * This is 90% of what we need to check. + */ + if ((error = dkl_extract_table(slice, &disklabel)) != 0) { + return (error); + } + /*- + * If there is no geometry info, extract it from the label + * as some drivers need this. + */ + /* XXX */ + + /*- + * well, it looks like one of ours. + */ + return (0); +} + +/*- + * This is a special HACK function for the IDE driver. + * It is here because everything it need is in scope here, + * but it is not really part of the SLICE code. + * Because old ESDI drives could not tell their geometry, They need + * to get it from the MBR or the disklabel. This is the disklabel bit. + */ +int +dkl_geom_hack(struct slice * slice, struct ide_geom *geom) +{ + struct disklabel disklabel; + struct disklabel *dl, *dl0; + int error; + RR; + + /* first check it's a disklabel*/ + if ((error = dkl_claim (slice, NULL, 0))) + return (error); + /*- + * Try load a valid disklabel table. + * This is wasteful but never called on new (< 5 YO ) drives. + */ + if ((error = dkl_extract_table(slice, &disklabel)) != 0) { + return (error); + } + geom->secpertrack = disklabel. d_nsectors; + geom->trackpercyl = disklabel.d_ntracks; + geom->cyls = disklabel.d_ncylinders; + return (0); +} + +/*- + * look at a slice we know to be ours and decide what the #$%^ to do with it. + */ +static int +dkl_constructor(sl_p slice) +{ + int i; + u_int64_t disksize = slice->limits.slicesize; + struct private_data *pd; + struct partition *dp, *dp0; + struct disklabel *dl; + sh_p tp; + char name[64]; + + int part; + int error = 0; + u_long dkl_offset; + + RR; + /*- + * If we are being called to re-load a slice, + * then don't reallocate resources. + */ + if ((pd = slice->private_up) == NULL) { + if (slice->name == NULL) { + printf("name is NULL\n"); + return (EINVAL); + } + if (strlen(slice->name) > 58) { + printf("slice: name %s too long\n", slice->name); + return (ENAMETOOLONG); + } + pd = malloc(sizeof(*pd), M_DEVBUF, M_NOWAIT); + if (pd == NULL) { + printf("fdisk: failed malloc\n"); + return (ENOMEM); + } + bzero(pd, sizeof(*pd)); + pd->slice_down = slice; + if ((error = dkl_extract_table(slice, &pd->disklabel)) != 0) { + struct partinfo data; + /* + * If it's just that there is no disklabel there, + * Then we fake one up and write it. if this were + * not ok, then we would have not been called. + * (as probe will have failed). If it's + * a physical error, then that's reason to fail. + */ + if (error != EINVAL) { + free(pd, M_DEVBUF); + return (error); + } + dkl_dummy_ioctl(slice, DIOCGPART, + (caddr_t) &data, 0, NULL); + bcopy(data.disklab, &pd->disklabel, + sizeof(pd->disklabel)); + if ((error = dkl_insert_table(slice, &pd->disklabel))) { + free(pd, M_DEVBUF); + return (error); + } + } + slice->refs++; + slice->handler_up = &slicetype; + slice->private_up = pd; + slicetype.refs++; + } + dl = &pd->disklabel; + dp0 = dl->d_partitions; + + /*- + * Handle each of the partitions. + * We should check that each makes sence and is legal. + * 1/ it should not already have a slice. + * 2/ should not be 0 length. + * 3/ should not go past end of our slice. + * 4/ should not overlap other slices. + * It can include sector 0 (unfortunatly) + */ + dp = dp0; + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + int i; + if ( part == 2 ) + continue; /* XXX skip the 'c' partition */ + /* + * We could be reloading, in which case skip + * entries already set up. + */ + if (pd->subdevs[part].slice != NULL) + breakout: continue; + /* + * also skip partitions not present + */ + if (dp->p_size == 0) + continue; +printf(" part %d, start=%d, size=%d\n", part, dp->p_offset, dp->p_size); + + if ((dp->p_offset + dp->p_size) > + (slice->limits.slicesize / slice->limits.blksize)) { + printf("dkl: slice %d too big ", part); + printf("(%x > %x:%x )\n", + (dp->p_offset + dp->p_size), + (slice->limits.slicesize / slice->limits.blksize) ); + continue; + } + /* check for overlaps with existing slices */ + for (i = 0; i < MAXPARTITIONS; i++) { + /* + * Don't bother if that slice was not made. + * This handles the (i == part) case. + */ + if (pd->subdevs[i].slice == NULL) + continue; + if ((dp0[i].p_offset < (dp->p_offset + dp->p_size)) + && ((dp0[i].p_offset + dp0[i].p_size) > dp->p_offset)) { + printf("dkl: slice %d overlaps slice %d\n", + part, i); + goto breakout; + } + } + /*- + * the slice seems to make sense. Use it. + */ + pd->subdevs[part].part = part; + pd->subdevs[part].pd = pd; + pd->subdevs[part].offset = dp->p_offset; + pd->subdevs[part].limit.blksize + = slice->limits.blksize; + pd->subdevs[part].limit.slicesize + = (slice->limits.blksize * (u_int64_t)dp->p_size); + + sprintf(name, "%s%c", slice->name, (char )('a' + part)); + sl_make_slice(&slicetype, + &pd->subdevs[part], + &pd->subdevs[part].limit, + &pd->subdevs[part].slice, + NULL, + name); + pd->subdevs[part].slice->probeinfo.typespecific = &dp->p_fstype; + switch (dp->p_fstype) { + case FS_UNUSED: + /* allow unuseed to be further split */ + pd->subdevs[part].slice->probeinfo.type = NULL; + break; + case FS_V6: + case FS_V7: + case FS_SYSV: + case FS_V71K: + case FS_V8: + case FS_MSDOS: + case FS_BSDLFS: + case FS_OTHER: + case FS_HPFS: + case FS_ISO9660: + case FS_BOOT : +#if 0 + printf("%s: type %d. Leaving\n", + pd->subdevs[part].slice->name, + (u_int)dp->p_fstype); +#endif + case FS_SWAP: + case FS_BSDFFS: + pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; + break; + default: + pd->subdevs[part].slice->probeinfo.type = NULL; + } + /* + * Dont allow further breakup of slices that + * cover our disklabel + */ + if (dp->p_offset < 16) { +#if 0 + printf("%s: covers disklabel. Leaving\n", + pd->subdevs[part].slice->name); +#endif + pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; + } + if ((tp = slice_probeall(pd->subdevs[part].slice)) != NULL) { + (*tp->constructor)(pd->subdevs[part].slice); + } + } + return (error); +} + +/*- + * look at a slice that USED to be ours. + * decide if any sub-slices need to be revoked. + * If not then at least ask them to verify themselves. + */ +static int +dkl_verify(sl_p slice) +{ + register struct private_data *pd; + struct disklabel label; + struct partition *dp, *dp2; + struct disklabel *dl; + int part; + int error; + /* register struct slice *slice; */ + + RR; + pd = slice->private_up; + /* slice = pd->slice_down; */ + bzero(&label, sizeof(label)); + /* + * Try load a valid disklabel. This is 90% of what we need to check. + */ + if (((error = dkl_extract_table(slice, &label)) != 0) + || (slice->limits.blksize != 512)) { + /*- + * Oh oh, we need to invalidate all the subslices. + * and relinquish this slice. + */ + return (dkl_revoke(pd)); + } + dl = &(pd->disklabel); + dp = dl->d_partitions; + dp2 = label.d_partitions; + for (part = 0; part < MAXPARTITIONS; part++, dp++, dp2++) { + if (pd->subdevs[part].slice) { + if ((dp2->p_offset != dp->p_offset) + || (dp2->p_size != dp->p_size)) { + sl_rmslice(pd->subdevs[part].slice); + pd->subdevs[part].slice = NULL; + } else if (pd->subdevs[part].slice->handler_up) { + (*pd->subdevs[part].slice->handler_up->verify) + (pd->subdevs[part].slice); + } + } + } + /*- having got rid of changing slices, replace + * the old table with the new one, and + * handle any new slices by calling the constructor. + */ + bcopy(&label, dl, sizeof(label)); + error = dkl_constructor(slice); +done: + return (error); +} + +/*- + * Invalidate all subslices, and free resources for this handler instance. + */ +static int +dkl_revoke(void *private) +{ + register struct private_data *pd; + register struct slice *slice; + int part; + + RR; + pd = private; + slice = pd->slice_down; + for (part = 0; part < MAXPARTITIONS; part++) { + if (pd->subdevs[part].slice) { + sl_rmslice(pd->subdevs[part].slice); + } + } + /*- + * remove ourself as a handler + */ + slice->handler_up = NULL; + slice->private_up = NULL; + slicetype.refs--; + free(pd, M_DEVBUF); + sl_unref(slice); + return (0); +} + +/*- + * shift the appropriate IO by the offset for that slice. + */ +static void +dkl_IOreq(void *private, struct buf * bp) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + bp->b_pblkno += sdp->offset; /* add the offset for that slice */ + sliceio(slice, bp, SLW_ABOVE); +} + +/* + * shift the appropriate IO by the offset for that slice. + */ +static void +mbr_IOreq(void *private, struct buf * bp) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + bp->b_pblkno += sdp->offset; /* add the offset for that slice */ + sliceio(slice, bp, SLW_ABOVE); +} + +static int +dkl_open(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + int error; + u_int8_t newrflags = 0; + u_int8_t newwflags = 0; + int newoflags; + int part; + u_int8_t partbit; + +RR; + sdp = private; + part = sdp->part; + partbit = (1 << part); + pd = sdp->pd; + slice = pd->slice_down; + + /* + * Calculate the change to to over-all picture here. + * Notice that this might result in LESS open bits + * if that was what was passed from above. + * (Prelude to 'mode-change' instead of open/close.) + */ + /* work out what our stored flags will be if this succeeds */ + newwflags &= ~ (partbit); + newrflags &= ~ (partbit); + newwflags |= (flags & FWRITE) ? (partbit) : 0; + newrflags |= (flags & FREAD) ? (partbit) : 0; + + /* work out what we want to pass down this time */ + newoflags = newwflags ? FWRITE : 0; + newoflags |= newrflags ? FREAD : 0; + + /* + * If the agregate flags we used last time are the same as + * the agregate flags we would use this time, then don't + * bother re-doing the command. + */ + if (newoflags != pd->savedoflags) { + if (error = sliceopen(slice, newoflags, mode, p, SLW_ABOVE)) { + return (error); + } + } + + /* + * Now that we know it succeeded, commit, by replacing the old + * flags with the new ones. + */ + pd->rflags = newrflags; + pd->wflags = newwflags; + pd->savedoflags = newoflags; + return (0); +} + +static void +dkl_close(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + u_int8_t newrflags = 0; + u_int8_t newwflags = 0; + int newoflags; + int part; + u_int8_t partbit; + +RR; + sdp = private; + part = sdp->part; + partbit = (1 << part); + pd = sdp->pd; + slice = pd->slice_down; + + if ((pd->rflags == 0) && (pd->wflags == 0)) + return; + + /* work out what our stored flags will be if this succeeds */ + newwflags &= ~ (partbit); + newrflags &= ~ (partbit); + newwflags |= (flags & FWRITE) ? (partbit) : 0; + newrflags |= (flags & FREAD) ? (partbit) : 0; + + /* work out what we want to pass down this time */ + newoflags = newwflags ? FWRITE : 0; + newoflags |= newrflags ? FREAD : 0; + + /* + * If this was the last open slice above, then release our own open + */ + if ((pd->rflags == 0) && (pd->wflags == 0)) { + sliceclose(slice, newoflags, mode, p, SLW_ABOVE); + } + pd->rflags = newrflags; + pd->wflags = newwflags; + pd->savedoflags = newoflags; + return ; +} + +static int +dkl_ioctl(void *private, int cmd, caddr_t addr, int flag, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + struct disklabel *lp; + int error; + + RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + lp = &pd->disklabel; + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = *lp; + return (0); + + case DIOCGPART: + if (lp == NULL) + return (EINVAL); + ((struct partinfo *)addr)->disklab = lp; + ((struct partinfo *)addr)->part = lp->d_partitions + sdp->part; + return (0); + +/* These don't really make sense. keep the headers for a reminder */ + case DIOCSDINFO: + case DIOCSYNCSLICEINFO: + case DIOCWDINFO: + case DIOCWLABEL: + return (ENOIOCTL); + } + + return ((*slice->handler_down->ioctl) (slice->private_down, + cmd, addr, flag, p)); +} + +static int +dkl_upconfig(struct slice *slice, int cmd, caddr_t addr, int flag, struct proc * p) +{ + RR; + switch (cmd) { + case SLCIOCRESET: + return (0); + +/* These don't really make sense. keep the headers for a reminder */ + default: + return (ENOIOCTL); + } + return (0); +} + +static struct disklabel static_label; +/* + * This is a hack routine called from the slice generic code to produce a dummy + * disklabel when given a slice descriptor. It's in here because this code + * knows about disklabels. + */ +int +dkl_dummy_ioctl(struct slice *slice, int cmd, caddr_t addr, + int flag, struct proc * p) +{ + struct disklabel *lp = &static_label; + + switch (cmd) { + case DIOCGDINFO: + case DIOCGPART: + bzero(lp, sizeof(static_label)); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_secsize = slice->limits.blksize; + lp->d_nsectors = 1; + lp->d_ntracks = 1; + lp->d_secpercyl = 1; + lp->d_ncylinders = + lp->d_secperunit = slice->limits.slicesize + / slice->limits.blksize; + lp->d_npartitions = RAW_PART + 1; + lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; + lp->d_partitions[RAW_PART].p_offset = 0; + break; + default: + return (ENOIOCTL); + } + lp->d_checksum = dkcksum(lp); + + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = *lp; + break; + case DIOCGPART: + /* XXX hack alert. + * This is a hack as this information is consumed immediatly + * otherwise the use of a static buffer would be dangerous. + */ + ((struct partinfo *)addr)->disklab = lp; + ((struct partinfo *)addr)->part = lp->d_partitions + RAW_PART; + } + + return (0); + +} + +#if 0 /* use the existing one for now */ +/*- + * Compute checksum for disk label. + */ +u_int +dkcksum(lp) + register struct disklabel *lp; +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *) lp; + end = (u_short *) & lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} +#endif /* 0 */ + diff --git a/sys/dev/slice/mbr.c b/sys/dev/slice/mbr.c new file mode 100644 index 0000000000000..7babfc9bb9493 --- /dev/null +++ b/sys/dev/slice/mbr.c @@ -0,0 +1,837 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. + * + * $Id: $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct private_data { + u_int32_t flags; + struct slice *slice_down; + int savedoflags; + struct dos_partition dos_table[NDOSPART]; + struct subdev { + int part; + struct slice *slice; + struct slicelimits limit; + struct private_data *pd; + u_int32_t offset; /* Fdisk only has 32 bits */ + } subdevs[NDOSPART]; +}; +/* + * Bits in the mbr private data flag word + */ +#define MBRF_OPEN_RBIT 0x01 +#define MBRF_S1_OPEN_RD 0x01 +#define MBRF_S2_OPEN_RD 0x02 +#define MBRF_S3_OPEN_RD 0x04 +#define MBRF_S4_OPEN_RD 0x08 +#define MBRF_MSK_RD 0x0F +#define MBRF_OPEN_WBIT 0x10 +#define MBRF_S1_OPEN_WR 0x10 +#define MBRF_S2_OPEN_WR 0x20 +#define MBRF_S3_OPEN_WR 0x40 +#define MBRF_S4_OPEN_WR 0x80 +#define MBRF_MSK_WR 0xF0 +#define MBRF_MSK_OPEN 0xFF + +static struct dos_partition historical_bogus_partition_table[NDOSPART] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0x80, 0, 1, 0, DOSPTYP_386BSD, 255, 255, 255, 0, 50000,}, +}; +#define DOSPTYP_ONTRACK 84 + +static sl_h_constructor_t mbr_constructor; /* constructor (from device) */ +static sl_h_IO_req_t mbr_IOreq; /* IO req downward (to device) */ +static sl_h_ioctl_t mbr_ioctl; /* ioctl req downward (to device) */ +static sl_h_open_t mbr_open; /* downwards travelling open */ +static sl_h_close_t mbr_close; /* downwards travelling close */ +static sl_h_claim_t mbr_claim; /* upwards travelling claim */ +static sl_h_revoke_t mbr_revoke;/* upwards travelling revokation */ +static sl_h_verify_t mbr_verify;/* things changed, are we stil valid? */ +static sl_h_upconfig_t mbr_upconfig;/* config request from below */ + +static struct slice_handler slicetype = { + "MBR", + 0, + NULL, + 0, + &mbr_constructor, /* constructor */ + &mbr_IOreq, + &mbr_ioctl, + &mbr_open, + &mbr_close, + &mbr_revoke, /* revoke */ + &mbr_claim, /* claim */ + &mbr_verify, /* verify */ + &mbr_upconfig /* config from below */ +}; + +static void +sd_drvinit(void *unused) +{ + sl_newtype(&slicetype); +} + +SYSINIT(sddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sd_drvinit, NULL); + +/* + * Given a slice, extract out our table of information + */ +static int +mbr_find_table(sl_p slice, struct buf **bpp, int *blknum) +{ + int ontrack_offset = 0; + int error; + u_int8_t *cp; + struct dos_partition *dp0, *dp; + int part; + int redone = 0; + struct buf *bp; + +RR; + *bpp = NULL; +reread: + if (error = slice_readblock(slice, ontrack_offset, &bp)) + return (error); + cp = bp->b_data; + if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) { + error = EINVAL; + goto done; + } + dp0 = (struct dos_partition *) (cp + DOSPARTOFF); + + /* + * Check for "Ontrack Diskmanager". Note that if the geometry is + * still needed then we probably won't be able to read a DiskManager + * MBR because we will fail to read sector 63. The very act of + * finding a Disk Manager might however have given us the info we + * need if the disk manager set's its partition up correctly. + */ + if (!redone) { + for (part = 0, dp = dp0; + part < NDOSPART; part++, dp++) { + if (dp->dp_typ == DOSPTYP_ONTRACK) { +#ifdef MAYBE + /* + * It's not known if this should always 63 or + * if this is just the start of the 2nd + * track. + */ + ontrack_offset = dp->dp_start; +#else + ontrack_offset = 63; +#endif + if (bootverbose) + printf("Found \"Ontrack Disk Manager\"\n"); + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + redone++; + goto reread; + } + } + } +done: + if (blknum) + *blknum = ontrack_offset; + *bpp = bp; + return (error); +} + +/* + * Given a slice, extract out our table of information + */ +static int +mbr_extract_table(sl_p slice, struct dos_partition *table) +{ + int error; + struct buf *bp; + +RR; + /* start off with a known result */ + bzero(table, sizeof(*table) * NDOSPART); + error = mbr_find_table(slice, &bp, NULL); + if (!error) + bcopy((bp->b_data + DOSPARTOFF), table, + sizeof(*table) * NDOSPART); +done: + if (bp) { + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + } + return (error); +} + +/* + * read the block and replace the mbr table with that given. + * If there isn't one, clear the rest of the block. + */ +static int +mbr_insert_table(sl_p slice, struct dos_partition *table) +{ + int blknum = 0; + int error; + struct buf *bp; + +RR; + error = mbr_find_table(slice, &bp, &blknum); + if ( error == EINVAL) { + /* + * The block was read, but there was no table there. + * just clear out the cruft for now. + */ + bzero(bp->b_data, slice->limits.blksize); + } else if (error == 0) { + bcopy( table, (bp->b_data + DOSPARTOFF), + sizeof(*table) * NDOSPART); + bp->b_data[0x1FE] = 0x55; + bp->b_data[0x1FF] = 0xAA; + /* XXX Somehow we should get boot code in there too. */ + /* for now leave it to the tool */ + error = slice_writeblock(slice, blknum, bp); + } +done: + if (bp) { + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + } + return (error); +} + +/* + * look at a slice and figure out if we should be interested in it. (Is it + * ours?) + */ +static int +mbr_claim(struct slice * slice, struct slice * lower, void *ID) +{ + struct dos_partition table[NDOSPART]; + struct dos_partition *dp, *dp0; + int part; + int error; + int max_ncyls; + int max_nsectors; + int max_ntracks; + u_int32_t secpercyl; + int numactive = 0; +RR; + + /* + * Don't even BOTHER if it's not 512 byte sectors + */ + if (slice->limits.blksize != 512) + return (EINVAL); + /* + * Try load a valid MBR table. This is 90% of what we need to check. + */ + if ((error = mbr_extract_table(slice, table)) != 0) { + return (error); + } + dp0 = table; + /* + * The first block of the dos code is marked like a valid MBR. + * Try to distinguish this case byt doing a sanity check on the table. + * Check: + * Flag byte can only be 0 or 0x80. + * At most one active partition. + * -Other tests to be added here- + */ + for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) { + if (dp->dp_flag & 0x7f) { + printf ("rejected.. bad flag "); + return(EINVAL); /* must be either 0 or 0x80 */ + } + if ((dp->dp_typ) && (dp->dp_size) && (dp->dp_start == 0)) { + printf("rejected.. Slice includes MBR "); + return (EINVAL); + } + if (dp->dp_flag == 0x80) + numactive++; + } + if (numactive > 1) { + printf ("rejected.. multiple active "); + return (EINVAL); + } + /* + * If it's the MBR that comes with the disklabel then we should just + * give up and let the disklabel handler take control of this slice. + */ + if (bcmp(dp0, historical_bogus_partition_table, + sizeof historical_bogus_partition_table) == 0) { + printf("rejecting disklabel table "); + return (EINVAL); + } + /* + * well, it looks like one of ours. + */ + return (0); +} + +/* + * This routine tries to guess the geometry for + * old disk drivers that need the MBR code to set it. Bits taken from + * diskslice_machdep.c which itself evolved from earlier code. + * This is not part of the SLICE code per-se, but just a convenient place to + * put this HACK because everything is in scope. Only called by the IDE driver. + */ +int +mbr_geom_hack(struct slice * slice, struct ide_geom *geom) +{ + struct dos_partition table[NDOSPART]; + struct dos_partition *dp, *dp0; + int part; + int error; + int max_ncyls; + int max_nsectors; + int max_ntracks; + u_int32_t secpercyl; +RR; + + /* + * Don't even BOTHER if it's not claimable by us. + */ + if ((error = mbr_claim(slice,NULL,0))) + return (error); + /* + * Load the mbr. + */ + if ((error = mbr_extract_table(slice, table)) != 0) { + return (error); + } + dp0 = table; + /* + * Guess the geometry. For some old drives (ESDI, st506) the + * driver below us may not yet know the geometry, but needs + * to before it can access blocks out of the first track. + * This hack is to use information in the MBR to "deduce" + * this information and pass it back. + */ + max_ncyls = 0; + max_nsectors = 0; + max_ntracks = 0; + for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) { + int ncyls; + int nsectors; + int ntracks; + + if (dp->dp_size == 0) + continue; + ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect) + 1; + if (max_ncyls < ncyls) + max_ncyls = ncyls; + nsectors = DPSECT(dp->dp_esect); + if (max_nsectors < nsectors) + max_nsectors = nsectors; + ntracks = dp->dp_ehd + 1; + if (max_ntracks < ntracks) + max_ntracks = ntracks; + } + if ((max_ncyls == 0) + && (max_nsectors == 0) + && (max_ntracks == 0)) { + /* we've gained nought, so just return */ + return (EINVAL); + } + secpercyl = (u_long) max_nsectors *max_ntracks; + printf("s=%d, h=%d, c=%d\n", max_nsectors, max_ntracks, max_ncyls); + /* + * Check that we have guessed the geometry right by checking + * the partition entries. + */ + error = 0; + for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) { + int cyl; + int sector; + int track; + int secpercyl; + + if (dp->dp_size == 0) + continue; + cyl = DPCYL(dp->dp_scyl, dp->dp_ssect); + track = dp->dp_shd; + sector = DPSECT(dp->dp_ssect) - 1; + secpercyl = max_nsectors * max_ntracks; + /* + * If the geometry doesn't work for any partition + * start then don't accept it. + */ + if (((((dp->dp_start / secpercyl) % 1024) != cyl) + && (cyl != 1023)) + || (((dp->dp_start % secpercyl) + / max_nsectors) != track) + || (((dp->dp_start % secpercyl) + % max_nsectors) != sector)) { + printf("Can't get disk geometry from MBR\n"); + return (EINVAL); + } + if ((dp->dp_start / secpercyl) > 1023) { + printf("part %d above BIOS reach\n", part); + } + } + + /* + * Set our newely hypothesised numbers into the geometry + * slots in the supplied SLICE. + */ + geom->secpertrack = max_nsectors; + geom->trackpercyl = max_ntracks; + geom->cyls = max_ncyls; + return (0); +} + +/* + * look at a slice we know to be ours and decide what the #$%^ to do with it. + * We presume the driver already did the geometry hack if needed. + */ +static int +mbr_constructor(sl_p slice) +{ + int i; + u_int64_t disksize = slice->limits.slicesize; + struct private_data *pd; + struct dos_partition *dp, *dp0; + int redone = 0; + int ontrack_offset = 0; + char name[64]; + sh_p tp; + + int part; + int error = 0; + +RR; + /* + * If we are being called to re-load a slice, + * then don't reallocate resources. + */ + if ( (pd = slice->private_up) == NULL) { + if (slice->name == NULL) { + printf("name is NULL\n"); + return (EINVAL); + } + if (strlen(slice->name) > 58) { + printf("slice: name %s too long\n", slice->name); + return (ENAMETOOLONG); + } + pd = malloc(sizeof(*pd), M_DEVBUF, M_NOWAIT); + if (pd == NULL) { + printf("fdisk: failed malloc\n"); + return (ENOMEM); + } + bzero(pd, sizeof(*pd)); + pd->slice_down = slice; + if ((error = mbr_extract_table(slice, pd->dos_table)) != 0) { + /* + * If it's just that there is no table there, + * Then we fake an empty one up and write it. if + * this were not ok, then we would have not been + * called. (as probe will have failed). If it's + * a physical error, then that's reason to fail. + */ + if (error != EINVAL) { + free(pd, M_DEVBUF); + return (error); + } + bzero(pd->dos_table, sizeof(pd->dos_table)); + if ((error = mbr_insert_table(slice, pd->dos_table))) { + free(pd, M_DEVBUF); + return (error); + } + + } + slice->refs++; + slice->handler_up = &slicetype; + slice->private_up = pd; + slicetype.refs++; + } + + dp0 = pd->dos_table; + + /* + * Handle each of the partitions. + * We should check that each makes sence and is legal. + * 1/ it should not already have a slice. + * 2/ should not be 0 length. + * 3/ should not go past end of our slice. + * 4/ should not include sector 0. + * 5/ should not overlap other slices. + */ + dp = dp0; + for (part = 0; part < NDOSPART; part++, dp++) { + int i; + if (pd->subdevs[part].slice != NULL) +breakout: continue; + if (dp->dp_size == 0) + continue; + if (dp->dp_start < 1) + continue; + if ((dp->dp_start + dp->dp_size) > + (slice->limits.slicesize/slice->limits.blksize)) + continue; + /* check for overlaps with existing slices */ + for (i = 0; i < NDOSPART; i++) { + /* skip empty slots (including this one) */ + if(pd->subdevs[i].slice == NULL ) + continue; + if ((dp0[i].dp_start < (dp->dp_start + dp->dp_size)) + && ((dp0[i].dp_start + dp0[i].dp_size) > dp->dp_start)) + { + printf("mbr: new slice %d overlaps slice %d\n", + part, i); + goto breakout; + } + } + /* + * the slice seems to make sense. Use it. + */ + pd->subdevs[part].part = part; + pd->subdevs[part].pd = pd; + pd->subdevs[part].offset = dp->dp_start; + pd->subdevs[part].limit.blksize + = slice->limits.blksize; + pd->subdevs[part].limit.slicesize + = (slice->limits.blksize * (u_int64_t)dp->dp_size); + + sprintf(name, "%ss%d", slice->name, part + 1); + sl_make_slice(&slicetype, + &pd->subdevs[part], + &pd->subdevs[part].limit, + &pd->subdevs[part].slice, + NULL, + name); + pd->subdevs[part].slice->probeinfo.typespecific = &dp->dp_typ; + switch (dp->dp_typ) { /* list stolen from fdisk */ + case 0x00: /* "unused" */ + case 0x01: /* "Primary DOS with 12 bit FAT" */ + case 0x02: /* "XENIX / filesystem" */ + case 0x03: /* "XENIX /usr filesystem" */ + case 0x04: /* "Primary DOS with 16 bit FAT" */ + case 0x05: /* "Extended DOS" */ + case 0x06: /* "Primary 'big' DOS (> 32MB)" */ + case 0x07: /* "OS/2 HPFS, QNX or Advanced UNIX" */ + case 0x08: /* "AIX filesystem" */ + case 0x09: /* "AIX boot partition or Coherent" */ + case 0x0A: /* "OS/2 Boot Manager or OPUS" */ + case 0x10: /* "OPUS" */ + case 0x40: /* "VENIX 286" */ + case 0x50: /* "DM" */ + case 0x51: /* "DM" */ + case 0x52: /* "CP/M or Microport SysV/AT" */ + case 0x56: /* "GB" */ + case 0x61: /* "Speed" */ + case 0x63: /* "ISC UNIX, System V/386, GNU HURD or Mach" */ + case 0x64: /* "Novell Netware 2.xx" */ + case 0x65: /* "Novell Netware 3.xx" */ + case 0x75: /* "PCIX" */ + case 0x80: /* "Minix 1.1 ... 1.4a" */ + case 0x81: /* "Minix 1.4b ... 1.5.10" */ + case 0x82: /* "Linux swap" */ + case 0x83: /* "Linux filesystem" */ + case 0x93: /* "Amoeba filesystem" */ + case 0x94: /* "Amoeba bad block table" */ + case 0xA6: /* "OpenBSD" */ + case 0xA7: /* "NEXTSTEP" */ + case 0xB7: /* "BSDI BSD/386 filesystem" */ + case 0xB8: /* "BSDI BSD/386 swap" */ + case 0xDB: /* "Concurrent CPM or C.DOS or CTOS" */ + case 0xE1: /* "Speed" */ + case 0xE3: /* "Speed" */ + case 0xE4: /* "Speed" */ + case 0xF1: /* "Speed" */ + case 0xF2: /* "DOS 3.3+ Secondary" */ + case 0xF4: /* "Speed" */ + case 0xFF: /* "BBT (Bad Blocks Table)" */ + printf("%s: type %d. Leaving\n", + pd->subdevs[part].slice->name, + (u_int)dp->dp_typ); + pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; + break; + case DOSPTYP_386BSD: /* 0xA5 "FreeBSD/NetBSD/386BSD" */ + pd->subdevs[part].slice->probeinfo.type = "disklabel"; + break; + default: + pd->subdevs[part].slice->probeinfo.type = NULL; + } + if ((tp = slice_probeall(pd->subdevs[part].slice)) != NULL) { + (*tp->constructor)(pd->subdevs[part].slice); + } + } + return (error); +} + +/* + * look at a slice that USED to be ours. + * decide if any sub-slices need to be revoked. + * If not then at least ask them to verify themselves. + * Note, arg 'slice' is not strictly needed + */ +static int +mbr_verify(sl_p slice) +{ + register struct private_data *pd; + struct dos_partition table[NDOSPART]; + struct dos_partition *dp, *dp0; + int part; + int error; + /* register struct slice *slice; */ + +RR; + pd = slice->private_up; + /* slice = pd->slice_down; */ + bzero(table, sizeof(table)); + /* + * Try load a valid MBR table. This is 90% of what we need to check. + */ + if ((slice->limits.blksize != 512) + || ((error = mbr_extract_table(slice, table)) != 0)) { + /* + * Oh oh, we need to invalidate all the subslices. + * and relinquish this slice. + */ + return(mbr_revoke(pd)); + } + /* + * For each existing subslice, check that the basic size + * and position has not changed. Also check the TYPE. + * It is possible we should allow a slice to grow. + */ + dp = dp0 = pd->dos_table; + for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) { + if (pd->subdevs[part].slice) { + if ((table[part].dp_start != dp->dp_start) + || (table[part].dp_size != dp->dp_size) + || (table[part].dp_typ != dp->dp_typ) ) { + sl_rmslice(pd->subdevs[part].slice); + pd->subdevs[part].slice = NULL; + } else if ( pd->subdevs[part].slice->handler_up) { + (*pd->subdevs[part].slice->handler_up->verify) + (pd->subdevs[part].slice); + } + } + } + /* + * Having got rid of changing slices, replace + * the old table with the new one, and + * Handle any new slices by calling the constructor. + * This way, if we are in 'promiscuous' mode, + * (e.g. repartitionning a disk we are running on from + * Single user mode, the unchanged slices can remain open and active + * through the process. If you change an open slice, + * the vnodes will be changed to deadfs so a crash is probably + * nearby. XXX too late. It's written to disk.. (we COULD reverse it, + * but....) + */ + bcopy( table, dp0, sizeof(table)); + error = mbr_constructor(slice); + return (error); +} + +/* + * Invalidate all subslices, and free resources for this handler instance. + */ +static int +mbr_revoke(void *private) +{ + register struct private_data *pd; + register struct slice *slice; + int part; + +RR; + pd = private; + slice = pd->slice_down; + for (part = 0; part < NDOSPART; part++) { + if (pd->subdevs[part].slice) { + sl_rmslice(pd->subdevs[part].slice); + } + } + /* + * remove ourself as a handler + */ + slice->handler_up = NULL; + slice->private_up = NULL; + slicetype.refs--; + free(pd,M_DEVBUF); + sl_unref(slice); + return (0); +} + +/* + * shift the appropriate IO by the offset for that slice. + */ +static void +mbr_IOreq(void *private, struct buf * bp) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + bp->b_pblkno += sdp->offset; /* add the offset for that slice */ + sliceio(slice, bp, SLW_ABOVE); +} + +static int +mbr_open(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + int part; + int error; + int newflags = 0; + int oldoflags = 0; + int newoflags = 0; + +RR; + sdp = private; + part = sdp->part; + pd = sdp->pd; + slice = pd->slice_down; + + /* + * Calculate the change to to over-all picture here. + * Notice that this might result in LESS open bits + * if that was what was passed from above. + * (Prelude to 'mode-change' instead of open/close.) + */ + /* work out what our stored flags will be if this succeeds */ + newflags = pd->flags & ~((MBRF_OPEN_WBIT|MBRF_OPEN_RBIT) << part); + newflags |= (flags & FWRITE) ? (MBRF_OPEN_WBIT << part) : 0; + newflags |= (flags & FREAD) ? (MBRF_OPEN_RBIT << part) : 0; + + /* work out what we want to pass down this time */ + newoflags = (newflags & MBRF_MSK_WR) ? FWRITE : 0; + newoflags |= (newflags & MBRF_MSK_RD) ? FREAD : 0; + + /* + * If the agregate flags we used last time are the same as + * the agregate flags we would use this time, then don't + * bother re-doing the command. + */ + if (newoflags != pd->savedoflags) { + if (error = sliceopen(slice, newoflags, mode, p, SLW_ABOVE)) { + return (error); + } + } + + /* + * Now that we know it succeeded, commit, by replacing the old + * flags with the new ones. + */ + pd->flags &= ~MBRF_MSK_OPEN; + pd->flags |= newflags; + pd->savedoflags = newoflags; + return (0); +} + +static void +mbr_close(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + int newflags; + int newoflags; + int part; + +RR; + sdp = private; + part = sdp->part; + pd = sdp->pd; + slice = pd->slice_down; + + if ((pd->flags & MBRF_MSK_OPEN) == 0) + return; + + /* work out what our stored flags will be if this succeeds */ + newflags = pd->flags & ~((MBRF_OPEN_WBIT|MBRF_OPEN_RBIT) << part); + newflags |= (flags & FWRITE) ? (MBRF_OPEN_WBIT << part) : 0; + newflags |= (flags & FREAD) ? (MBRF_OPEN_RBIT << part) : 0; + + /* work out what we want to pass down this time */ + newoflags = (newflags & MBRF_MSK_WR) ? FWRITE : 0; + newoflags |= (newflags & MBRF_MSK_RD) ? FREAD : 0; + + /* + * If this was the last open slice above, then release our own open + */ + pd->flags &= ~((MBRF_OPEN_RBIT|MBRF_OPEN_WBIT) << part); + if (pd->flags & MBRF_MSK_OPEN) { + sliceclose(slice, newoflags, mode, p, SLW_ABOVE); + } + pd->flags &= ~MBRF_MSK_OPEN; + pd->flags |= newflags; + pd->savedoflags = newoflags; + return ; +} + +static int +mbr_ioctl(void *private, int cmd, caddr_t addr, int flag, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + int error; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + + return ((*slice->handler_down->ioctl) (slice->private_down, + cmd, addr, flag, p)); +} + +static int +mbr_upconfig(struct slice *slice, int cmd, caddr_t addr, + int flag, struct proc * p) +{ + RR; + switch (cmd) { + case SLCIOCRESET: + return (0); + +/* These don't really make sense. keep the headers for a reminder */ + default: + return (ENOIOCTL); + } + return (0); +} + diff --git a/sys/dev/slice/slice.4 b/sys/dev/slice/slice.4 new file mode 100644 index 0000000000000..2fffdb3ca9971 --- /dev/null +++ b/sys/dev/slice/slice.4 @@ -0,0 +1,152 @@ +yes I know this is not in mandoc format.. + +The slices are stackable.. +With alternating layers of handler(driver)/slice/handler/slice/handler/slice +The "Slice" is implemented as a common structure shared between three +pieces of code. Each slice in the stack can be thought of in OO terms as +an instance of the 'slice' object. Methods include all the 'device' node +methods exported via the cdevsw[], bdevsw[] and devfs interfaces. Thus +whenever a handler exports a slice object, a unique node is made available +to the users via the device system, to access that slice, as if it were a +disk in it's own right. Since the interface is implemented by the same +code no matter where in the stack it occurs, all partitions and devices +which are exported by the slice code, exhibit almost identical behavior. +Theoretically, it should be possible to treat a partition of a device, as +if it were a separate device, as it should exhibit the same behavior as +the device itself (except for size). + +The diagram below exhibits the form of one layer of the stack. Each handler +can decide how many slices to export on the upper side, and +how many slices to connect to on the lower side. If A slice can not be +further subdivided, there may not be an upper handler. + + [upper handler] (optional) + ^ + | + | + v |------ raw (char) device +[common slice information]<---------->[slice device] + ^ |------ block device + | + | + v + [lower handler] (may be a device driver) + +Each of these 3 items share some knowledge of the internal structure and +contents of the slice structure. They also know each other's published +interfaces. This may change as the design settles down and it becomes more +obvious which parts can be hidden. + +The slices are created bottom up. +When the driver decides that there is media that should be made available, +it creates a 'slice' object to represent it. This slice object comes with a +set of methods for exporting and implementing a device. The action of creating +a slice therefor creates the interface through which a user can access that +device. A driver might even export such slice before the media is present, +in order to make a device node available to the user. (e.g. the floppy +driver would make /dev/rfd0 available even if there was no media present, +simply because it has no way of detecting that the media has been added. +Attempts to open or access that node would result in the usual EIO +errors. + +i.e. the device probes, and creates a static 'slice' that is associated with +the device.. The static slice can't be removed unless the driver does so, +thought if the media is changed the size of the slice may change. + + +Some time after the media has been detected, or deduced to be present, +the driver would ask the system to try interpret the contents of the +media. It does this by passing the slice to the generic code. The generic +code will ask all the possible handlers to see if that slice (or virtual +disk) has the structure it requires. Sometimes the driver (or lower handler, +for that is what the driver is from the point of view of the slice) Will 'seed' +the slice with a 'hint' which will make the generic code narrow it's requests +to a particular handler, or group of handlers. + +When a slice object attaches an handler to one of it's slices, that handler +might elect to further export more slices, each representing some different +view of the slice. This could result on a multi layer stack of slices and +handlers, depending on the contents of the device. Whether a handler will +elect to further divide a slice given to it is solely up to that handler. No +other code has jurisdiction over that decision. + +Because a device may need to know that it is being used, it is important +that open() events be cascaded down towards the device. Handlers that +export multiple slices upwards must pass down the union of all the open +states of those slices. + +A lower level handler can decide that the slices it has exported are no +longer valid. This can occur for several reasons. For example a write to a +low level slice might change the structures defining a higher level slice, +or a driver (the lowest level handler) might notice that the media on which +a slice is based, has been removed, or in some other way become +unavailable. The handler must be able to invalidate the slice(es) affected, +and know that the system will cascade that invalidation upwards as needed. +A higher handler may decide to no pass on the invalidation if it calculates +that higher level services can still be provided without the particular +lower slice being present, (e.g. a RAID handler). + +Access to various layers is controlled by a strict protocol to avoid +accidental system damage. There is a single sysctl variable that can +disable the enforcement of this protocol, however it should ony be used +in special (e.g. system instalation) circumstances. The basic protocol +is that a higher level device cannot be opened while one of it's lower +layers is open for writing. Similarly, a lower layer cannot be openned for +writing while an upper layer is open at all. Two devices at different +layers can be openned at the same time if there is no direct +decendancy between the two. In an analogue, we might say that 'cousins' +can be openned independantly, but anscestors and descendents cannot. +The sysctl variable kern.slicexclusive has 3 values. +0 disables the checks metioned above. 1 enables them, and 2 +enables eve more draconian rules in which even READ opens are disabled. + +Further rules govern the interaction of the block and raw versions of a +slice. For example, if a block device is open for read/write, it's raw +device can not be written to (in mode 1) + +[think about upwards permission inherritance for subslices] + + +[setting up new configurations] +A disk exports simply a raw slice. It has no preference as to what goes on it.. +(preferences are stored in the slice's probehints structure.) +To slice it into fdisk type: +1/ set the hints to "mbr", through an ioctl on that device. (e.g. rsd0) +2/ Run the "mbr" code's constructor. this will initialise the slice. + The "mbr" code will actually write an mbr to the slice, + with default values. (so it will recognise it in the future). + (this is why the claim is separate from the constructor). The claim() + is nondestructive. The constructor KNOWS it owns the slice. +3/ Send ioctls to the device that are redirected UP to the new handler. + These ioctls allow "type specific templates" and manipulation + of slice tables. Each hander interprets these to suit it's own table + format. This uses the sl_h_upconfig() method, which is basically an + ioctl entrypoint, but which is not automatically cascaded up. + + +rc should have the following added to it to make the system 'safe' +when multi-user mode is entered. + +*** /etc/rc.orig Sat Apr 18 14:34:48 1998 +--- /etc/rc Sat Apr 18 14:38:32 1998 +*************** +*** 82,87 **** +--- 82,96 ---- + exit 1 + fi + ++ ###DEVFS ++ # put the storage slices into safe mode. ++ # 0 == unsafe. One char, one blk and one subslice can all be openned R/W. ++ # 1 = readonly. If a subslice is open, a blk and chr can be openned R/O. ++ # If a slice is open R/W, subslices cannot be openned. ++ # 2 = exclusive. If a subslice is open, a blk or chr cannot be openned. ++ # and visa versa. ++ sysctl -w kern.slicexclusive=1 ++ + # If there is a global system configuration file, suck it in. + if [ -f /etc/rc.conf ]; then + . /etc/rc.conf + + + diff --git a/sys/dev/slice/slice.h b/sys/dev/slice/slice.h new file mode 100644 index 0000000000000..553bdb51a9a86 --- /dev/null +++ b/sys/dev/slice/slice.h @@ -0,0 +1,195 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. + * + * $Id: $ + */ + +typedef struct slice_handler *sh_p; +typedef struct slice *sl_p; + +struct slicelimits { + u_int32_t blksize; /* IN BYTES */ + u_int64_t slicesize; /* IN BYTES */ +}; +typedef struct slicelimits *slmt_p; + +/* + * This struct is only used by the IDE geometry guessing hack in + * the MBR and disklabel code, when talked to by the IDE driver with a VERY + * OLD DISK + */ +struct ide_geom { + u_int32_t secpertrack; /* set to 0 if geom not known */ + u_int16_t trackpercyl; + u_int32_t cyls; +}; + +/* + * The probehints are set by the lower handler, to give direction as to + * what handler is probably required above. If a slice is repartitioned, + * these may change e.g. mbr may set 165 meaning "FreeBSD slice" or 4 "DOS". + * -type: a string for the type that should be used. + * if it's a null string ("") then don't even try find a sub handler. + * defined as NO_SUBPART + * if it's a NULL pointer (NULL) then probe all known types. + * -typespecific: A pointer to SOMETHING that teh lower handler thinks + * may be of interest to the higher hamdlers. The "something" is dependent + * on the type of the lower handler so the upper handler must know of + * this in advance. The type of this should be specified in an + * include file associated with the lower type. This is probably rarely + * needed. + */ +struct probehints { + char *type; /* don't probe, just use this type */ + void *typespecific; /* the lower layer specifies this */ +}; +#define NO_SUBPART "" +/* + * The common slice structure with data, methods and linkages. + */ +struct slice { + /* Per slice data */ + char *name; /* e.g. sd0 wd0s1, wd0s1a ?? */ + struct probehints probeinfo; /* how we should probe this */ + u_int32_t flags; /* this device open, etc. */ + u_int16_t refs; /* active references, free if 1->0 */ + u_int16_t opencount; /* actual count of opens if allowed */ + struct slicelimits limits; /* limits on this slice */ + sh_p handler_up; /* type methods etc. */ + void *private_up; /* data for the slice type */ + sh_p handler_down; /* type methods etc. */ + void *private_down; /* data for the slice type */ + /*------- fields for the slice device driver -------*/ + LIST_ENTRY(slice) hash_list; /* next slice in this bucket */ + u_int32_t minor; /* the key for finding us */ + void *devfs_btoken; + void *devfs_ctoken; +}; + +/* bit definitions for the slice flags */ +#define SLF_CLOSED 0x00000000 /* slice not open */ +#define SLF_OPEN_BLK_RD 0x00000001 /* blk slice readable */ +#define SLF_OPEN_BLK_WR 0x00000002 /* blk slice writeable */ +#define SLF_OPEN_BLK (SLF_OPEN_BLK_RD|SLF_OPEN_BLK_WR) +#define SLF_OPEN_CHR_RD 0x00000004 /* raw slice readable */ +#define SLF_OPEN_CHR_WR 0x00000008 /* raw slice writeable */ +#define SLF_OPEN_CHR (SLF_OPEN_CHR_RD|SLF_OPEN_CHR_WR) +#define SLF_OPEN_DEV_RD (SLF_OPEN_CHR_RD|SLF_OPEN_BLK_RD) +#define SLF_OPEN_DEV_WR (SLF_OPEN_CHR_WR|SLF_OPEN_BLK_WR) +#define SLF_OPEN_DEV (SLF_OPEN_DEV_RD|SLF_OPEN_DEV_WR) +#define SLF_OPEN_UP_RD 0x00000010 /* upper layer is readable */ +#define SLF_OPEN_UP_WR 0x00000020 /* upper layer is writable */ +#define SLF_OPEN_UP 0x00000030 /* upper layer is open */ +#define SLF_OPEN_WR (SLF_OPEN_UP_WR|SLF_OPEN_DEV_WR) +#define SLF_OPEN_RD (SLF_OPEN_UP_RD|SLF_OPEN_DEV_RD) +#define SLF_OPEN_STATE (SLF_OPEN_WR|SLF_OPEN_RD) /* Mask open state */ + +#define SLF_INVALID 0x00000100 /* Everything aborts */ +#define SLF_LOCKED 0x00000200 /* Hold off, It's busy */ +#define SLF_WANTED 0x00000400 /* I held off, wake me up */ + +/* + * prototypes for slice methods + */ +typedef void sl_h_IO_req_t(void *private, struct buf * buf); +typedef int sl_h_ioctl_t(void *private, int cmd, caddr_t data, + int fflag, struct proc * p); +typedef int sl_h_constructor_t(sl_p slice); +typedef int sl_h_open_t(void *private, int flags, int mode, struct proc * p); +typedef void sl_h_close_t(void *private, int flags, int mode, struct proc * p); +typedef int sl_h_revoke_t(void *private); +typedef int sl_h_claim_t(struct slice * slice, struct slice * lower, + void *ID); /* eg ID=165 for BSD */ +typedef int sl_h_verify_t(struct slice *slice); +typedef int sl_h_upconfig_t(struct slice *slice, int cmd, caddr_t data, + int fflag, struct proc *p); + +struct slice_handler { + char *name; + int version;/* the version of this handler */ + struct slice_handler *next; /* next registered type */ + int refs; /* references to this type */ + sl_h_constructor_t *constructor; /* make new instantiation */ + sl_h_IO_req_t *IOreq; /* IO req downward (to device) */ + sl_h_ioctl_t *ioctl; /* ioctl downward (to device) */ + sl_h_open_t *open; /* downwards travelling open */ + sl_h_close_t *close; /* downwards travelling close */ + sl_h_revoke_t *revoke; /* revoke upwards (towards user ) */ + sl_h_claim_t *claim; /* claim a new slice */ + sl_h_verify_t *verify; /* verify that a slice as it was before */ + sl_h_upconfig_t *upconf; /* config requests from slice below */ +}; + +/* + * general routines that handlers need. + */ +int sl_make_slice(sh_p handler_down, void *private_down, + struct slicelimits *limits, + sl_p *slicepp, char *type, char *name); +void sl_rmslice(sl_p slice); +int sl_newtype(sh_p tp); +sh_p sl_findtype(char *type); +sh_p slice_probeall(sl_p slice); +int lockslice(sl_p slice); +int unlockslice(sl_p slice); +int slice_readblock(struct slice *slice, int blkno, struct buf **bpp); +int slice_writeblock(struct slice *slice, int blkno, struct buf *bp); + +/* + * Definitions for "SLICE" utilities. (handler or device acting on a slice). + */ +enum slc_who { SLW_ABOVE, SLW_DEVICE }; /* helps to know who's calling */ + +void sliceio(sl_p slice, struct buf * bp, enum slc_who who); +int sliceopen(sl_p slice, int flags, int mode, + struct proc * p, enum slc_who who); +void sliceclose(sl_p slice, int flags, int mode, + struct proc * p, enum slc_who who); + +void sl_unref(sl_p slice); +void slice_add_device(sl_p slice); +void slice_remove_device(sl_p slice); + +/* + * The geometry guessing HACK functions + */ +int mbr_geom_hack(struct slice * slice, struct ide_geom *geom); +int dkl_geom_hack(struct slice * slice, struct ide_geom *geom); +/* + * The routine to produce a dummy disklabel from a slice. + * Lives in disklabel.c because that's where everyhting is in scope, + * but is used in slice_device.c. XXX hack. + */ +int dkl_dummy_ioctl(struct slice *slice, int cmd, caddr_t addr, + int flag, struct proc * p); + +/* + * debugging + */ +#if 0 +#define RR printf(__FUNCTION__ " called\n") +#else +#define RR /* nothing */ +#endif diff --git a/sys/dev/slice/slice_base.c b/sys/dev/slice/slice_base.c new file mode 100644 index 0000000000000..558720bfd20c5 --- /dev/null +++ b/sys/dev/slice/slice_base.c @@ -0,0 +1,671 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. + * + * $Id: $ + */ + +#include +#include +#include /* SYSINIT stuff */ +#include /* FREAD/FWRITE */ +#include /* cdevsw stuff */ +#include /* malloc region definitions */ +#include /* buffers for IO */ +#include /* linked lists etc. */ +#include /* S_IFCHR, S_IFBLK */ +#include /* the sysctl for shooting self in foot */ +/*#include */ /* DEVFS defintitions */ +#include /* temporary location */ + +#define SLICESPL() splbio() + + +static int slicexclusive = 0; /* default value == "foot shootable" */ + +/* + * Make a new type available. Just link it in, but first make sure there is + * no name collision. + */ + +static sh_p types; + +int +sl_newtype(sh_p tp) +{ + if (sl_findtype(tp->name)) { + return (EEXIST); + } + tp->next = types; + types = tp; + return (0); +} + +/* + * Look for a type of the name given. + */ +sh_p +sl_findtype(char *type) +{ + sh_p tp; + + tp = types; + while (tp) { + if (strcmp(tp->name, type) == 0) + return (tp); + tp = tp->next; + } + return (NULL); +} + +/* + * Ask all known handler types if a given slice is handled by them. + * If the slice specifies a type, then just find that. + */ +sh_p +slice_probeall(sl_p slice) +{ + sh_p tp = types; + if (slice->probeinfo.type == NULL) { + while (tp) { + printf("%s: probing for %s.. ",slice->name, tp->name); + if ((*tp->claim) (slice, NULL, NULL) == 0) { + printf("yep\n"); + return (tp); + } + printf("nope\n"); + tp = tp->next; + } + /* + * Null string ("") means "don't even try". Caller probably should + * pre-trap such cases but we'll check here too. + */ + } else if (slice->probeinfo.type[0]) { + tp = sl_findtype(slice->probeinfo.type); + if ((tp) && ((*tp->claim) (slice, NULL, NULL) == 0)) { + printf("%s: attaching %s..\n",slice->name, tp->name); + return (tp); + } + } + /*printf("%s: Leaving as raw device\n", slice->name); */ + return (NULL); +} + + + +/* + * Make a handler instantiation of the requested type. + * + */ +static int +sl_make_handler(char *type, sl_p slice) +{ + sh_p handler_up; + void *private_up; + int errval; + + /* + * check that the type makes sense. + */ + if (type == NULL) { + return (EINVAL); + } + handler_up = sl_findtype(type); + if (handler_up == NULL) { + return (ENXIO); + } + /* + * and call the constructor + */ + if (handler_up->constructor != NULL) { + errval = (*handler_up->constructor) (slice); + return (errval); + } else { + printf("slice handler %s has no constructor\n", + handler_up->name); + return (EINVAL); + } +} + +/* + * lock and unlock Slices while doing operations such as open(). + * gets a reference on the slice.. + * XXX This doesn't work for SMP. + */ +int +lockslice(struct slice *slice) +{ + int s = SLICESPL(); + slice->refs++; + while ( slice->flags & (SLF_LOCKED | SLF_INVALID)) { + if (slice->flags & SLF_INVALID) { + sl_unref(slice); + splx(s); + return (ENXIO); + } + slice->flags |= SLF_WANTED; + tsleep(slice, PRIBIO, "lockslice", 0); + } + slice->flags |= SLF_LOCKED; + splx(s); + return (0); +} + +/* + * Releases a slice + * Assumes that if we had it locked, no-one else could invalidate it. + * We can still hold a reference on it. + */ +int +unlockslice(struct slice *slice) +{ + int s = SLICESPL(); + slice->flags &= ~SLF_LOCKED; + if ( slice->flags & SLF_WANTED) { + slice->flags &= ~SLF_WANTED; + wakeup(slice); + } + splx(s); + return (0); +} + +/* + * create a new slice. Link it into the structures. don't yet find and call + * it's type handler. That's done later + */ +int +sl_make_slice(sh_p handler_down, void *private_down, + struct slicelimits * limits, + sl_p * slicepp, char *type, char *name) +{ + sl_p slice; + + /* + * Allocate storage for this instance . + */ + slice = malloc(sizeof(*slice), M_DEVBUF, M_NOWAIT); + if (slice == NULL) { + printf("slice failed to allocate driver storage\n"); + return (ENOMEM); + } + bzero(slice, sizeof(*slice)); + if (name) { + slice->name = malloc(strlen(name) + 1, M_DEVBUF, M_NOWAIT); + if (slice->name == NULL) { + printf("slice failed name storage\n"); + free(slice, M_DEVBUF); + return (ENOMEM); + } + strcpy(slice->name, name); + } + slice->handler_down = handler_down; + slice->private_down = private_down; + handler_down->refs++; + slice->limits = *limits; + slice_add_device(slice); + slice->refs = 1; /* one for our downward creator */ + *slicepp = slice; + if (type) { + slice->refs++; /* don't go away *//* probably not needed */ + sl_make_handler(type, slice); + sl_unref(slice); + } + return (0); +} + +/* + * Forceably start a shutdown process on a slice. Either call it's shutdown + * method, or do the default shutdown if there is no type-specific method. + * XXX Really should say who called us. + */ +void +sl_rmslice(sl_p slice) +{ +RR; + /* + * An extra reference so it doesn't go away while we are not looking. + */ + slice->refs++; + + if (slice->flags & SLF_INVALID) { + /* + * If it's already shutting down, let it die without further + * taunting. "go away or I'll taunt you a second time, you + * silly eenglish pig-dog" + */ + sl_unref(slice);/* possibly the last reference */ + return; + } + + /* + * Mark it as invalid so any newcomers know not to try use it. + * No real need to LOCK it. + */ + slice->flags &= ~SLF_OPEN_STATE; + slice->flags |= SLF_INVALID; + + /* + * remove the device appendages. + * Any open vnodes SHOULD go to deadfs. + */ + slice_remove_device(slice); + + /* + * Propogate the damage upwards. + * Note that the revoke method is not optional. + * The upper handler releases it's reference so refs--. + */ + if (slice->handler_up) { + (*slice->handler_up->revoke) (slice->private_up); + } + sl_unref(slice); /* One for the lower handler that called us */ + sl_unref(slice); /* possibly the last reference */ +} + + + +void +sl_unref(sl_p slice) +{ + if ((--(slice->refs)) == 0) { + FREE(slice, M_DEVBUF); + } +} + +/* + * Read a block on behalf of a handler. + * This is not a bulk IO routine but meant for probes etc. + * I think that perhaps it should attempt to do sliceopen() + * calls on the slice first. (XXX?) + */ +int +slice_readblock(struct slice * slice, int blkno, struct buf ** bpp) +{ + struct buf *bp; + int error = 0; + + /* + * posibly attempt to open device? + */ + /* --not yet-- */ + /* + * Now that it is open, get the buffer and set in the parameters. + */ + bp = geteblk((int) slice->limits.blksize); + if (bp == NULL) { + return (ENOMEM); + } + bp->b_pblkno = bp->b_blkno = blkno; + bp->b_bcount = slice->limits.blksize; + bp->b_flags |= B_BUSY | B_READ; + sliceio(slice, bp, SLW_ABOVE); + if (biowait(bp) != 0) { + printf("failure reading device block\n"); + error = EIO; + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + bp = NULL; + } + *bpp = bp; + return (error); +} + +/* + * Read a block on behalf of a handler. + * This is not a bulk IO routine but meant for probes etc. + * I think that perhaps it should attempt to do sliceopen() + * calls on the slice first. (XXX?) + */ +int +slice_writeblock(struct slice * slice, int blkno, struct buf * bp) +{ + int error = 0; + + if (bp == NULL) { + return (ENOMEM); + } + bp->b_pblkno = bp->b_blkno = blkno; + bp->b_bcount = slice->limits.blksize; + bp->b_flags |= B_BUSY | B_WRITE; + sliceio(slice, bp, SLW_ABOVE); + if (biowait(bp) != 0) { + printf("failure reading device block\n"); + error = EIO; + } + return (error); +} + +/* + * functions that are used to call the next level down. + */ +void +sliceio(sl_p slice, struct buf * bp, enum slc_who who) +{ + /* XXX do shortcuts here */ + + if (slice->flags & SLF_INVALID) { + bp->b_error = ENXIO; + goto bad; + } + /* + * if it's from above, assume it hasn't + * broken it's agreement about read/write. + * A higher level slice would have caught it. + * Make no such assumption if it's this device. + */ + if (who == SLW_DEVICE) { + if (((slice->flags & SLF_OPEN_DEV_WR) == 0) && + ( (bp->b_flags & B_READ) == B_WRITE )) { + bp->b_error = EROFS; + goto bad; + } + } + (*slice->handler_down->IOreq) (slice->private_down, bp); + return; +bad: + bp->b_flags |= B_ERROR; + /* toss transfer, we're done early */ + biodone(bp); + return; +} + +/* + * Try open a slice. + * don't forget to say if we are above (1) or the dev (0). + * + * We really need to add a lot of support for CHANGING + * what we have openned.. i.e if we have ABOVE open R/W + * and DEVICE open R/O, then closing the device + * should downgrade our open to those items below us to R/O. + * This would need support in both open and close routines in both + * slice and handler code. + * + * ((*) == Illegal state.. (how did we get here?)) + * (must have been in "shoot foot mode"). + * A bit already set can be set again. (may represent part of an upgrade) + * This may not hold true if we are in an 'illegal state'. + * Some such opens will fail in an attempt to revert to a legal state. + * success = ((request & allowed[state]) == request) + */ +#define UP_RDWR SLF_OPEN_UP +#define CHR_RDWR SLF_OPEN_CHR +#define CHR_RD SLF_OPEN_CHR_RD +#define BLK_RDWR SLF_OPEN_BLK +#define BLK_RD SLF_OPEN_BLK_RD +static u_char allowed[64] = { +/* Present state | requested states allowed */ +/* UP CHR BLK | UP CHR BLK */ +/* R W R W R W | R W R W R W */ +/* 0 0 0 0 0 0 1 1 1 1 1 1 */( UP_RDWR|CHR_RDWR|BLK_RDWR ), +/* 0 0 0 0 0 1 0 0 1 0 1 1 */( CHR_RD|BLK_RDWR ), +/* 0 0 0 0 1 0 1 1 1 1 1 1 */( UP_RDWR|CHR_RDWR|BLK_RDWR ), +/* 0 0 0 0 1 1 0 0 1 0 1 1 */( CHR_RD|BLK_RDWR ), +/* 0 0 0 1 0 0 0 0 1 1 1 0 */( CHR_RDWR|BLK_RD ), +/* 0 0 0 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 0 0 1 1 0 0 0 1 1 1 0 */( CHR_RDWR|BLK_RD ), +/* 0 0 0 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 0 1 0 0 0 1 1 1 1 1 1 */( UP_RDWR|CHR_RDWR|BLK_RDWR ), +/* 0 0 1 0 0 1 0 0 1 0 1 1 */( CHR_RD|BLK_RDWR ), +/* 0 0 1 0 1 0 1 1 1 1 1 1 */( UP_RDWR|CHR_RDWR|BLK_RDWR ), +/* 0 0 1 0 1 1 0 0 1 0 1 1 */( CHR_RD|BLK_RDWR ), +/* 0 0 1 1 0 0 0 0 1 1 1 0 */( CHR_RDWR|BLK_RD ), +/* 0 0 1 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 0 1 1 1 0 0 0 1 1 1 0 */( CHR_RDWR|BLK_RD ), +/* 0 0 1 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 0 1 0 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 0 1 0 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 0 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 0 1 1 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 0 1 1 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 0 1 1 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 0 0 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 0 0 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 0 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 0 1 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 0 1 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 0 1 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 1 0 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 1 0 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 0 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 0 0 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 1 1 0 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 0 1 0 1 1 1 0 1 0 */( UP_RDWR|CHR_RD|BLK_RD ), +/* 1 1 1 0 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 1 0 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 1 0 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 1 1 0 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ), +/* 1 1 1 1 1 1 0 0 1 0 1 0 (*) */( CHR_RD|BLK_RD ) }; + +int +sliceopen(struct slice *slice, int flags, int mode, + struct proc * p, enum slc_who who) +{ + int s; + int error; + int sl_flags = slice->flags & SLF_OPEN_STATE; + int or_flags; + int and_flags; + int dn_flags; + int odn_flags; + + + if (slice->flags & SLF_INVALID) + return (ENXIO); + /* + * Firstly, don't allow re-opens of what is already open + */ + if (error = lockslice(slice)) + return (error); + error = EBUSY; /* default answer */ + switch (who) { + case SLW_ABOVE: + or_flags = ((flags & FREAD) ? SLF_OPEN_UP_RD : 0); + or_flags |= ((flags & FWRITE) ? SLF_OPEN_UP_WR : 0); + and_flags = ~SLF_OPEN_UP; + break; + case SLW_DEVICE: + switch (mode & S_IFMT) { + case S_IFCHR: + or_flags = ((flags & FREAD) ? SLF_OPEN_CHR_RD : 0); + or_flags |= ((flags & FWRITE) ? SLF_OPEN_CHR_WR : 0); + and_flags = ~SLF_OPEN_CHR; + break; + case S_IFBLK: + or_flags = ((flags & FREAD) ? SLF_OPEN_BLK_RD : 0); + or_flags |= ((flags & FWRITE) ? SLF_OPEN_BLK_WR : 0); + and_flags = ~SLF_OPEN_BLK; + break; + default: + panic("slice: bad open type"); + } + break; + default: + panic("slice: bad request source"); + } + /* + * Be appropriatly paranoid depending on the system mode. + * This is also probably wrong XXX + */ + switch(slicexclusive) { + case 2: + /* + * if any one path has it open, we forbid any other + * paths. Only allow an upgrade/downgrade from + * the same source as the present openner. + */ + if ( sl_flags & and_flags) + goto reject; + case 1: /* + * The behaviour is encoded into the state array given above. + */ + if ((or_flags & allowed[sl_flags]) != or_flags) + goto reject; + break; + case 0: /* + * Permission is granted to shoot self in foot. + * All three of UPPER, CHAR and BLK can be open at once. + */ + break; + } + /* + * Get the old open mode and the new open mode. + * If we already have it open in this way, don't do it again. + * + * XXX More thought needed for the locking and open-flags. + * For now ignore the existance of flags other than FWRITE & FREAD. + */ + odn_flags = (sl_flags & SLF_OPEN_WR) ? FWRITE : 0; + odn_flags |= (sl_flags & SLF_OPEN_RD) ? FREAD : 0; + sl_flags &= and_flags; + sl_flags |= or_flags; + dn_flags = (sl_flags & SLF_OPEN_WR) ? FWRITE : 0; + dn_flags |= (sl_flags & SLF_OPEN_RD) ? FREAD : 0; + error = 0; + if (dn_flags != odn_flags) { + if ((error = (*slice->handler_down->open) (slice->private_down, + dn_flags, mode, p)) != 0) { + goto reject; + } + } + slice->flags &= ~SLF_OPEN_STATE; + slice->flags |= sl_flags; +reject: + unlockslice(slice); + sl_unref(slice); /* lockslice gave us a ref.*/ + return (error); +} + +void +sliceclose(struct slice *slice, int flags, int mode, + struct proc * p, enum slc_who who) +{ + sh_p tp; + + if (slice->flags & SLF_INVALID) + return ; + if (lockslice(slice)) + return ; + switch (who) { + case SLW_ABOVE: + slice->flags &= ~SLF_OPEN_UP; + break; + case SLW_DEVICE: + switch (mode & S_IFMT) { + case S_IFCHR: + slice->flags &= ~SLF_OPEN_CHR; + break; + case S_IFBLK: + slice->flags &= ~SLF_OPEN_BLK; + break; + default: + panic("slice: bad open type"); + } + /* + * If we had an upper handler, ask it to check if it's still + * valid. it may decide to self destruct. + */ + if (slice->handler_up) { + (*slice->handler_up->verify)(slice); + } + /* + * If we don't have an upper handler, check if + * maybe there is now a suitable environment for one. + * We may end up with a different handler + * from what we had above. Maybe we should clear the hint? + * Maybe we should ask the lower one to re-issue the request? + */ + if (slice->handler_up == NULL) { + if ((tp = slice_probeall(slice)) != NULL) { + (*tp->constructor)(slice); + } + } + break; + } + /* + * Last-close semantics strike again + * This may refine to a downgrade if we closed (say) the last writer + * but there are still readers. + * probably open/close should merge to one 'mode-change' function. + * (except for a vnode reference with no mode) + */ + if ( (slice->flags & SLF_OPEN_STATE) == 0) + (*slice->handler_down->close) (slice->private_down, + flags, mode, p); + unlockslice(slice); + sl_unref(slice); + return ; +} + + +/* + * control behaviour of slices WRT sharing: + * 2 = no sharing + * 1 = read on a device already mounted (or parent of) is ok. No writes. + * 0 = go ahead.. shoot yourself in the foot. + */ +static int +sysctl_kern_slicexclusive SYSCTL_HANDLER_ARGS +{ + int error; + int new_val = slicexclusive; + + error = sysctl_handle_int(oidp, &new_val, 0, req); + if (error == 0) { + if ((new_val >= 0) && (new_val < 3)) { + slicexclusive = new_val; + } else { + error = EINVAL; + } + } + return (error); +} + +SYSCTL_PROC(_kern, OID_AUTO, slicexclusive, CTLTYPE_INT|CTLFLAG_RW, + 0, sizeof slicexclusive, sysctl_kern_slicexclusive, "I", ""); + diff --git a/sys/dev/slice/slice_device.c b/sys/dev/slice/slice_device.c new file mode 100644 index 0000000000000..4b7f86bf74115 --- /dev/null +++ b/sys/dev/slice/slice_device.c @@ -0,0 +1,388 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. + * + * $Id: $ + */ + +#include +#include +#include /* SYSINIT stuff */ +#include /* cdevsw stuff */ +#include /* malloc region definitions */ +#include /* bufs for describing IO */ +#include /* file open modes etc. */ +#include /* standard queue macros */ +#include /* S_IFBLK, S_IFMT etc. */ +#include /* DEVFS defintitions */ +#include /* temporary location */ + + + +/* Function prototypes (these should all be static except for slicenew()) */ +static d_open_t slcdevopen; +static d_close_t slcdevclose; +static d_ioctl_t slcdevioctl; +static d_dump_t slcdevdump; +static d_psize_t slcdevsize; +static d_strategy_t slcdevstrategy; + +#define BDEV_MAJOR 14 +#define CDEV_MAJOR 20 + +static struct cdevsw slice_cdevsw; +static struct bdevsw slice_bdevsw = { + slcdevopen, + slcdevclose, + slcdevstrategy, + slcdevioctl, + slcdevdump, + slcdevsize, + D_DISK, + "slice", + &slice_cdevsw, + -1 +}; + +static dev_t cdevnum, bdevnum; + +#define UNIT_HASH_SIZE 64 +LIST_HEAD(slice_bucket, slice) hash_table[UNIT_HASH_SIZE - 1]; + +/* + * Now for some driver initialisation. Occurs ONCE during boot (very early). + */ +static void +slice_drvinit(void *unused) +{ + int i; + + /* + * add bdevsw and cdevsw entries + */ + bdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &slice_bdevsw); + + /* + * clear out the hash table + */ + for (i = 0; i < UNIT_HASH_SIZE; i++) { + LIST_INIT(hash_table + i); + } +} + +SYSINIT(slicedev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, + slice_drvinit, NULL); + +static int nextunit = 0; + +void +slice_add_device(sl_p slice) +{ + int unit = nextunit++; + char *name = slice->name; +RR; + slice->minor = makedev(0, + (((unit << 8) & 0xffff0000) | (unit & 0x000000ff))); + /* + * put it on the hash chain for it's bucket so we can find it again + * later. + */ + LIST_INSERT_HEAD(hash_table + (slice->minor % UNIT_HASH_SIZE), + slice, hash_list); + /* + * Add an entry in the devfs for it. Possibly should happen later. + */ + slice->devfs_ctoken = devfs_add_devswf(&slice_cdevsw, unit, DV_CHR, + UID_ROOT, GID_KMEM, 0600, "r%s", name ? name : "-"); + slice->devfs_btoken = devfs_add_devswf(&slice_bdevsw, unit, DV_BLK, + UID_ROOT, GID_KMEM, 0600, "%s", name ? name : "-"); + /* XXX link this node into upper list of caller */ +} + +/* + * Given a minor number, find the slice which the operations are destined. + * When DEVFS DDEV devices are enabled this is bypassed entirely. + */ +static struct slice * +minor_to_slice(unsigned int minor) +{ + int hash = minor % UNIT_HASH_SIZE; + struct slice *slice; + + slice = (hash_table + hash)->lh_first; + while (slice) { + if (slice->minor == minor) { + return (slice); + } + slice = slice->hash_list.le_next; + } + return (NULL); +} + +/* + * Macro to check that the unit number is valid Often this isn't needed as + * once the open() is performed, the unit number is pretty much safe.. The + * exception would be if we implemented devices that could "go away". in + * which case all these routines would be wise to check the number, + * DIAGNOSTIC or not. + */ +#define CHECKUNIT() \ +do { /* the do-while is a safe way to do this grouping */ \ + if (slice == NULL) { \ + printf( __FUNCTION__ ": unit not attached\n", unit); \ + panic ("slice"); \ + } \ +} while (0) + +#ifdef DIAGNOSTIC +#define CHECKUNIT_DIAG() CHECKUNIT() +#else /* DIAGNOSTIC */ +#define CHECKUNIT_DIAG() +#endif /* DIAGNOSTIC */ + +int +slcdevioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc * p) +{ + sl_p slice = minor_to_slice(minor(dev)); + int error = 0; + + CHECKUNIT_DIAG(); +RR; + + /* + * Look for only some generic "inherrited" ioctls that apply to all + * disk-like devices otherwise pass it down to the previous handler + */ + + switch (cmd) { + /* + * At present there are none, but eventually there would be + * something that returns the basic partition parameters. + * Whether this would be in the form of a disklabel or + * similar I have not yet decided. + */ + default: + if (slice->handler_down->ioctl) { + error = (*slice->handler_down->ioctl) + (slice->private_down, cmd, data, flag, p); + } else { + error = ENOTTY; + } + if (error) { + /* + * If no disklabel was returned, let's make + * up something that will satisfy the system's + * need for a disklabel to mount an ffs on. + * Don't overwrite error unless we get a dummy. + * let the called routine decide + * if it can handle any ioctl. + */ + if (dkl_dummy_ioctl(slice, cmd, data, flag, p) == 0) { + error = 0; + } + } + break; + } + return (error); +} + +/* + * You also need read, write, open, close routines. This should get you + * started. + * The open MIGHT allow the caller to proceed if it is a READ + * mode, and it is open at a higher layer. + * All Accesses would have to be checked for READ + * as the system doesn't enforce this at this time. + */ +static int +slcdevopen(dev_t dev, int flags, int mode, struct proc * p) +{ + sl_p slice = minor_to_slice(minor(dev)); + int error; + +RR; + if (slice == NULL) + return (ENXIO); +#if 1 /* the hack */ + if ((mode & S_IFMT) == S_IFBLK) { + /* + * XXX Because a mount -u does not re-open the device + * The hack here, is to always open block devices + * in full read/write mode. Eventually, if DEVFS + * becomes ubiquitous, VOP to do a file upgrade + * might be implemented. Other Filesystems need + * not implement it.. + * THIS SHOULD BE DONE IN slice_device.c + */ + flags |= FWRITE; + } +#endif /* the hack */ + return (sliceopen(slice, flags, mode, p, SLW_DEVICE)); +} + +static int +slcdevclose(dev_t dev, int flags, int mode, struct proc * p) +{ + sl_p slice = minor_to_slice(minor(dev)); +RR; + CHECKUNIT_DIAG(); + sliceclose(slice, flags, mode, p, SLW_DEVICE); + return(0); +} + +static int +slcdevsize(dev_t dev) +{ + sl_p slice = minor_to_slice(minor(dev)); + +RR; + if (slice == NULL) + return (-1); + +#if 0 + return (slice->limits.slicesize / slice->limits.blksize); +#else + return (slice->limits.slicesize / 512); +#endif +} + + +/* + * Read/write routine for a buffer. Finds the proper unit, range checks + * arguments, and schedules the transfer. Does not wait for the transfer to + * complete. Multi-page transfers are supported. All I/O requests must be a + * multiple of a sector in length. + */ +void +slcdevstrategy(struct buf * bp) +{ + sl_p slice = minor_to_slice(minor(bp->b_dev)); + u_int64_t start, end; + u_int32_t blksize; + daddr_t blkno; + int s; + +RR; + if (slice == NULL) { + bp->b_error = ENXIO; + goto bad; + } + blksize = slice->limits.blksize; + /* Check we are going to be able to do this kind of transfer */ + /* Check the start point too if DEV_BSIZE != reallity */ + if (bp->b_blkno < 0) { + Debugger("Slice code got negative blocknumber"); + bp->b_error = EINVAL; + goto bad; + } + start = (u_int64_t)bp->b_blkno * DEV_BSIZE; + if (blksize != DEV_BSIZE) { + if ((start % blksize) != 0) { + Debugger("slice: request not on block boundary."); + bp->b_error = EINVAL; + goto bad; + } + blkno = start / blksize; + } else { + blkno = bp->b_blkno; + } + + if ((bp->b_bcount % blksize) != 0) { + printf("bcount = %d, blksize= %d(%d)\n", + bp->b_bcount, blksize, + slice->limits.blksize); + Debugger("slice: request not multile of blocksize."); + bp->b_error = EINVAL; + goto bad; + } + /* + * Do bounds checking, adjust transfer, and set b_pblkno. + */ + bp->b_pblkno = blkno; + end = start + (u_int64_t)bp->b_bcount; /* first byte BEYOND the IO */ + + /* + * Handle the cases near or beyond the end of the slice. Assumes IO + * is < 2^63 bytes long. (pretty safe) + */ + if (end > slice->limits.slicesize) { + int64_t size; + size = slice->limits.slicesize - start; + /* + * if exactly on end of slice, return EOF + */ + if ((size == 0) && (bp->b_flags & B_READ)) { + printf("slice: at end of slice."); + bp->b_resid = bp->b_bcount; + goto done; + } + if (size <= 0) { + printf("slice: beyond end of slice."); + bp->b_error = EINVAL; + goto bad; + } + bp->b_bcount = size; + } + sliceio(slice, bp, SLW_DEVICE); + return; + +done: + s = splbio(); + /* toss transfer, we're done early */ + biodone(bp); + splx(s); + return; +bad: + bp->b_flags |= B_ERROR; + goto done; + +} + +void +slice_remove_device(sl_p slice) +{ + /* + * Remove the devfs entry, which revokes the vnode etc. XXX if + * handler has madde more, we should tell it too. e.g. floppy driver + * does this. + */ +RR; + devfs_remove_dev(slice->devfs_btoken); + devfs_remove_dev(slice->devfs_ctoken); + + /* + * Remove it from the hashtable. + */ + LIST_REMOVE(slice, hash_list); +} + +static int +slcdevdump(dev_t dev) +{ + sl_p slice = minor_to_slice(minor(dev)); +RR; + if (slice == NULL) + return (ENXIO); + return (0); +} diff --git a/sys/dev/slice/slices.thought b/sys/dev/slice/slices.thought new file mode 100644 index 0000000000000..654f250a44dba --- /dev/null +++ b/sys/dev/slice/slices.thought @@ -0,0 +1,124 @@ +Original notes on how this might be implememented.. may not reflect +the final code very much. +======================================================[JRE] + +the slices are 'kinda' stackable.. +With alternating layers of handler(driver)/slice/handler/slice/handler/slice +The "Slice" is a common structure shared between three pieces of code. +1/ the lower handler +2/ the upper handler +3/ the generic slice code, which implements the device nodes etc. +Each of these 3 items share a knowledge of the internal struture and +contents of the slice structure. they also know each other's published +interfaces. + +Each layer is much like the previous.. +Each layer has similar characteristics.. +The slices are created bottom up.. +i.e. the device probes, and creates a static 'slice' that is +assiciated with the device.. The static slice +can't be altered, unless the media is changed.. + +A translation method, which might be NULL, in which case +it is a simple case of offset addition. +Possibly the offset might be already added.. +possibly this might be achieved by specifying a default method set. + +Each disk slice has a structure associated with it.. +When a slice is 'loaded' there must be some way of deciding if it has +a subslice structure. if it does, then that structure must +be loaded to create mode slices.. +this is recursive. + +The structuring must be such that it can recognise an attempt to change +higer level structuring.. +This suggests a recursive 'open' count.. for open subslices. + +The idea would be to pass the following operations through methods. + + translation to (possibly more than one) io operation + open count passing.. + interpretation of subslicing and other slicing operations. + possibly there might be permissions inherritance. + open a slice... + create a slice.. methods are supplied by the disk driver.. + + upward methods: + force close + + identify slice type.. slice type modules each asked + to identify.. + +to do IO + +1/ find apropriate slice (hash) +LIST_HEAD(slice_bucket, slice) hash_table[UNIT_HASH_SIZE - 1]; + +in the init, +for ( i = 0; i < UNIT_HASH_SIZE; i++) { + LIST_INIT(hash_table + i) +} + +struct slice *minor_to_slice(unsigned int minor) +{ + int hash = minor % UNIT_HASH_SIZE; + struct slice *slice; + + slice = (hash_table + hash)->lh_first; + while (slice) { + if (slice->minor == minor) { + return (slice); + } + slice = slice->hashlist.le_next + } + return (NULL); +} +2/ + if IO method, + do IO method.. + return + check bounds + adjust offset + follow slice-parent link and loop to top +IO methods are supplied by drivers +drivers including concatination drivers etc. + +concatination must be seen as a slice type + +once all parts of a concatinated slice are created, then +the new 'concatinated slice' appears in the devfs. +a concatinated slice has a 'label' that identifies it's volume +and it's part (e.g. part 3 out of 5) +'slice's in ram are either Primary or slave slices in a concatinated slice.. + + +to set up a slice call slice_add() which: +1/ If (slice type not known) + Identify the type (call all type probe routines in turn) +2/ call the succeeding type attach routine +3/ the attach routine calls slice_add() for each sub slice +The probe and attach may merge. + + +the type of a slice must be detirmined taking into account the type of the +parent slice.. It is conceivable that the parent slice +might TELL the slice_add() routine what to make the slice.. +Possibly the parent set's a CONTEXT which might be helpful +in identifying a slice + + +disk: +set up Method struct: +IO points to disk strategy +set size to whole disk +set offset to 0 +set context to RAW +call slice_add + + +slice_add() +called when we know about a slice..possibly we should fill out a slice +struct before calling it. +The slice struct is all that is needed to be able to do IO +to the slice. slice_add, will then link the slice into +the system as needed. -- cgit v1.3