freebsd-skq/sys/dev/slice/slice_base.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", "");