/** * ch_msg: - process message from queue * @q: queue * @mp: the message to process * * This simply flows data messages through with flow control and intercepts * all other messages. */ static inline fastcall __hot int ch_msg(queue_t *q, mblk_t *mp) { struct ch *ch = CH_PRIV(q); if (test_bit(CH_ENABLE_BIT, &ch->flags)) { if (likely(DB_TYPE(mp) == M_DATA)) { if (likely(bcanputnext(q, mp->b_band))) { putnext(q, mp); return (0); } return (-EBUSY); } else if (DB_TYPE(mp) == M_PROTO) { if (*(uint32_t *) mp->b_rptr = CH_DATA_REQ) { if (likely(bcanputnext(q, mp->b_band))) { *(uint32_t *) mp->b_rptr = CH_DATA_IND; putnext(q, mp); return (0); } return (-EBUSY); } /* go slow */ } } return ch_msg_slow(ch, q, mp); }
/* * wput(9E) is symmetric for master and slave sides, so this handles both * without splitting the codepath. * * zc_wput() looks at the other side; if there is no process holding that * side open, it frees the message. This prevents processes from hanging * if no one is holding open the console. Otherwise, it putnext's high * priority messages, putnext's normal messages if possible, and otherwise * enqueues the messages; in the case that something is enqueued, wsrv(9E) * will take care of eventually shuttling I/O to the other side. */ static void zc_wput(queue_t *qp, mblk_t *mp) { unsigned char type = mp->b_datap->db_type; ASSERT(qp->q_ptr); DBG1("entering zc_wput, %s side", zc_side(qp)); if (zc_switch(RD(qp)) == NULL) { DBG1("wput to %s side (no one listening)", zc_side(qp)); switch (type) { case M_FLUSH: handle_mflush(qp, mp); break; case M_IOCTL: miocnak(qp, mp, 0, 0); break; default: freemsg(mp); break; } return; } if (type >= QPCTL) { DBG1("(hipri) wput, %s side", zc_side(qp)); switch (type) { case M_READ: /* supposedly from ldterm? */ DBG("zc_wput: tossing M_READ\n"); freemsg(mp); break; case M_FLUSH: handle_mflush(qp, mp); break; default: /* * Put this to the other side. */ ASSERT(zc_switch(RD(qp)) != NULL); putnext(zc_switch(RD(qp)), mp); break; } DBG1("done (hipri) wput, %s side", zc_side(qp)); return; } /* * Only putnext if there isn't already something in the queue. * otherwise things would wind up out of order. */ if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) { DBG("wput: putting message to other side\n"); putnext(RD(zc_switch(qp)), mp); } else { DBG("wput: putting msg onto queue\n"); (void) putq(qp, mp); } DBG1("done wput, %s side", zc_side(qp)); }
/* * ptem write queue service procedure. */ static void ptemwsrv(queue_t *q) { mblk_t *mp; while ((mp = getq(q)) != NULL) { if (!bcanputnext(q, mp->b_band) || !ptemwmsg(q, mp)) { (void) putbq(q, mp); break; } } }
STATIC streamscall int srvmod_srv(queue_t *q) { mblk_t *mp; while ((mp = getq(q))) { if (likely(mp->b_datap->db_type >= QPCTL || bcanputnext(q, mp->b_band))) { putnext(q, mp); continue; } putbq(q, mp); break; } return (0); }
static int sl_r_msg(queue_t *q, mblk_t *mp) { struct sl *mux = SL_PRIV(q); if (mux->other && mux->other->rq) { if (bcanputnext(mux->other->rq, mp->b_band)) { putnext(mux->other->rq, mp); return (0); } return (-EBUSY); } freemsg(mp); return (0); }
static streamscall int pckt_rput(queue_t *q, mblk_t *mp) { if (mp->b_datap->db_type < QPCTL) { if (q->q_first) goto queue_it; if (q->q_flag & QSVCBUSY) goto queue_it; if (!bcanputnext(q, mp->b_band)) goto queue_it; } if (pckt_r_msg(q, mp)) return (0); queue_it: if (!putq(q, mp)) freemsg(mp); return (0); }
static streamscall int pckt_rsrv(queue_t *q) { mblk_t *mp; while ((mp = getq(q))) { if (mp->b_datap->db_type < QPCTL) { if (!bcanputnext(q, mp->b_band)) goto put_back; } if (pckt_r_msg(q, mp)) continue; put_back: if (!putbq(q, mp)) freemsg(mp); break; } return (0); }
/* * This routine is symmetric for master and slave, so it handles both without * splitting up the codepath. * * If there are messages on this queue that can be sent to the other, send * them via putnext(). Else, if queued messages cannot be sent, leave them * on this queue. */ static void zc_wsrv(queue_t *qp) { mblk_t *mp; DBG1("zc_wsrv master (%s) side", zc_side(qp)); /* * Partner has no read queue, so take the data, and throw it away. */ if (zc_switch(RD(qp)) == NULL) { DBG("zc_wsrv: other side isn't listening"); while ((mp = getq(qp)) != NULL) { if (mp->b_datap->db_type == M_IOCTL) miocnak(qp, mp, 0, 0); else freemsg(mp); } flushq(qp, FLUSHALL); return; } /* * while there are messages on this write queue... */ while ((mp = getq(qp)) != NULL) { /* * Due to the way zc_wput is implemented, we should never * see a control message here. */ ASSERT(mp->b_datap->db_type < QPCTL); if (bcanputnext(RD(zc_switch(qp)), mp->b_band)) { DBG("wsrv: send message to other side\n"); putnext(RD(zc_switch(qp)), mp); } else { DBG("wsrv: putting msg back on queue\n"); (void) putbq(qp, mp); break; } } }
static int sl_w_msg(queue_t *q, mblk_t *mp) { struct sl *mux = SL_PRIV(q); switch (DB_TYPE(mp)) { case M_IOCTL: return sl_w_ioctl(mux, q, mp); case M_IOCDATA: return sl_w_iocdata(mux, q, mp); } if (mux->other && mux->other->rq) { if (bcanputnext(mux->other->rq, mp->b_band)) { putnext(mux->other->rq, mp); return (0); } return (-EBUSY); } freemsg(mp); return (0); }
STATIC streamscall int srvmod_rput(queue_t *q, mblk_t *mp) { if (unlikely(mp->b_datap->db_type == M_FLUSH)) { if (mp->b_rptr[0] & FLUSHR) { if (mp->b_rptr[0] & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHDATA); else flushq(q, FLUSHDATA); } } if (likely(mp->b_datap->db_type >= QPCTL || (q->q_first == NULL && !(q->q_flag & QSVCBUSY) && bcanputnext(q, mp->b_band)))) { putnext(q, mp); return (0); } if (unlikely(putq(q, mp) == 0)) { mp->b_band = 0; putq(q, mp); /* this must succeed */ } return (0); }
STATIC streamscall int srvmod_wput(queue_t *q, mblk_t *mp) { if (unlikely(mp->b_datap->db_type == M_FLUSH)) { if (mp->b_rptr[0] & FLUSHW) { if (mp->b_rptr[0] & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHDATA); else flushq(q, FLUSHDATA); } } if (likely(mp->b_datap->db_type >= QPCTL || (q->q_first == NULL && !(q->q_flag & QSVCBUSY) && bcanputnext(q, mp->b_band)))) { putnext(q, mp); return (0); } /* always buffer, always schedule out of service procedure for testing */ if (unlikely(putq(q, mp) == 0)) { mp->b_band = 0; putq(q, mp); /* this must succeed */ } return (0); }
static struct int sl_w_iocdata(queue_t *q, mblk_t *mp) { struct copyresp *cp = (struct copyresp *) mp->b_rptr; struct sl_mux_ppa sm; unsigned long flags; struct sl_mux *bot, *mux; mblk_t *dp, *bp; switch (cp->cp_cmd) { case SL_SETLINK: switch (mi_copy_state(q, mp, &dp)) { case MI_COPY_CASE(MI_COPY_IN, 1): /* This input-output control is used to set the global-PPA and CLEI associated with a lower multiplex stream. The argument is an sl_mux_ppa structure that contains the multiplex id, the 32-bit PPA, and a CLEI string of up to 32 characters in length. */ bcopy(dp->b_rptr, &sm, sizeof(sm)); /* The assigned global PPA must be non-zero. */ if (sm.sm_ppa == 0) { mi_copy_done(q, mp, EINVAL); return (0); } /* The assigned CLEI must be non-null. */ if (sm.sm_clei[0] == '\0') { mi_copy_done(q, mp, EINVAL); return (0); } write_lock_str(&mux_lock, flags); /* Find the stream with the associated MUXID. */ for (bot = mux_list; bot; bot = bot->next) if (bot->dev == sm.sm_index) break; if (!bot) { write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } /* The global PPA to assign must be unique. */ for (mux = mux_list; mux; mux = mux->next) if (mux != bot && mux->ppa == sm.sm_ppa) break; if (mux) { write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } /* The CLEI to assign must also be unique. */ for (mux = mux_list; mux; mux = mux->next) if (mux != bot && strncmp(mux->clei, sm.sm_clei, 32) == 0) break; if (mux) { write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } bot->ppa = sm.sm_ppa; bcopy(bot->clei, sm.sm_clei, 32); write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, 0); return (0); case -1: return (0); } break; case SL_GETLINK: switch (mi_copy_state(q, mp, &dp)) { case MI_COPY_CASE(MI_COPY_IN, 1): /* This input-output control is used to obtain the multiplex-id assocated with a lower multiplex stream. The argument is an sl_mux_ppa structure that contains a 32-bit PPA or CLEI string of up to 32 characters in length. It returns the multiplex id in the same structure. */ bcopy(dp->b_rptr, &sm, sizeof(sm)); if (sm.sm_ppa != 0) { /* If the supplied global PPA is non-zero, then search on PPA. */ read_lock_str(&mux_lock, flags); for (bot = mux_list; bot; bot = bot->next) if (bot->ppa == sm.sm_ppa) break; if (!bot) { read_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } strncpy(sm.sm_clei, bot->clei, 32); sm.sm_index = bot->dev; read_unlock_str(&mux_lock, flags); if ((bp = mi_copyout_alloc(q, mp, NULL, sizeof(sm)))) { bcopy(&sm, bp->b_rptr, sizeof(sm)); mi_copyout(q, mp); } return (0); } else if (sm.sm_clei[0] != '\0') { /* If the supplied CLEI is non-null, then search on CLEI. */ read_lock_str(&mux_lock, flags); for (bot = mux_list; bot; bot = bot->next) if (strncmp(sm.sm_clei, bot->clei, 32) == 0) break; if (!bot) { read_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } sm.sm_ppa = bot->ppa; sm.sm_index = bot->dev; read_unlock_str(&mux_lock, flags); if ((bp = mi_copyout_alloc(q, mp, NULL, sizeof(sm)))) { bcopy(&sm, bp->b_rptr, sizeof(sm)); mi_copyout(q, mp); } return (0); } else { /* If PPA is zero and CLEI is null, return the first item in the list. */ read_lock_str(&mux_lock, flags); bot = mux_list; if (!bot) { read_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } sm.sm_ppa = bot->ppa; strncpy(sm.sm_clei, bot->clei, 32); sm.sm_index = bot->dev; read_unlock_str(&mux_lock, flags); if ((bp = mi_copyout_alloc(q, mp, NULL, sizeof(sm)))) { bcopy(&sm, bp->b_rptr, sizeof(sm)); mi_copyout(q, mp); } return (0); } case MI_COPY_CASE(MI_COPY_OUT, 1): mi_copy_done(q, mp, 0); return (0); case -1: return (0); } break; default: if (mux->other && mux->other->rq) { if (bcanputnext(mux->other->rq, mp->b_band)) { putnext(mux->other->rq, mp); return (0); } return (-EBUSY); } break; } mi_copy_done(q, mp, EINVAL); return (0); }
/* * wput(9E) is symmetric for master and slave sides, so this handles both * without splitting the codepath. (The only exception to this is the * processing of zcons ioctls, which is restricted to the master side.) * * zc_wput() looks at the other side; if there is no process holding that * side open, it frees the message. This prevents processes from hanging * if no one is holding open the console. Otherwise, it putnext's high * priority messages, putnext's normal messages if possible, and otherwise * enqueues the messages; in the case that something is enqueued, wsrv(9E) * will take care of eventually shuttling I/O to the other side. */ static void zc_wput(queue_t *qp, mblk_t *mp) { unsigned char type = mp->b_datap->db_type; zc_state_t *zcs; struct iocblk *iocbp; file_t *slave_filep; struct snode *slave_snodep; int slave_fd; ASSERT(qp->q_ptr); DBG1("entering zc_wput, %s side", zc_side(qp)); /* * Process zcons ioctl messages if qp is the master console's write * queue. */ zcs = (zc_state_t *)qp->q_ptr; if (zcs->zc_master_rdq != NULL && qp == WR(zcs->zc_master_rdq) && type == M_IOCTL) { iocbp = (struct iocblk *)(void *)mp->b_rptr; switch (iocbp->ioc_cmd) { case ZC_HOLDSLAVE: /* * Hold the slave's vnode and increment the refcount * of the snode. If the vnode is already held, then * indicate success. */ if (iocbp->ioc_count != TRANSPARENT) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode != NULL) { miocack(qp, mp, 0, 0); return; } /* * The process that passed the ioctl must be running in * the global zone. */ if (curzone != global_zone) { miocack(qp, mp, 0, EINVAL); return; } /* * The calling process must pass a file descriptor for * the slave device. */ slave_fd = (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> b_rptr; slave_filep = getf(slave_fd); if (slave_filep == NULL) { miocack(qp, mp, 0, EINVAL); return; } if (ZC_STATE_TO_SLAVEDEV(zcs) != slave_filep->f_vnode->v_rdev) { releasef(slave_fd); miocack(qp, mp, 0, EINVAL); return; } /* * Get a reference to the slave's vnode. Also bump the * reference count on the associated snode. */ ASSERT(vn_matchops(slave_filep->f_vnode, spec_getvnodeops())); zcs->zc_slave_vnode = slave_filep->f_vnode; VN_HOLD(zcs->zc_slave_vnode); slave_snodep = VTOCS(zcs->zc_slave_vnode); mutex_enter(&slave_snodep->s_lock); ++slave_snodep->s_count; mutex_exit(&slave_snodep->s_lock); releasef(slave_fd); miocack(qp, mp, 0, 0); return; case ZC_RELEASESLAVE: /* * Release the master's handle on the slave's vnode. * If there isn't a handle for the vnode, then indicate * success. */ if (iocbp->ioc_count != TRANSPARENT) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode == NULL) { miocack(qp, mp, 0, 0); return; } /* * The process that passed the ioctl must be running in * the global zone. */ if (curzone != global_zone) { miocack(qp, mp, 0, EINVAL); return; } /* * The process that passed the ioctl must have provided * a file descriptor for the slave device. Make sure * this is correct. */ slave_fd = (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> b_rptr; slave_filep = getf(slave_fd); if (slave_filep == NULL) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode->v_rdev != slave_filep->f_vnode->v_rdev) { releasef(slave_fd); miocack(qp, mp, 0, EINVAL); return; } /* * Decrement the snode's reference count and release the * vnode. */ ASSERT(vn_matchops(slave_filep->f_vnode, spec_getvnodeops())); slave_snodep = VTOCS(zcs->zc_slave_vnode); mutex_enter(&slave_snodep->s_lock); --slave_snodep->s_count; mutex_exit(&slave_snodep->s_lock); VN_RELE(zcs->zc_slave_vnode); zcs->zc_slave_vnode = NULL; releasef(slave_fd); miocack(qp, mp, 0, 0); return; default: break; } } if (zc_switch(RD(qp)) == NULL) { DBG1("wput to %s side (no one listening)", zc_side(qp)); switch (type) { case M_FLUSH: handle_mflush(qp, mp); break; case M_IOCTL: miocnak(qp, mp, 0, 0); break; default: freemsg(mp); break; } return; } if (type >= QPCTL) { DBG1("(hipri) wput, %s side", zc_side(qp)); switch (type) { case M_READ: /* supposedly from ldterm? */ DBG("zc_wput: tossing M_READ\n"); freemsg(mp); break; case M_FLUSH: handle_mflush(qp, mp); break; default: /* * Put this to the other side. */ ASSERT(zc_switch(RD(qp)) != NULL); putnext(zc_switch(RD(qp)), mp); break; } DBG1("done (hipri) wput, %s side", zc_side(qp)); return; } /* * Only putnext if there isn't already something in the queue. * otherwise things would wind up out of order. */ if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) { DBG("wput: putting message to other side\n"); putnext(RD(zc_switch(qp)), mp); } else { DBG("wput: putting msg onto queue\n"); (void) putq(qp, mp); } DBG1("done wput, %s side", zc_side(qp)); }
/** * sl_w_ioctl: - process M_IOCTL message * @q: active queue (upper write queue) * @mp: the message * * Linking of streams: streams are linked under the multiplexing driver by opening an upper stream * and then linking a signalling link stream under the multiplexing driver. Then the SL_SETLINK * input-output control is used with the multiplexer id to set the global-PPA and CLEI associated * with the signalling link. The SL_GETLINK input-output control can be used at a later date to * determine the multiplexer id for a given signalling link stream. */ static struct int sl_w_ioctl(queue_t *q, mblk_t *mp) { struct iocblk *ioc = (struct iocblk *) mp->b_rptr; switch (ioc->ioc_cmd) { case I_LINK: case I_PLINK: { struct linkblk *l; if (!mp->b_cont) mi_copy_done(q, mp, EINVAL); l = (struct linkblk *) mp->b_cont->b_rptr; if (!(bot = kmem_alloc(sizeof(*bot), KM_NOSLEEP))) mi_copy_done(q, mp, ENOMEM); write_lock_str(&mux_lock, flags); bot->next = mux_links; bpt->prev = &mux_links; mux_links = bot; bot->dev = l->l_index; bot->rq = RD(l->l_qtop); bot->wq = l->l_qtop; bot->other = NULL; noenable(bot->rq); l->l_qtop->q_ptr = RD(l->l_qtop)->q_ptr = (void *) bot; write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, 0); return (0); } case I_UNLINK: case I_PUNLINK: { struct linkblk *l; if (!mp->b_cont) mi_copy_done(q, mp, EINVAL); l = (struct linkblk *) mp->b_cont->b_rptr; write_lock_str(&mux_lock, flags); for (bot = mux_list; bot; bot = bot->next) if (bot->dev == l->l_index) break; if (!bot) { write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, EINVAL); return (0); } /* Note that the lower multiplex driver put and service procedures must be prepared to be invoked event after the M_IOCACK for the I_UNLINK or I_PUNLINK ioctl has been returned. THis is because the setq(9) back to the Stream head is not performed until after the acknowledgement has been received. We set q->q_ptr to a null multiplex structure to keep the lower Stream functioning until the setq(9) is performed. */ l->l_qtop->q_ptr = RD(l->l_qtop)->q_ptr = &no_mux; if ((*bot->prev = bot->next)) { bot->next = NULL; bot->prev = &bot->next; } bot->other = NULL; kmem_free(bot, sizeof(*bot)); /* hang up all upper streams that feed this lower stream */ for (top = mux_opens; top; top = top->next) { if (top->other == bot) { putnextctl(top->rq, M_HANGUP); top->other = NULL; } } write_unlock_str(&mux_lock, flags); mi_copy_done(q, mp, 0); return (0); } case SL_SETLINK: { struct sl_mux_ppa *sm; /* This input-output control is used to set the global-PPA and CLEI associated with a lower multiplex stream. The argument is an sl_mux_ppa structure that contains the multiplex id, the 32-bit PPA, and a CLEI string of up to 32 characters in length. */ mi_copyin(q, mp, NULL, sizeof(struct sl_mux_ppa)); return (0); } case SL_GETLINK: { /* This input-output control is used to obtain the multiplex-id assocated with a lower multiplex stream. The argument is an sl_mux_ppa structure that contains a 32-bit PPA or CLEI string of up to 32 characters in length. It returns the multiplex id in the same structure. */ mi_copyin(q, mp, NULL, sizeof(struct sl_mux_ppa)); return (0); } default: if (mux->other && mux->other->rq) { if (bcanputnext(mux->other->rq, mp->b_band)) { putnext(mux->other->rq, mp); return (0); } return (-EBUSY); } break; } mi_copy_done(q, mp, EINVAL); return (0); }
/* * This routine is called from both ptemwput and ptemwsrv to do the * actual work of dealing with mp. ptmewput will have already * dealt with high priority messages. * * Return 1 if the message was processed completely and 0 if not. */ static int ptemwmsg(queue_t *q, mblk_t *mp) { struct ptem *ntp = (struct ptem *)q->q_ptr; struct iocblk *iocp; /* outgoing ioctl structure */ struct termio *termiop; struct termios *termiosp; mblk_t *dack_ptr; /* disconnect message ACK block */ mblk_t *pckt_msgp; /* message sent to the PCKT module */ mblk_t *dp; /* ioctl reply data */ tcflag_t cflags; int error; switch (mp->b_datap->db_type) { case M_IOCTL: /* * Note: for each "set" type operation a copy * of the M_IOCTL message is made and passed * downstream. Eventually the PCKT module, if * it has been pushed, should pick up this message. * If the PCKT module has not been pushed the master * side stream head will free it. */ iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { case TCSETAF: case TCSETSF: /* * Flush the read queue. */ if (putnextctl1(q, M_FLUSH, FLUSHR) == 0) { miocnak(q, mp, 0, EAGAIN); break; } /* FALLTHROUGH */ case TCSETA: case TCSETAW: case TCSETS: case TCSETSW: switch (iocp->ioc_cmd) { case TCSETAF: case TCSETA: case TCSETAW: error = miocpullup(mp, sizeof (struct termio)); if (error != 0) { miocnak(q, mp, 0, error); goto out; } cflags = ((struct termio *) mp->b_cont->b_rptr)->c_cflag; ntp->cflags = (ntp->cflags & 0xffff0000 | cflags); break; case TCSETSF: case TCSETS: case TCSETSW: error = miocpullup(mp, sizeof (struct termios)); if (error != 0) { miocnak(q, mp, 0, error); goto out; } cflags = ((struct termios *) mp->b_cont->b_rptr)->c_cflag; ntp->cflags = cflags; break; } if ((cflags & CBAUD) == B0) { /* * Hang-up: Send a zero length message. */ dack_ptr = ntp->dack_ptr; if (dack_ptr) { ntp->dack_ptr = NULL; /* * Send a zero length message * downstream. */ putnext(q, dack_ptr); } } else { /* * Make a copy of this message and pass it on * to the PCKT module. */ if ((pckt_msgp = copymsg(mp)) == NULL) { miocnak(q, mp, 0, EAGAIN); break; } putnext(q, pckt_msgp); } /* * Send ACK upstream. */ mioc2ack(mp, NULL, 0, 0); qreply(q, mp); out: break; case TCGETA: dp = allocb(sizeof (struct termio), BPRI_MED); if (dp == NULL) { miocnak(q, mp, 0, EAGAIN); break; } termiop = (struct termio *)dp->b_rptr; termiop->c_cflag = (ushort_t)ntp->cflags; mioc2ack(mp, dp, sizeof (struct termio), 0); qreply(q, mp); break; case TCGETS: dp = allocb(sizeof (struct termios), BPRI_MED); if (dp == NULL) { miocnak(q, mp, 0, EAGAIN); break; } termiosp = (struct termios *)dp->b_rptr; termiosp->c_cflag = ntp->cflags; mioc2ack(mp, dp, sizeof (struct termios), 0); qreply(q, mp); break; case TCSBRK: error = miocpullup(mp, sizeof (int)); if (error != 0) { miocnak(q, mp, 0, error); break; } /* * Need a copy of this message to pass it on to * the PCKT module. */ if ((pckt_msgp = copymsg(mp)) == NULL) { miocnak(q, mp, 0, EAGAIN); break; } /* * Send a copy of the M_IOCTL to the PCKT module. */ putnext(q, pckt_msgp); /* * TCSBRK meaningful if data part of message is 0 * cf. termio(7). */ if (!(*(int *)mp->b_cont->b_rptr)) (void) putnextctl(q, M_BREAK); /* * ACK the ioctl. */ mioc2ack(mp, NULL, 0, 0); qreply(q, mp); break; case JWINSIZE: case TIOCGWINSZ: case TIOCSWINSZ: ptioc(q, mp, WRSIDE); break; case TIOCSTI: /* * Simulate typing of a character at the terminal. In * all cases, we acknowledge the ioctl and pass a copy * of it along for the PCKT module to encapsulate. If * not in remote mode, we also process the ioctl * itself, looping the character given as its argument * back around to the read side. */ /* * Need a copy of this message to pass on to the PCKT * module. */ if ((pckt_msgp = copymsg(mp)) == NULL) { miocnak(q, mp, 0, EAGAIN); break; } if ((ntp->state & REMOTEMODE) == 0) { mblk_t *bp; error = miocpullup(mp, sizeof (char)); if (error != 0) { freemsg(pckt_msgp); miocnak(q, mp, 0, error); break; } /* * The permission checking has already been * done at the stream head, since it has to be * done in the context of the process doing * the call. */ if ((bp = allocb(1, BPRI_MED)) == NULL) { freemsg(pckt_msgp); miocnak(q, mp, 0, EAGAIN); break; } /* * XXX: Is EAGAIN really the right response to * flow control blockage? */ if (!bcanputnext(RD(q), mp->b_band)) { freemsg(bp); freemsg(pckt_msgp); miocnak(q, mp, 0, EAGAIN); break; } *bp->b_wptr++ = *mp->b_cont->b_rptr; qreply(q, bp); } putnext(q, pckt_msgp); mioc2ack(mp, NULL, 0, 0); qreply(q, mp); break; case PTSSTTY: if (ntp->state & IS_PTSTTY) { miocnak(q, mp, 0, EEXIST); } else { ntp->state |= IS_PTSTTY; mioc2ack(mp, NULL, 0, 0); qreply(q, mp); } break; default: /* * End of the line. The slave driver doesn't see any * ioctls that we don't explicitly pass along to it. */ miocnak(q, mp, 0, EINVAL); break; } break; case M_DELAY: /* tty delays not supported */ freemsg(mp); break; case M_DATA: if ((mp->b_wptr - mp->b_rptr) < 0) { /* * Free all bad length messages. */ freemsg(mp); break; } else if ((mp->b_wptr - mp->b_rptr) == 0) { if (!(ntp->state & IS_PTSTTY)) { freemsg(mp); break; } } if (ntp->state & OFLOW_CTL) return (0); default: putnext(q, mp); break; } return (1); }
/** * ptem_w_msg - process a message on the write side * @q: write queue * @mp: message to process * * Returns 1 when the caller (putp or srvp) needs to queue or requeue the * message. Returns 0 when the message has been disposed and the caller must * release its reference to mp. * * Keep this function out of the way of the fastpath. */ static streams_noinline int ptem_w_msg(queue_t *q, mblk_t *mp) { struct ptem *p = PTEM_PRIV(q); /* fast path */ if (likely(mp->b_datap->db_type == M_DATA)) { m_data: if ((p->flags & PTEM_OUTPUT_STOPPED) || (q->q_first != NULL) || (q->q_flag & QSVCBUSY) || (!bcanputnext(q, mp->b_band))) return (1); putnext(q, mp); return (0); } switch (mp->b_datap->db_type) { case M_DATA: goto m_data; case M_IOCTL: { struct iocblk *ioc = (struct iocblk *) mp->b_rptr; int error = EINVAL; int rval = 0; int count = 0; mblk_t *bp, *cp; /* The Stream head is set to recognized all transparent terminal input-output controls and pass them downstream as though they were I_STR input-output controls. There is also the opportunity to register input-output controls with the Stream head using the TIOC_REPLY message. */ if (ioc->ioc_count == TRANSPARENT) { __swerr(); goto nak; } if ((bp = mp->b_cont) == NULL) goto nak; switch (ioc->ioc_cmd) { case TCSETAF: /* Note, if properly handled the M_FLUSH message will never be queued and upon successful return from this function, we have already processed the read-side flush along the entire Stream. */ if (!putnextctl1(q, M_FLUSH, FLUSHR)) { error = EAGAIN; goto nak; } /* fall through */ case TCSETAW: /* Note, output should have already drained. */ /* fall through */ case TCSETA: { struct termio *c; mblk_t *zp; if (!pullupmsg(bp, sizeof(struct termio))) goto nak; c = (typeof(c)) bp->b_rptr; if ((c->c_cflag & CBAUD) == B0) { /* slave hangup */ if ((zp = xchg(&p->zero, NULL))) putnext(q, zp); } else { if (!(cp = copymsg(mp))) { error = EAGAIN; goto nak; } p->c.c_iflag = (p->c.c_iflag & 0xffff0000) | c->c_iflag; p->c.c_oflag = (p->c.c_oflag & 0xffff0000) | c->c_oflag; p->c.c_cflag = (p->c.c_cflag & 0xffff0000) | c->c_cflag; p->c.c_lflag = (p->c.c_lflag & 0xffff0000) | c->c_lflag; p->c.c_line = c->c_line; bcopy(c->c_cc, p->c.c_cc, NCC); putnext(q, cp); } goto ack; } case TCSETSF: /* Note, if properly handled the M_FLUSH message will never be queued and upon successful return from this function, we have already processed the read-side flush along the entire Stream. */ if (!putnextctl1(q, M_FLUSH, FLUSHR)) { error = EAGAIN; goto nak; } /* fall through */ case TCSETSW: /* Note, output should have already drained. */ /* fall through */ case TCSETS: { struct termios *c; mblk_t *zp; if (!pullupmsg(bp, sizeof(struct termios))) goto nak; c = (typeof(c)) bp->b_rptr; if ((c->c_cflag & CBAUD) == B0) { /* slave hangup */ if ((zp = xchg(&p->zero, NULL))) putnext(q, zp); } else { if (!(cp = copymsg(mp))) { error = EAGAIN; goto nak; } p->c = *c; putnext(q, cp); } goto ack; } case TCGETA: { struct termio *c; extern void __struct_termio_is_too_large_for_fastbuf(void); if (FASTBUF < sizeof(struct termio)) __struct_termio_is_too_large_for_fastbuf(); count = sizeof(*c); bp->b_rptr = bp->b_datap->db_base; bp->b_wptr = bp->b_rptr + count; c = (typeof(c)) bp->b_rptr; c->c_iflag = p->c.c_iflag; c->c_oflag = p->c.c_oflag; c->c_cflag = p->c.c_cflag; c->c_lflag = p->c.c_lflag; c->c_line = p->c.c_line; bcopy(p->c.c_cc, p->c.c_cc, NCC); goto ack; } case TCGETS: { extern void __struct_termios_is_too_large_for_fastbuf(void); if (FASTBUF < sizeof(struct termios)) __struct_termios_is_too_large_for_fastbuf(); count = sizeof(p->c); bp->b_rptr = bp->b_datap->db_base; bp->b_wptr = bp->b_rptr + count; *((struct termios *) bp->b_rptr) = p->c; goto ack; } case TIOCGWINSZ: { extern void __struct_winsize_is_too_large_for_fastbuf(void); if (!(p->flags & PTEM_HAVE_WINSIZE)) goto nak; if (FASTBUF < sizeof(struct winsize)) __struct_winsize_is_too_large_for_fastbuf(); count = sizeof(p->ws); bp->b_rptr = bp->b_datap->db_base; bp->b_wptr = bp->b_rptr + count; *((struct winsize *) bp->b_rptr) = p->ws; goto ack; } #ifdef JWINSIZE case JWINSIZE: { struct jwinsize *jws; extern void __struct_jwinsize_is_too_large_for_fastbuf(void); if (!(p->flags & PTEM_HAVE_WINSIZE)) goto nak; if (FASTBUF < sizeof(struct jwinsize)) __struct_jwinsize_is_too_large_for_fastbuf(); /* always have room in a fastbuf */ count = sizeof(*jws); bp->b_rptr = bp->b_datap->db_base; bp->b_wptr = bp->b_rptr + count; jws = (typeof(jws)) bp->b_rptr; jws->bytesx = p->ws.ws_col; jws->bytesy = p->ws.ws_row; jws->bitsx = p->ws.ws_xpixel; jws->bitsy = p->ws.ws_ypixel; goto ack; } #endif /* JWINSIZE */ case TIOCSWINSZ: { struct winsize *ws; int changed = 0; int zeroed = !(p->flags & PTEM_HAVE_WINSIZE); mblk_t *mb; if (!pullupmsg(bp, sizeof(*ws))) goto nak; if (!(cp = copymsg(mp))) { error = EAGAIN; goto nak; } if (!(mb = allocb(1, BPRI_MED))) { freemsg(cp); error = EAGAIN; goto nak; } ws = (typeof(ws)) bp->b_rptr; if (ws->ws_col != p->ws.ws_col) { if ((p->ws.ws_col = ws->ws_col)) zeroed = 0; changed = 1; } if (ws->ws_row != p->ws.ws_row) { if ((p->ws.ws_row = ws->ws_row)) zeroed = 0; changed = 1; } if (ws->ws_xpixel != p->ws.ws_xpixel) { if ((p->ws.ws_xpixel = ws->ws_xpixel)) zeroed = 0; changed = 1; } if (ws->ws_ypixel != p->ws.ws_ypixel) { if ((p->ws.ws_ypixel = ws->ws_ypixel)) zeroed = 0; changed = 1; } if (zeroed) p->flags &= ~PTEM_HAVE_WINSIZE; else p->flags |= PTEM_HAVE_WINSIZE; if (changed) { mb->b_datap->db_type = M_SIG; *mb->b_wptr++ = SIGWINCH; qreply(q, mb); } else freeb(mb); putnext(q, cp); /* copy for pctk(4) */ count = 0; goto ack; } case TCSBRK: if (!(cp = copymsg(mp))) { error = EAGAIN; goto nak; } putnext(q, cp); count = 0; goto ack; default: goto nak; } break; ack: mp->b_datap->db_type = M_IOCACK; ioc->ioc_error = 0; ioc->ioc_rval = rval; ioc->ioc_count = count; goto reply; nak: mp->b_datap->db_type = M_IOCNAK; ioc->ioc_error = error; ioc->ioc_rval = -1; ioc->ioc_count = 0; reply: qreply(q, mp); break; } case M_FLUSH: if (mp->b_rptr[0] & FLUSHW) { if (mp->b_rptr[0] & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHDATA); else flushq(q, FLUSHDATA); } putnext(q, mp); break; default: if (mp->b_datap->db_type < QPCTL) { if ((q->q_first != NULL) || (q->q_flag & QSVCBUSY) || (!bcanputnext(q, mp->b_band))) return (1); /* (re)queue */ } putnext(q, mp); break; } return (0); }
/* * ptemwput - Module write queue put procedure. * * This is called from the module or stream head upstream. * * XXX: This routine is quite lazy about handling allocation failures, * basically just giving up and reporting failure. It really ought to * set up bufcalls and only fail when it's absolutely necessary. */ static void ptemwput(queue_t *q, mblk_t *mp) { struct ptem *ntp = (struct ptem *)q->q_ptr; struct iocblk *iocp; /* outgoing ioctl structure */ struct copyresp *resp; unsigned char type = mp->b_datap->db_type; if (type >= QPCTL) { switch (type) { case M_IOCDATA: resp = (struct copyresp *)mp->b_rptr; if (resp->cp_rval) { /* * Just free message on failure. */ freemsg(mp); break; } /* * Only need to copy data for the SET case. */ switch (resp->cp_cmd) { case TIOCSWINSZ: ptioc(q, mp, WRSIDE); break; case JWINSIZE: case TIOCGWINSZ: mioc2ack(mp, NULL, 0, 0); qreply(q, mp); break; default: freemsg(mp); } break; case M_FLUSH: if (*mp->b_rptr & FLUSHW) { if ((ntp->state & IS_PTSTTY) && (*mp->b_rptr & FLUSHBAND)) flushband(q, *(mp->b_rptr + 1), FLUSHDATA); else flushq(q, FLUSHDATA); } putnext(q, mp); break; case M_READ: freemsg(mp); break; case M_STOP: /* * Set the output flow control state. */ ntp->state |= OFLOW_CTL; putnext(q, mp); break; case M_START: /* * Relieve the output flow control state. */ ntp->state &= ~OFLOW_CTL; putnext(q, mp); qenable(q); break; default: putnext(q, mp); break; } return; } /* * If our queue is nonempty or flow control persists * downstream or module in stopped state, queue this message. */ if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) { /* * Exception: ioctls, except for those defined to * take effect after output has drained, should be * processed immediately. */ switch (type) { case M_IOCTL: iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { /* * Queue these. */ case TCSETSW: case TCSETSF: case TCSETAW: case TCSETAF: case TCSBRK: break; /* * Handle all others immediately. */ default: (void) ptemwmsg(q, mp); return; } break; case M_DELAY: /* tty delays not supported */ freemsg(mp); return; case M_DATA: if ((mp->b_wptr - mp->b_rptr) < 0) { /* * Free all bad length messages. */ freemsg(mp); return; } else if ((mp->b_wptr - mp->b_rptr) == 0) { if (!(ntp->state & IS_PTSTTY)) { freemsg(mp); return; } } } (void) putq(q, mp); return; } /* * fast path into ptemwmsg to dispose of mp. */ if (!ptemwmsg(q, mp)) (void) putq(q, mp); }
static streamscall __hot_put int ptem_wput(queue_t *q, mblk_t *mp) { struct ptem *p = PTEM_PRIV(q); /* fast path */ if (likely(mp->b_datap->db_type == M_DATA)) { m_data: /* free zero-length messages */ if (msgdsize(mp) != 0) { if ((p->flags & PTEM_OUTPUT_STOPPED) || (q->q_first != NULL) || (q->q_flag & QSVCBUSY) || (!bcanputnext(q, mp->b_band))) { /* Note, the only reason for failinng putq() is the lack of a queue band, in which case the band is empty and no loss of order will result from putting it to the next queue. */ if (putq(q, mp)) return (0); } putnext(q, mp); return (0); } freemsg(mp); return (0); } switch (mp->b_datap->db_type) { case M_DATA: goto m_data; case M_IOCTL: { struct iocblk *ioc = (struct iocblk *) mp->b_rptr; /* The Stream head is set to recognized all transparent terminal input-output controls and pass them downstream as though they were I_STR input-output controls. There is also the opportunity to register input-output controls with the Stream head using the TIOC_REPLY message. */ if (unlikely(ioc->ioc_count == TRANSPARENT)) goto do_it; switch (ioc->ioc_cmd) { case TCSETAW: case TCSETAF: case TCSETSW: case TCSETSF: case TCSBRK: /* These need to wait for the output to drain before being processed, queue them. */ putq(q, mp); break; default: /* Process others immediately, regardless of whether there is any data or other messages in queue. */ goto do_it; } break; } case M_DELAY: case M_READ: freemsg(mp); break; case M_STOP: if (canenable(q)) { noenable(q); p->flags |= PTEM_OUTPUT_STOPPED; } putnext(q, mp); break; case M_START: if (!canenable(q)) { p->flags &= ~PTEM_OUTPUT_STOPPED; enableok(q); qenable(q); } putnext(q, mp); break; case M_STOPI: case M_STARTI: /* We have no read side queue so we cannot queue in this direction. Tell master so that pckt(4) can tell master not to send anything more. */ putnext(q, mp); break; default: do_it: if (ptem_w_msg(q, mp) && !putq(q, mp)) freemsg(mp); break; } return (0); }