summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/streamio.c
diff options
context:
space:
mode:
authormeem <none@none>2008-05-08 16:56:26 -0700
committermeem <none@none>2008-05-08 16:56:26 -0700
commitca9327a6de44d69ddab3668cc1e143ce781387a3 (patch)
treee8f80a575694596c82ba7326781eca8437ecc661 /usr/src/uts/common/os/streamio.c
parent7975540170826a816320fa0cf07ba06a66c4252d (diff)
downloadillumos-gate-ca9327a6de44d69ddab3668cc1e143ce781387a3.tar.gz
PSARC/2008/265 STREAMS _I_CMD and pfiles TLI support
4960773 pfiles should show info for TLI endpoints
Diffstat (limited to 'usr/src/uts/common/os/streamio.c')
-rw-r--r--usr/src/uts/common/os/streamio.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/streamio.c b/usr/src/uts/common/os/streamio.c
index d80fa67f56..3fcbf8634b 100644
--- a/usr/src/uts/common/os/streamio.c
+++ b/usr/src/uts/common/os/streamio.c
@@ -143,6 +143,7 @@ static uint32_t ioc_id;
static void putback(struct stdata *, queue_t *, mblk_t *, int);
static void strcleanall(struct vnode *);
static int strwsrv(queue_t *);
+static int strdocmd(struct stdata *, struct strcmd *, cred_t *);
/*
* qinit and module_info structures for stream head read and write queues
@@ -391,6 +392,7 @@ ckreturn:
stp->sd_wroff = 0;
stp->sd_tail = 0;
stp->sd_iocblk = NULL;
+ stp->sd_cmdblk = NULL;
stp->sd_pushcnt = 0;
stp->sd_qn_minpsz = 0;
stp->sd_qn_maxpsz = INFPSZ - 1; /* used to check for initialization */
@@ -811,6 +813,8 @@ strclose(struct vnode *vp, int flag, cred_t *crp)
vp->v_stream = NULL;
mutex_exit(&vp->v_lock);
mutex_enter(&stp->sd_lock);
+ freemsg(stp->sd_cmdblk);
+ stp->sd_cmdblk = NULL;
stp->sd_flag &= ~STRCLOSE;
cv_broadcast(&stp->sd_monitor);
mutex_exit(&stp->sd_lock);
@@ -2124,6 +2128,24 @@ strrput_nondata(queue_t *q, mblk_t *bp)
freemsg(bp);
return (0);
+ case M_CMD:
+ if (MBLKL(bp) != sizeof (cmdblk_t)) {
+ freemsg(bp);
+ return (0);
+ }
+
+ mutex_enter(&stp->sd_lock);
+ if (stp->sd_flag & STRCMDWAIT) {
+ ASSERT(stp->sd_cmdblk == NULL);
+ stp->sd_cmdblk = bp;
+ cv_broadcast(&stp->sd_monitor);
+ mutex_exit(&stp->sd_lock);
+ } else {
+ mutex_exit(&stp->sd_lock);
+ freemsg(bp);
+ }
+ return (0);
+
case M_FLUSH:
/*
* Flush queues. The indication of which queues to flush
@@ -3180,6 +3202,7 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag,
cred_t *crp, int *rvalp)
{
struct stdata *stp;
+ struct strcmd *scp;
struct strioctl strioc;
struct uio uio;
struct iovec iov;
@@ -3552,6 +3575,39 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag,
}
return (error);
+ case _I_CMD:
+ /*
+ * Like I_STR, but without using M_IOC* messages and without
+ * copyins/copyouts beyond the passed-in argument.
+ */
+ if (stp->sd_flag & STRHUP)
+ return (ENXIO);
+
+ if ((scp = kmem_alloc(sizeof (strcmd_t), KM_NOSLEEP)) == NULL)
+ return (ENOMEM);
+
+ if (copyin((void *)arg, scp, sizeof (strcmd_t))) {
+ kmem_free(scp, sizeof (strcmd_t));
+ return (EFAULT);
+ }
+
+ access = job_control_type(scp->sc_cmd);
+ mutex_enter(&stp->sd_lock);
+ if (access != -1 && (error = i_straccess(stp, access)) != 0) {
+ mutex_exit(&stp->sd_lock);
+ kmem_free(scp, sizeof (strcmd_t));
+ return (error);
+ }
+ mutex_exit(&stp->sd_lock);
+
+ *rvalp = 0;
+ if ((error = strdocmd(stp, scp, crp)) == 0) {
+ if (copyout(scp, (void *)arg, sizeof (strcmd_t)))
+ error = EFAULT;
+ }
+ kmem_free(scp, sizeof (strcmd_t));
+ return (error);
+
case I_NREAD:
/*
* Return number of bytes of data in first message
@@ -6321,6 +6377,140 @@ waitioc:
}
/*
+ * Send an M_CMD message downstream and wait for a reply. This is a ptools
+ * special used to retrieve information from modules/drivers a stream without
+ * being subjected to flow control or interfering with pending messages on the
+ * stream (e.g. an ioctl in flight).
+ */
+int
+strdocmd(struct stdata *stp, struct strcmd *scp, cred_t *crp)
+{
+ mblk_t *mp;
+ struct cmdblk *cmdp;
+ int error = 0;
+ int errs = STRHUP|STRDERR|STWRERR|STPLEX;
+ clock_t rval, timeout = STRTIMOUT;
+
+ if (scp->sc_len < 0 || scp->sc_len > sizeof (scp->sc_buf) ||
+ scp->sc_timeout < -1)
+ return (EINVAL);
+
+ if (scp->sc_timeout > 0)
+ timeout = scp->sc_timeout * MILLISEC;
+
+ if ((mp = allocb_cred(sizeof (struct cmdblk), crp)) == NULL)
+ return (ENOMEM);
+
+ crhold(crp);
+
+ cmdp = (struct cmdblk *)mp->b_wptr;
+ cmdp->cb_cr = crp;
+ cmdp->cb_cmd = scp->sc_cmd;
+ cmdp->cb_len = scp->sc_len;
+ cmdp->cb_error = 0;
+ mp->b_wptr += sizeof (struct cmdblk);
+
+ DB_TYPE(mp) = M_CMD;
+ DB_CPID(mp) = curproc->p_pid;
+
+ /*
+ * Copy in the payload.
+ */
+ if (cmdp->cb_len > 0) {
+ mp->b_cont = allocb_cred(sizeof (scp->sc_buf), crp);
+ if (mp->b_cont == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* cb_len comes from sc_len, which has already been checked */
+ ASSERT(cmdp->cb_len <= sizeof (scp->sc_buf));
+ (void) bcopy(scp->sc_buf, mp->b_cont->b_wptr, cmdp->cb_len);
+ mp->b_cont->b_wptr += cmdp->cb_len;
+ DB_CPID(mp->b_cont) = curproc->p_pid;
+ }
+
+ /*
+ * Since this mechanism is strictly for ptools, and since only one
+ * process can be grabbed at a time, we simply fail if there's
+ * currently an operation pending.
+ */
+ mutex_enter(&stp->sd_lock);
+ if (stp->sd_flag & STRCMDWAIT) {
+ mutex_exit(&stp->sd_lock);
+ error = EBUSY;
+ goto out;
+ }
+ stp->sd_flag |= STRCMDWAIT;
+ ASSERT(stp->sd_cmdblk == NULL);
+ mutex_exit(&stp->sd_lock);
+
+ putnext(stp->sd_wrq, mp);
+ mp = NULL;
+
+ /*
+ * Timed wait for acknowledgment. If the reply has already arrived,
+ * don't sleep. If awakened from the sleep, fail only if the reply
+ * has not arrived by then. Otherwise, process the reply.
+ */
+ mutex_enter(&stp->sd_lock);
+ while (stp->sd_cmdblk == NULL) {
+ if (stp->sd_flag & errs) {
+ if ((error = strgeterr(stp, errs, 0)) != 0)
+ goto waitout;
+ }
+
+ rval = str_cv_wait(&stp->sd_monitor, &stp->sd_lock, timeout, 0);
+ if (stp->sd_cmdblk != NULL)
+ break;
+
+ if (rval <= 0) {
+ error = (rval == 0) ? EINTR : ETIME;
+ goto waitout;
+ }
+ }
+
+ /*
+ * We received a reply.
+ */
+ mp = stp->sd_cmdblk;
+ stp->sd_cmdblk = NULL;
+ ASSERT(mp != NULL && DB_TYPE(mp) == M_CMD);
+ ASSERT(stp->sd_flag & STRCMDWAIT);
+ stp->sd_flag &= ~STRCMDWAIT;
+ mutex_exit(&stp->sd_lock);
+
+ cmdp = (struct cmdblk *)mp->b_rptr;
+ if ((error = cmdp->cb_error) != 0)
+ goto out;
+
+ /*
+ * Data may have been returned in the reply (cb_len > 0).
+ * If so, copy it out to the user's buffer.
+ */
+ if (cmdp->cb_len > 0) {
+ if (mp->b_cont == NULL || MBLKL(mp->b_cont) < cmdp->cb_len) {
+ error = EPROTO;
+ goto out;
+ }
+
+ cmdp->cb_len = MIN(cmdp->cb_len, sizeof (scp->sc_buf));
+ (void) bcopy(mp->b_cont->b_rptr, scp->sc_buf, cmdp->cb_len);
+ }
+ scp->sc_len = cmdp->cb_len;
+out:
+ freemsg(mp);
+ crfree(crp);
+ return (error);
+waitout:
+ ASSERT(stp->sd_cmdblk == NULL);
+ stp->sd_flag &= ~STRCMDWAIT;
+ mutex_exit(&stp->sd_lock);
+ crfree(crp);
+ return (error);
+}
+
+/*
* For the SunOS keyboard driver.
* Return the next available "ioctl" sequence number.
* Exported, so that streams modules can send "ioctl" messages