633 lines
16 KiB
C
633 lines
16 KiB
C
static char sccsid[] = "@(#)58 1.44 src/bos/kernel/specfs/devsubs.c, sysspecfs, bos41J, 9521A_all 5/23/95 08:02:22";
|
|
/*
|
|
* COMPONENT_NAME: (SYSPFS) Physical File System
|
|
*
|
|
* FUNCTIONS: devopen, devclose, devioctl,
|
|
* devmpx, devread, devwrite
|
|
*
|
|
* ORIGINS: 27, 83
|
|
*
|
|
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
|
|
* combined with the aggregated modules for this product)
|
|
* SOURCE MATERIALS
|
|
* (C) COPYRIGHT International Business Machines Corp. 1988, 1993
|
|
* All Rights Reserved
|
|
*
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*/
|
|
/*
|
|
* LEVEL 1, 5 Years Bull Confidential Information
|
|
*/
|
|
|
|
#include "sys/user.h"
|
|
#include "sys/types.h"
|
|
#include "sys/vnode.h"
|
|
#include "sys/file.h"
|
|
#include "sys/buf.h"
|
|
#include "sys/uio.h"
|
|
#include "sys/syspest.h"
|
|
#include "sys/errno.h"
|
|
#include "sys/conf.h"
|
|
#include "sys/device.h"
|
|
#include "sys/lockl.h"
|
|
#include "sys/dev.h"
|
|
#include "sys/specnode.h"
|
|
#include "sys/fs_locks.h"
|
|
#include "sys/sleep.h"
|
|
|
|
/* Declarations */
|
|
static devcopen();
|
|
|
|
int ocr_event = EVENT_NULL; /* open/close/revoke wait list */
|
|
|
|
/*
|
|
* NAME: fp_opendev(devno, flags, cname, ext, fpp)
|
|
*
|
|
* FUNCTION: This function opens the specified device using the
|
|
* specified flags and returns a file struct pointer
|
|
* referencing the open device.
|
|
* This function examines character special devices to
|
|
* determine whether they are actually multiplexed devices.
|
|
*
|
|
* PARAMETERS: devno - device number of dev to open
|
|
* flags - device open flags (DREAD and/or DWRITE)
|
|
* cname - channel name
|
|
* ext - extension parameter
|
|
* fpp - pointer for returned file struct pointer
|
|
*
|
|
* RETURN : ENXIO - Invalid device
|
|
* ENFILE - file table overflow
|
|
* ENXIO - bad channel allocation
|
|
*
|
|
*/
|
|
|
|
int
|
|
fp_opendev(
|
|
dev_t devno, /* device number of dev to open */
|
|
int flags, /* file open flags */
|
|
caddr_t cname, /* channel name */
|
|
int ext, /* extension parameter */
|
|
struct file ** fpp) /* file struct pointer return */
|
|
{
|
|
int rc; /* return code */
|
|
uint status; /* status of dev switch entry */
|
|
chan_t chan = 0; /* channel number */
|
|
struct gnode * gp = NULL; /* spec gnode */
|
|
extern struct fileops gno_fops; /* file ops for gnode type fps */
|
|
int waslocked; /* kernel lock status */
|
|
caddr_t cnamex; /* rationalized channel name */
|
|
|
|
|
|
/* check file open flags */
|
|
if ((flags & (DREAD|DWRITE)) == 0)
|
|
return EINVAL;
|
|
|
|
/* Lose the kernel lock to avoid hierarchy problems. */
|
|
if (waslocked = IS_LOCKED(&kernel_lock))
|
|
unlockl(&kernel_lock);
|
|
|
|
/* get and check the status of the dev switch entry */
|
|
if (devswqry(devno, &status, NULL) || status == DSW_UNDEFINED)
|
|
{
|
|
rc = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
/* If this is an mpx device, allocate the mpx channel. */
|
|
if (status & DSW_MPX)
|
|
{
|
|
cnamex = (cname)? cname : "";
|
|
|
|
DD_ENT(rc = ,
|
|
(*devsw[major(devno)].d_mpx)(devno, &chan, cnamex),
|
|
IPRI_NOPREMPT,major(devno));
|
|
if (rc)
|
|
{
|
|
rc = ENXIO;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Pass a NULL gnode to devcopen() because we don't have
|
|
* the spec gnode.
|
|
*/
|
|
gp = NULL;
|
|
rc = devcopen(devno, DKERNEL|flags, chan, ext, &gp);
|
|
if (rc == 0)
|
|
{
|
|
/* allocate the file struct pointer */
|
|
if (rc = fpalloc(gp, flags, DTYPE_GNODE, &gno_fops, fpp))
|
|
rdevclose(gp, flags);
|
|
}
|
|
|
|
out:
|
|
if (waslocked)
|
|
lockl(&kernel_lock, LOCK_SHORT);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: rdevopen(devno, flags, chan, ext, gpp)
|
|
*
|
|
* FUNCTION: This function is a common device open routine, shared
|
|
* between the specfs and the rest of the kernel. This
|
|
* routine serves two purposes. First, it performs
|
|
* intra-kernel device opens. Second, it is used by the
|
|
* file system to "register" device opens processed through
|
|
* the file system.
|
|
*
|
|
* PARAMETERS: devno - device number of device to open
|
|
* flags - device open flags (DREAD, DWRITE, and/or DMOUNT)
|
|
* chan - device channel number
|
|
* ext - open extension parameter
|
|
* fpp - pointer for returned file struct pointer
|
|
*
|
|
* RETURN : ENXIO - Invalid device
|
|
* ENFILE - File table overflow
|
|
*
|
|
*/
|
|
|
|
int
|
|
rdevopen (
|
|
dev_t devno, /* device number */
|
|
int flags, /* DREAD, DWRITE, and/or DMOUNT */
|
|
chan_t chan, /* device channel number */
|
|
int ext, /* open extension parameter */
|
|
struct gnode ** gpp) /* spec gnode return */
|
|
{
|
|
uint status; /* status of dev switch entry */
|
|
int waslocked; /* kernel lock status */
|
|
int rc;
|
|
|
|
/* Lose the kernel lock to avoid hierarchy problems. */
|
|
if (waslocked = IS_LOCKED(&kernel_lock))
|
|
unlockl(&kernel_lock);
|
|
|
|
/* Check for device number out of range or device entry
|
|
* being removed.
|
|
*/
|
|
if (devswqry(devno, &status, NULL) || status == DSW_UNDEFINED)
|
|
rc = ENXIO;
|
|
else
|
|
rc = devcopen(devno, flags, chan, ext, gpp);
|
|
|
|
if (waslocked)
|
|
lockl(&kernel_lock, LOCK_SHORT);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: devcopen(devno, flags, chan, ext, gpp)
|
|
*
|
|
* FUNCTION: This function is a common device open routine, shared
|
|
* between rdevopen() and fp_opendev(). It performs device
|
|
* opens for the kernel and for kernel extensions.
|
|
*
|
|
* PARAMETERS: devno - device number of dev to open
|
|
* flags - device open flags (DREAD, DWRITE, and/or DMOUNT)
|
|
* chan - device channel number
|
|
* ext - open extension parameter
|
|
* fpp - pointer for returned file struct pointer
|
|
*
|
|
* RETURN : ENXIO - Invalid device
|
|
* ENFILE - File table overflow
|
|
*
|
|
* SERIALIZATION: This routine must be called with no locks held.
|
|
*/
|
|
|
|
static
|
|
int
|
|
devcopen (
|
|
dev_t devno, /* dev number of device to open */
|
|
int flags, /* DREAD, DWRITE, and/or DMOUNT */
|
|
chan_t chan, /* device channel number */
|
|
int ext, /* open extension parameter */
|
|
struct gnode ** gpp) /* spec gnode return */
|
|
{
|
|
int rc; /* return codes */
|
|
struct devnode *dp; /* devnode for device */
|
|
struct gnode * gp; /* dev gnode of device */
|
|
int majorno; /* device major number */
|
|
int mpsafe; /* 1 = safe, 0 = funneled */
|
|
label_t jb; /* setjmpx/longjmpx buffer */
|
|
|
|
/* get the devnode for the device if it wasn't passed in */
|
|
if (*gpp == NULL)
|
|
{
|
|
uint status = 0; /* device switch status */
|
|
int devtype; /* type of device */
|
|
|
|
/* get and check the status of the dev switch entry */
|
|
if (devswqry(devno, &status, NULL) || status == DSW_UNDEFINED)
|
|
return ENXIO;
|
|
|
|
/* set the device type for devget */
|
|
devtype = (status & DSW_BLOCK) ? VBLK
|
|
: (status & DSW_MPX) ? VMPC : VCHR;
|
|
|
|
/*
|
|
* If we succesfully return from devget(), the devnode,
|
|
* and hence the gnode, is locked.
|
|
*/
|
|
if (rc = devget(devno, chan, devtype, &dp))
|
|
return rc;
|
|
gp = DTODGP(dp);
|
|
}
|
|
else
|
|
{
|
|
/* Lock the devnode so the counts are protected. */
|
|
gp = *gpp;
|
|
dp = DGTODP(gp);
|
|
DEVNODE_LOCK(dp);
|
|
}
|
|
|
|
/* Check for device closing. If it is then wait till the
|
|
* close completes.
|
|
*/
|
|
while (dp->dv_flag & DV_CLOSING)
|
|
{
|
|
dp->dv_flag |= DV_OPENPENDING;
|
|
rc = e_sleep_thread(&ocr_event, &dp->dv_lock,
|
|
INTERRUPTIBLE|LOCK_SIMPLE);
|
|
if (rc == THREAD_INTERRUPTED)
|
|
{
|
|
if (*gpp == NULL)
|
|
devput(dp);
|
|
else
|
|
DEVNODE_UNLOCK(dp);
|
|
return EINTR;
|
|
}
|
|
}
|
|
|
|
/* Set flags before calling open. If open sleeps() I want to
|
|
* err on the conservative side in the case of mount.
|
|
* Opens for read are ok.
|
|
*/
|
|
if (flags & DMOUNT)
|
|
{
|
|
if (gp->gn_wrcnt || gp->gn_mntcnt)
|
|
{
|
|
if (*gpp == NULL)
|
|
devput(dp);
|
|
else
|
|
DEVNODE_UNLOCK(dp);
|
|
return EBUSY;
|
|
}
|
|
}
|
|
|
|
/* adjust open counts */
|
|
if (flags & DREAD)
|
|
gp->gn_rdcnt += 1;
|
|
if (flags & DWRITE)
|
|
gp->gn_wrcnt += 1;
|
|
if (flags & DMOUNT)
|
|
gp->gn_mntcnt += 1;
|
|
|
|
majorno = major(devno);
|
|
mpsafe = devsw[majorno].d_opts & DEV_MPSAFE;
|
|
|
|
/* We should never hold any private locks across the device open.
|
|
* For funneled device drivers, we are required to hold the
|
|
* kernel lock across the open call.
|
|
*/
|
|
DEVNODE_UNLOCK(dp);
|
|
|
|
ASSERT(!IS_LOCKED(&kernel_lock));
|
|
if (!mpsafe)
|
|
lockl(&kernel_lock, LOCK_SHORT);
|
|
|
|
/*
|
|
* Establish exception return point. Drivers can call
|
|
* preemtible services that longjmp. (eg. lockl)
|
|
*/
|
|
if ((rc = setjmpx(&jb)) == 0)
|
|
{
|
|
DD_ENT(rc = ,
|
|
(*devsw[majorno].d_open)(devno,
|
|
flags & DMASK,
|
|
chan,
|
|
ext),
|
|
IPRI_NOPREMPT,majorno);
|
|
|
|
clrjmpx(&jb);
|
|
}
|
|
|
|
/* Restore state of locks prior to call to device. */
|
|
if (!mpsafe)
|
|
unlockl(&kernel_lock);
|
|
|
|
if (rc)
|
|
{
|
|
/* Lock the devnode to adjust counts */
|
|
DEVNODE_LOCK(dp);
|
|
|
|
/* If mpx, then decrement count and determine if devput
|
|
should be called, otherwise for all other drivers
|
|
always call close on error.
|
|
*/
|
|
if ((gp->gn_type == VMPC))
|
|
{
|
|
/* adjust open counts */
|
|
if (flags & DREAD)
|
|
gp->gn_rdcnt -= 1;
|
|
if (flags & DWRITE)
|
|
gp->gn_wrcnt -= 1;
|
|
if (flags & DMOUNT)
|
|
gp->gn_mntcnt -= 1;
|
|
|
|
if (*gpp == NULL &&
|
|
(gp->gn_rdcnt + gp->gn_wrcnt + gp->gn_mntcnt) == 0)
|
|
devput(dp);
|
|
else
|
|
DEVNODE_UNLOCK(dp);
|
|
}
|
|
else
|
|
{
|
|
(void)devcclose(gp, flags);
|
|
if (*gpp == NULL)
|
|
devput(dp);
|
|
else
|
|
DEVNODE_UNLOCK(dp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*gpp = gp;
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
/*
|
|
* NAME: rdevclose(gp, flags)
|
|
*
|
|
* FUNCTION: registered device close for those who want the gnode cleaned up
|
|
*
|
|
* PARAMETERS: gp - pointer to the gnode structure that
|
|
* represents the device
|
|
* flags - device open flags (DREAD, DWRITE, and/or DMOUNT)
|
|
*
|
|
* RETURN : EINVAL - Invalid file pointer
|
|
*/
|
|
|
|
int
|
|
rdevclose (
|
|
struct gnode * gp, /* gnode of device to close */
|
|
int flags) /* device open flags */
|
|
{
|
|
int rc; /* return code */
|
|
struct devnode * dp;
|
|
int waslocked; /* kernel lock status */
|
|
|
|
/* Lose the kernel lock to avoid hierarchy problems. */
|
|
if (waslocked = IS_LOCKED(&kernel_lock))
|
|
unlockl(&kernel_lock);
|
|
|
|
dp = DGTODP(gp);
|
|
DEVNODE_LOCK(dp);
|
|
rc = devcclose(gp, flags);
|
|
(void)devput(dp);
|
|
|
|
/* Restore kernel lock if needed. */
|
|
if (waslocked)
|
|
lockl(&kernel_lock, LOCK_SHORT);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: devcclose(gp, flags)
|
|
*
|
|
* FUNCTION: Common device close, shared between specfs and devclose.
|
|
*
|
|
* PARAMETERS: gp - pointer to the gnode structure that
|
|
* represents the device
|
|
* flags - device open flags (DREAD, DWRITE, and/or DMOUNT)
|
|
*
|
|
* RETURN : EINVAL - Invalid file pointer
|
|
*
|
|
* SERIALIZATION: This routine must be called with the DEVNODE_LOCK held.
|
|
*/
|
|
|
|
int
|
|
devcclose (
|
|
struct gnode * gp, /* gnode of device to close */
|
|
int flags) /* device open flags */
|
|
{
|
|
int cnt; /* sum of open counts */
|
|
int rc = 0; /* return code */
|
|
label_t jb; /* setjmpx/longjmpx buffer */
|
|
|
|
/* adjust the open counts */
|
|
if (flags & DREAD)
|
|
gp->gn_rdcnt -= 1;
|
|
if (flags & DWRITE)
|
|
gp->gn_wrcnt -= 1;
|
|
if (flags & DMOUNT)
|
|
gp->gn_mntcnt -= 1;
|
|
|
|
/* We always call the device close entry point for mpx devices.
|
|
* If the total of the open counts is zero, then after this close
|
|
* there will be no remaining open references to the device.
|
|
* For non-mpx devices, this is the only case in which we want
|
|
* to call the close entry point of the device.
|
|
* In all cases, this instance of the open is considered closed.
|
|
*/
|
|
cnt = gp->gn_rdcnt + gp->gn_wrcnt + gp->gn_mntcnt;
|
|
if (cnt == 0 || gp->gn_type == VMPC)
|
|
{
|
|
struct devnode *dp; /* devnode for the gnode */
|
|
int majorno; /* device major number */
|
|
int mpsafe; /* 1 = safe, 0 = funneled */
|
|
|
|
dp = DGTODP(gp);
|
|
majorno = major(gp->gn_rdev);
|
|
mpsafe = devsw[majorno].d_opts & DEV_MPSAFE;
|
|
|
|
/* Mark the device as closing to prevent opens */
|
|
dp->dv_flag |= DV_CLOSING;
|
|
|
|
/* sleep uninterruptibly waiting for revoke to finish
|
|
*/
|
|
while (dp->dv_flag & DV_REVOKING)
|
|
{
|
|
dp->dv_flag |= DV_CLOSEPENDING;
|
|
e_sleep_thread(&ocr_event, &dp->dv_lock, LOCK_SIMPLE);
|
|
}
|
|
|
|
/* Release private locks before calling the driver.
|
|
* For funneled device drivers, we are required to hold
|
|
* the kernel lock across the close call.
|
|
*/
|
|
DEVNODE_UNLOCK(dp);
|
|
ASSERT(!IS_LOCKED(&kernel_lock));
|
|
if (!mpsafe)
|
|
lockl(&kernel_lock, LOCK_SHORT);
|
|
|
|
/*
|
|
* Establish exception return point. Drivers can call
|
|
* preemtible services that longjmp. (eg. lockl)
|
|
*/
|
|
if ((rc = setjmpx(&jb)) == 0)
|
|
{
|
|
DD_ENT(rc =,
|
|
(*devsw[majorno].d_close)(gp->gn_rdev,
|
|
gp->gn_chan, 0),
|
|
IPRI_NOPREMPT,majorno);
|
|
|
|
clrjmpx(&jb);
|
|
}
|
|
|
|
/* Restore locks to previous state. */
|
|
if (!mpsafe)
|
|
unlockl(&kernel_lock);
|
|
DEVNODE_LOCK(dp);
|
|
|
|
/* Wake up any pending opens. */
|
|
dp->dv_flag &= ~DV_CLOSING;
|
|
if (dp->dv_flag & DV_OPENPENDING)
|
|
{
|
|
dp->dv_flag &= ~DV_OPENPENDING;
|
|
e_wakeup(&ocr_event);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: rdevread(rdev, uiop, chan, ext)
|
|
*
|
|
* FUNCTION: This function reads from the specified device. This
|
|
* function is called from both the specfs and the fp services
|
|
* (fp_read()) for direct device opens (via fp_opendev()).
|
|
*
|
|
* PARAMETERS: rdev - device number of device to read from
|
|
* uiop - read location information
|
|
* chan - device channel number
|
|
* ext - read extension parameter
|
|
*
|
|
* RETURN : errors from the device read entry point
|
|
*
|
|
*/
|
|
|
|
rdevread (
|
|
dev_t rdev, /* dev number of device to read */
|
|
struct uio * uiop, /* read location information */
|
|
chan_t chan, /* device channel number */
|
|
int ext) /* read extension parameter */
|
|
{
|
|
int rc; /* return code */
|
|
|
|
DD_ENT(rc =,
|
|
(*devsw[major(rdev)].d_read)(rdev, uiop, chan, ext),
|
|
IPRI_BASE,major(rdev));
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: rdevwrite(rdev, uiop, chan, ext)
|
|
*
|
|
* FUNCTION: This function writes to the specified device. This
|
|
* function is called from both the specfs and the fp services
|
|
* (fp_write()) for direct device opens (via fp_opendev()).
|
|
*
|
|
* PARAMETERS: rdev - device number of device to write to
|
|
* uiop - write location information
|
|
* chan - device channel number
|
|
* ext - write extension parameter
|
|
*
|
|
* RETURN : errors from the device write entry point
|
|
*/
|
|
|
|
rdevwrite (
|
|
dev_t rdev, /* dev num of device to write */
|
|
struct uio * uiop, /* write location information */
|
|
chan_t chan, /* device channel number */
|
|
int ext) /* write extension parameter */
|
|
{
|
|
int rc; /* return code */
|
|
|
|
DD_ENT(rc = , (*devsw[major(rdev)].d_write)
|
|
(rdev, uiop, chan, ext),IPRI_BASE,major(rdev));
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: rdevselect(rdev, corl, event, reventp, notify, chan)
|
|
*
|
|
* FUNCTION: perform select operation to character or mpx device
|
|
* from the file system
|
|
*
|
|
* PARAMETERS: rdev - device number of device to select on
|
|
* corl - select correlator
|
|
* event - select event
|
|
* reventp - event list
|
|
* notify - function pointer of notify routine for nested polls
|
|
* chan - device channel number
|
|
*
|
|
* RETURN : Errors from d_select()
|
|
*/
|
|
|
|
rdevselect (
|
|
dev_t rdev, /* device number of device */
|
|
int corl, /* select correlator */
|
|
ushort event, /* select event */
|
|
ushort * reventp, /* event list */
|
|
void (* notify)(), /* notify func for nested polls */
|
|
chan_t chan) /* device channel number */
|
|
{
|
|
int rc; /* return code */
|
|
|
|
rc = selreg(corl, rdev, chan, event, notify);
|
|
if (rc == 0)
|
|
DD_ENT(rc =,
|
|
(*devsw[major(rdev)].d_select)(rdev,
|
|
event,
|
|
reventp,
|
|
chan),
|
|
IPRI_BASE,major(rdev));
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* NAME: rdevioctl(rdev, cmd, arg, flag, chan, ext)
|
|
*
|
|
* FUNCTION: Common device ioctl, shared between specfs and devclose.
|
|
*
|
|
* PARAMETERS: rdev - device number of device to issue ioctl for
|
|
* cmd - ioctl command
|
|
* arg - address of parameter block
|
|
* flag - file open flags
|
|
* chan - device channel number
|
|
* ext - extended ioctl information
|
|
*
|
|
* RETURN : Errors from d_ioctl
|
|
*/
|
|
|
|
rdevioctl (
|
|
dev_t rdev, /* dev num of device to ioctl */
|
|
int cmd, /* user specified ioctl command */
|
|
int arg, /* argument for ioctl command */
|
|
long flag, /* file open flags */
|
|
chan_t chan, /* device channel number */
|
|
caddr_t ext) /* extended ioctl information */
|
|
{
|
|
int rc; /* return code */
|
|
|
|
DD_ENT(rc =, (*devsw[major(rdev)].d_ioctl)(rdev,
|
|
cmd,
|
|
arg,
|
|
flag,
|
|
chan,
|
|
ext,
|
|
&u.u_ioctlrv),
|
|
IPRI_BASE,major(rdev));
|
|
|
|
return rc;
|
|
}
|