Files
Arquivotheca.AIX-4.1.3/bos/kernel/specfs/devsubs.c
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

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;
}