827 lines
23 KiB
C
827 lines
23 KiB
C
/*-
|
|
* 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: slice_base.c,v 1.5 1998/07/13 08:22:56 julian Exp $
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h> /* SYSINIT stuff */
|
|
#include <sys/fcntl.h> /* FREAD/FWRITE */
|
|
#include <sys/conf.h> /* cdevsw stuff */
|
|
#include <sys/malloc.h> /* malloc region definitions */
|
|
#include <sys/buf.h> /* buffers for IO */
|
|
#include <sys/queue.h> /* linked lists etc. */
|
|
#include <sys/stat.h> /* S_IFCHR, S_IFBLK */
|
|
#include <sys/sysctl.h> /* the sysctl for shooting self in foot */
|
|
/*#include <sys/devfsext.h> */ /* DEVFS defintitions */
|
|
#include <dev/slice/slice.h> /* temporary location */
|
|
|
|
#define SLICESPL() splbio()
|
|
|
|
static void sl_async_done(struct buf *bp);
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Make a handler instantiation of the requested type.
|
|
* don't take no for an answer.
|
|
* force it to mark it's new territory.
|
|
* Must be called from a within a user context.
|
|
*
|
|
*/
|
|
static int
|
|
sl_make_handler(sl_p slice, char *type)
|
|
{
|
|
sh_p handler_up;
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
slice->flags |= SLF_DONT_ARGUE;
|
|
return( (*handler_up->claim) (slice));
|
|
}
|
|
|
|
/*
|
|
* lock and unlock Slices while doing operations such as open().
|
|
* gets a reference on the slice..
|
|
* XXX This doesn't work for SMP.
|
|
*/
|
|
int
|
|
slice_lock(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, "slice_lock", 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
|
|
slice_unlock(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.
|
|
* As of yet it has no upper handler.
|
|
*/
|
|
int
|
|
sl_make_slice(sh_p handler_down, void *private_down,
|
|
struct slicelimits * limits,
|
|
sl_p * slicepp, 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;
|
|
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.
|
|
* Should be called at SLICESPL (splbio)
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* Handler probing state machine support.
|
|
***********************************************************************/
|
|
|
|
/*
|
|
* Ask all known handler types if a given slice is handled by them.
|
|
* If the slice specifies a type, then just find that.
|
|
* This will be done asynchronously. The claim operation may simply
|
|
* queue the work to be done. When this item has been rejected,
|
|
* control will pass to slice_probe_next().
|
|
* This starts up the generic probeing state machine, which
|
|
* will start up the probing state machine for each handler in turn,
|
|
* until one has claimed the device, or there are no more handlers.
|
|
*
|
|
*/
|
|
void
|
|
slice_start_probe(sl_p slice)
|
|
{
|
|
sh_p tp = types;
|
|
|
|
if (slice->probeinfo.type == NULL) {
|
|
if(slice->handler_up == NULL) {
|
|
slice->probeinfo.trial_handler = tp;
|
|
slice->flags |= SLF_PROBING;
|
|
printf("%s: probing for %s.. ",slice->name, tp->name);
|
|
(*tp->claim) (slice);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Null string ("") means "don't even try". Caller probably
|
|
* should pre-trap such cases but we'll check here too.
|
|
* Notice that the PROBING bit is not set.
|
|
* This means that we should not do a full probe,
|
|
* but just this one handler.
|
|
*/
|
|
if (slice->probeinfo.type[0]) {
|
|
tp = sl_findtype(slice->probeinfo.type);
|
|
if (tp) {
|
|
printf("%s: attaching %s..\n", slice->name, tp->name);
|
|
(*tp->claim) (slice);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Move the slice probe type, on to the next type
|
|
* and call that. Called from failed probes.
|
|
* Don't do anything if the PROBING flag has been cleared.
|
|
*/
|
|
void
|
|
slice_probe_next(sl_p slice)
|
|
{
|
|
sh_p tp = slice->probeinfo.trial_handler;
|
|
|
|
if ((slice->flags & SLF_PROBING) == 0)
|
|
return;
|
|
if (tp != NULL) {
|
|
if (slice->probeinfo.trial_handler = tp = tp->next) {
|
|
printf("%s: probing for %s.. ",slice->name, tp->name);
|
|
(*tp->claim) (slice);
|
|
return;
|
|
}
|
|
}
|
|
slice->flags &= ~SLF_PROBING;
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a slice, launch an IOrequest for information
|
|
* This is not a bulk IO routine but meant for probes etc.
|
|
* This routine may be called at interrupt time. It schedules an
|
|
* IO that will be completed asynchronously. On completion the
|
|
* Block IO system will call sl_async_done, which will trigger
|
|
* a completion event for the handler's probe state machine.
|
|
*/
|
|
int
|
|
slice_request_block(sl_p slice, int blknum)
|
|
{
|
|
struct buf *bp;
|
|
int s;
|
|
|
|
RR;
|
|
s = splbio();
|
|
#ifdef PARANOID
|
|
if ( slice->private_up == NULL) {
|
|
panic("slice_request_block: no pd");
|
|
}
|
|
if (slice->flags & SLF_PROBE_STATE) {
|
|
panic("slice_request_block: 2nd IO");
|
|
}
|
|
#endif /* PARANOID */
|
|
bp = geteblk((int) slice->limits.blksize);
|
|
if (bp == NULL) {
|
|
return (ENOMEM);
|
|
}
|
|
slice->flags |= SLF_WAIT_READ;
|
|
bp->b_iodone = &sl_async_done;
|
|
bp->b_flags |= B_CALL;
|
|
bp->b_dev = (dev_t)slice; /* XXX HACK ALERT! */
|
|
bp->b_pblkno = bp->b_blkno = blknum;
|
|
bp->b_bcount = slice->limits.blksize;
|
|
bp->b_flags |= B_BUSY | B_READ;
|
|
sliceio(slice, bp, SLW_ABOVE);
|
|
splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Write 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?) no, they may block?
|
|
*/
|
|
int
|
|
slice_writeblock(struct slice * slice, int blkno,
|
|
void (*iodone )(struct buf *),
|
|
caddr_t data, int len)
|
|
{
|
|
struct buf *bp;
|
|
int error = 0;
|
|
|
|
#ifdef PARANOID
|
|
if ( slice->handler_up == NULL) {
|
|
panic("slice_writeblock: no handler");
|
|
}
|
|
if (slice->flags & SLF_PROBE_STATE) {
|
|
panic("slice_writeblock: 2nd IO");
|
|
}
|
|
#endif /* PARANOID */
|
|
if (len > slice->limits.blksize)
|
|
return (EINVAL);
|
|
bp = geteblk((int) slice->limits.blksize);
|
|
if (bp == NULL) {
|
|
return (ENOMEM);
|
|
}
|
|
slice->flags |= SLF_WAIT_WRITE;
|
|
bcopy(data, bp->b_data, len);
|
|
bp->b_iodone = sl_async_done;
|
|
bp->b_flags |= B_CALL;
|
|
bp->b_dev = (dev_t)slice; /* XXX HACK ALERT! */
|
|
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);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* called with an argument of a bp when it is completed.
|
|
* Th eslice is extracted from the operation and the completion event
|
|
* is used to trigger that slice's state machine to make the next move.
|
|
*/
|
|
static void
|
|
sl_async_done(struct buf *bp)
|
|
{
|
|
sl_p slice;
|
|
int error;
|
|
|
|
RR;
|
|
if (bp->b_dev < 0xf0000000)
|
|
panic ("b_dev used in SLICE code");
|
|
slice = (struct slice *)bp->b_dev; /* XXX HACK! */
|
|
|
|
#ifdef PARANOID
|
|
if ( slice->handler_up == NULL) {
|
|
panic("sl_async_done: no pd");
|
|
}
|
|
if (bp->b_flags & B_READ) {
|
|
if ((slice->flags & SLF_PROBE_STATE) != SLF_WAIT_READ)
|
|
panic("sl_async_done: unexpected read completion");
|
|
} else {
|
|
if ((slice->flags & SLF_PROBE_STATE) != SLF_WAIT_WRITE)
|
|
panic("sl_async_done: unexpected write completion");
|
|
}
|
|
#endif /* PARANOID */
|
|
|
|
/*
|
|
* if the IO failed, then abandon the probes and
|
|
* return. Possibly ask the lower layer to try again later?
|
|
* It's assumed that a a revoke will abort the state machine.
|
|
* XXX Maybe we should call the done() routine anyhow
|
|
* and let each handler detect the failure..
|
|
*/
|
|
if (bp->b_flags & B_ERROR) {
|
|
(* slice->handler_up->revoke)(slice->private_up);
|
|
|
|
/* (* slice->handler_down->SOMETHING) (slice->private_down); */
|
|
|
|
bp->b_flags |= B_INVAL | B_AGE;
|
|
brelse(bp);
|
|
slice->flags &= ~SLF_PROBING;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Call the handler's done() routine. This will
|
|
* examine the result of the probe and do whatever is needed.
|
|
* Check for abnormal error conditions. (return value non 0)
|
|
* Not claiming the slice is not an error condition.
|
|
*/
|
|
if ( (* slice->handler_up->done)(slice, bp)) {
|
|
slice->flags &= ~SLF_PROBING;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the handler has left itself there, or cleared
|
|
* the PROBING bit, then consider
|
|
* probing to have come to a close. So just return.
|
|
* XXX An IO error would be a great hint to abandon probing as well.
|
|
* we catch that on the way up but we might want to give
|
|
* the handler a chance to clean up state?
|
|
*/
|
|
if (slice->handler_up || ((slice->flags & SLF_PROBING) == 0)) {
|
|
slice->flags &= ~SLF_PROBING;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* The handler didn't claim it. Nor did it abort the
|
|
* probing sequence.
|
|
* Ok, so we should try the next handler to probe.
|
|
*/
|
|
slice_probe_next(slice);
|
|
}
|
|
|
|
/*
|
|
* 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 = slice_lock(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");
|
|
}
|
|
/* XXX only accumulate flags as we don't know about all closes */
|
|
/* XXX */ if ( or_flags )
|
|
/* XXX */ and_flags = ~0;
|
|
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;
|
|
#if 1 /* it was basically a close */
|
|
if ((slice->flags & SLF_OPEN_STATE) == SLF_CLOSED) {
|
|
sh_p tp;
|
|
|
|
/*
|
|
* 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) {
|
|
slice_start_probe(slice);
|
|
}
|
|
}
|
|
#endif
|
|
reject:
|
|
slice_unlock(slice);
|
|
if ((slice->flags & SLF_INVALID) == SLF_INVALID)
|
|
error = ENODEV; /* we've been zapped while down there! */
|
|
sl_unref(slice); /* slice_lock gave us a ref.*/
|
|
return (error);
|
|
}
|
|
|
|
#if 0
|
|
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 (slice_lock(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_start_probe(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);
|
|
slice_unlock(slice);
|
|
sl_unref(slice);
|
|
return ;
|
|
}
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* 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", "");
|
|
|