#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/logindmux.h>
#include <sys/logindmux_impl.h>
#include <sys/stat.h>
#include <sys/kmem.h>
#include <sys/vmem.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/mkdev.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/termios.h>
#include <sys/cmn_err.h>
static int logdmuxopen(queue_t *, dev_t *, int, int, cred_t *);
static int logdmuxclose(queue_t *, int, cred_t *);
static int logdmuxursrv(queue_t *);
static int logdmuxuwput(queue_t *, mblk_t *);
static int logdmuxlrput(queue_t *, mblk_t *);
static int logdmuxlrsrv(queue_t *);
static int logdmuxlwsrv(queue_t *);
static int logdmuxuwsrv(queue_t *);
static int logdmux_alloc_unlinkinfo(struct tmx *, struct tmx *);
static void logdmuxlink(queue_t *, mblk_t *);
static void logdmuxunlink(queue_t *, mblk_t *);
static void logdmux_finish_unlink(queue_t *, mblk_t *);
static void logdmux_unlink_timer(void *arg);
static void recover(queue_t *, mblk_t *, size_t);
static void flushq_dataonly(queue_t *);
static kmutex_t logdmux_qexch_lock;
static kmutex_t logdmux_peerq_lock;
static kmutex_t logdmux_minor_lock;
static minor_t logdmux_maxminor = 256;
static vmem_t *logdmux_minor_arena;
static void *logdmux_statep;
static struct module_info logdmuxm_info = {
LOGDMX_ID,
"logindmux",
0,
256,
512,
256
};
static struct qinit logdmuxurinit = {
NULL,
logdmuxursrv,
logdmuxopen,
logdmuxclose,
NULL,
&logdmuxm_info
};
static struct qinit logdmuxuwinit = {
logdmuxuwput,
logdmuxuwsrv,
NULL,
NULL,
NULL,
&logdmuxm_info
};
static struct qinit logdmuxlrinit = {
logdmuxlrput,
logdmuxlrsrv,
NULL,
NULL,
NULL,
&logdmuxm_info
};
static struct qinit logdmuxlwinit = {
NULL,
logdmuxlwsrv,
NULL,
NULL,
NULL,
&logdmuxm_info
};
struct streamtab logdmuxinfo = {
&logdmuxurinit,
&logdmuxuwinit,
&logdmuxlrinit,
&logdmuxlwinit
};
static int logdmux_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int logdmux_attach(dev_info_t *, ddi_attach_cmd_t);
static int logdmux_detach(dev_info_t *, ddi_detach_cmd_t);
static dev_info_t *logdmux_dip;
DDI_DEFINE_STREAM_OPS(logdmux_ops, nulldev, nulldev, logdmux_attach,
logdmux_detach, nulldev, logdmux_info, D_MP | D_MTPERQ, &logdmuxinfo,
ddi_quiesce_not_needed);
static struct modldrv modldrv = {
&mod_driverops,
"logindmux driver",
&logdmux_ops
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, NULL
};
int
_init(void)
{
int ret;
mutex_init(&logdmux_peerq_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&logdmux_qexch_lock, NULL, MUTEX_DRIVER, NULL);
if ((ret = mod_install(&modlinkage)) != 0) {
mutex_destroy(&logdmux_peerq_lock);
mutex_destroy(&logdmux_qexch_lock);
return (ret);
}
logdmux_minor_arena = vmem_create("logdmux_minor", (void *)1,
logdmux_maxminor, 1, NULL, NULL, NULL, 0,
VM_SLEEP | VMC_IDENTIFIER);
(void) ddi_soft_state_init(&logdmux_statep, sizeof (struct tmx), 1);
return (0);
}
int
_fini(void)
{
int ret;
if ((ret = mod_remove(&modlinkage)) == 0) {
mutex_destroy(&logdmux_peerq_lock);
mutex_destroy(&logdmux_qexch_lock);
ddi_soft_state_fini(&logdmux_statep);
vmem_destroy(logdmux_minor_arena);
logdmux_minor_arena = NULL;
}
return (ret);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
logdmux_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (ddi_create_minor_node(devi, "logindmux", S_IFCHR, 0, DDI_PSEUDO,
CLONE_DEV) == DDI_FAILURE)
return (DDI_FAILURE);
logdmux_dip = devi;
return (DDI_SUCCESS);
}
static int
logdmux_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
ddi_remove_minor_node(devi, NULL);
return (DDI_SUCCESS);
}
static int
logdmux_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (logdmux_dip == NULL) {
error = DDI_FAILURE;
} else {
*result = logdmux_dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
logdmuxopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
struct tmx *tmxp;
minor_t minor, omaxminor;
if (sflag != CLONEOPEN)
return (EINVAL);
mutex_enter(&logdmux_minor_lock);
if (vmem_size(logdmux_minor_arena, VMEM_FREE) == 0) {
if (logdmux_maxminor == MAXMIN) {
mutex_exit(&logdmux_minor_lock);
return (ENOMEM);
}
omaxminor = logdmux_maxminor;
logdmux_maxminor = MIN(logdmux_maxminor << 1, MAXMIN);
(void) vmem_add(logdmux_minor_arena,
(void *)(uintptr_t)(omaxminor + 1),
logdmux_maxminor - omaxminor, VM_SLEEP);
}
minor = (minor_t)(uintptr_t)
vmem_alloc(logdmux_minor_arena, 1, VM_SLEEP);
mutex_exit(&logdmux_minor_lock);
if (ddi_soft_state_zalloc(logdmux_statep, minor) == DDI_FAILURE) {
vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
return (ENOMEM);
}
tmxp = ddi_get_soft_state(logdmux_statep, minor);
tmxp->rdq = q;
tmxp->muxq = NULL;
tmxp->peerq = NULL;
tmxp->unlinkinfop = NULL;
tmxp->dev0 = minor;
*devp = makedevice(getmajor(*devp), tmxp->dev0);
q->q_ptr = tmxp;
WR(q)->q_ptr = tmxp;
qprocson(q);
return (0);
}
static int
logdmuxclose(queue_t *q, int flag, cred_t *crp)
{
struct tmx *tmxp = q->q_ptr;
minor_t minor = tmxp->dev0;
ASSERT(tmxp->muxq == NULL);
ASSERT(tmxp->peerq == NULL);
qprocsoff(q);
if (tmxp->wbufcid != 0) {
qunbufcall(q, tmxp->wbufcid);
tmxp->wbufcid = 0;
}
if (tmxp->rbufcid != 0) {
qunbufcall(q, tmxp->rbufcid);
tmxp->rbufcid = 0;
}
if (tmxp->rtimoutid != 0) {
(void) quntimeout(q, tmxp->rtimoutid);
tmxp->rtimoutid = 0;
}
if (tmxp->wtimoutid != 0) {
(void) quntimeout(q, tmxp->wtimoutid);
tmxp->wtimoutid = 0;
}
if (tmxp->utimoutid != 0) {
(void) quntimeout(q, tmxp->utimoutid);
tmxp->utimoutid = 0;
}
mutex_enter(&logdmux_qexch_lock);
ddi_soft_state_free(logdmux_statep, minor);
vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
mutex_exit(&logdmux_qexch_lock);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
return (0);
}
static int
logdmuxursrv(queue_t *q)
{
struct tmx *tmxp = q->q_ptr;
if (tmxp->muxq != NULL)
qenable(RD(tmxp->muxq));
return (0);
}
static int
logdmuxuwput(queue_t *q, mblk_t *mp)
{
queue_t *qp;
mblk_t *newmp;
struct iocblk *ioc;
minor_t minor;
STRUCT_HANDLE(protocol_arg, protoh);
struct tmx *tmxp, *tmxpeerp;
int error;
tmxp = q->q_ptr;
switch (mp->b_datap->db_type) {
case M_IOCTL:
ASSERT(MBLKL(mp) == sizeof (struct iocblk));
ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case LOGDMX_IOC_QEXCHANGE:
error = miocpullup(mp,
SIZEOF_STRUCT(protocol_arg, ioc->ioc_flag));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
STRUCT_SET_HANDLE(protoh, ioc->ioc_flag,
(struct protocol_arg *)mp->b_cont->b_rptr);
#ifdef _SYSCALL32_IMPL
if ((ioc->ioc_flag & DATAMODEL_MASK) ==
DATAMODEL_ILP32) {
minor = getminor(expldev(
STRUCT_FGET(protoh, dev)));
} else
#endif
{
minor = getminor(STRUCT_FGET(protoh, dev));
}
if ((int)minor < 0) {
miocnak(q, mp, 0, EINVAL);
break;
}
mutex_enter(&logdmux_qexch_lock);
tmxpeerp = ddi_get_soft_state(logdmux_statep, minor);
if (tmxpeerp == NULL || tmxpeerp == tmxp ||
tmxpeerp->muxq == NULL || tmxpeerp->peerq != NULL ||
tmxp->muxq == NULL || tmxp->peerq != NULL) {
mutex_exit(&logdmux_qexch_lock);
miocnak(q, mp, 0, EINVAL);
break;
}
if (STRUCT_FGET(protoh, flag)) {
if ((error = logdmux_alloc_unlinkinfo(
tmxp, tmxpeerp)) != 0) {
mutex_exit(&logdmux_qexch_lock);
miocnak(q, mp, 0, error);
break;
}
tmxp->peerq = tmxpeerp->muxq;
tmxpeerp->peerq = tmxp->muxq;
tmxp->isptm = B_TRUE;
}
mutex_exit(&logdmux_qexch_lock);
miocack(q, mp, 0, 0);
break;
case I_LINK:
ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
logdmuxlink(q, mp);
break;
case I_UNLINK:
ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
logdmuxunlink(q, mp);
break;
default:
if (tmxp->muxq == NULL) {
miocnak(q, mp, 0, EINVAL);
return (0);
}
putnext(tmxp->muxq, mp);
break;
}
break;
case M_DATA:
if (!tmxp->isptm) {
if ((newmp = allocb(sizeof (char), BPRI_MED)) == NULL) {
recover(q, mp, sizeof (char));
return (0);
}
newmp->b_datap->db_type = M_CTL;
*newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
newmp->b_cont = mp;
mp = newmp;
}
case M_PROTO:
case M_PCPROTO:
qp = tmxp->muxq;
if (qp == NULL) {
merror(q, mp, EINVAL);
return (0);
}
if (queclass(mp) < QPCTL) {
if (q->q_first != NULL || !canputnext(qp)) {
(void) putq(q, mp);
return (0);
}
}
putnext(qp, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(q, FLUSHALL);
if (tmxp->muxq != NULL) {
putnext(tmxp->muxq, mp);
return (0);
}
*mp->b_rptr &= ~FLUSHW;
if (*mp->b_rptr & FLUSHR)
qreply(q, mp);
else
freemsg(mp);
break;
default:
cmn_err(CE_NOTE, "logdmuxuwput: received unexpected message"
" of type 0x%x", mp->b_datap->db_type);
freemsg(mp);
}
return (0);
}
static int
logdmuxuwsrv(queue_t *q)
{
mblk_t *mp, *newmp;
queue_t *qp;
struct tmx *tmxp = q->q_ptr;
while ((mp = getq(q)) != NULL) {
switch (mp->b_datap->db_type) {
case M_DATA:
if (!tmxp->isptm) {
if ((newmp = allocb(sizeof (char), BPRI_MED)) ==
NULL) {
recover(q, mp, sizeof (char));
return (0);
}
newmp->b_datap->db_type = M_CTL;
*newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
newmp->b_cont = mp;
mp = newmp;
}
case M_CTL:
case M_PROTO:
if (tmxp->muxq == NULL) {
merror(q, mp, EIO);
break;
}
qp = tmxp->muxq;
if (!canputnext(qp)) {
(void) putbq(q, mp);
return (0);
}
putnext(qp, mp);
break;
default:
cmn_err(CE_NOTE, "logdmuxuwsrv: received unexpected"
" message of type 0x%x", mp->b_datap->db_type);
freemsg(mp);
}
}
return (0);
}
static int
logdmuxlrput(queue_t *q, mblk_t *mp)
{
mblk_t *savemp;
queue_t *qp;
struct iocblk *ioc;
struct tmx *tmxp = q->q_ptr;
uchar_t flush;
uint_t *messagep;
unlinkinfo_t *unlinkinfop = tmxp->unlinkinfop;
if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
freemsg(mp);
return (0);
}
if ((q->q_first != NULL) && (queclass(mp) < QPCTL) &&
(!LOGDMUX_PROTO_MBLK(mp))) {
(void) putq(q, mp);
return (0);
}
switch (mp->b_datap->db_type) {
case M_IOCTL:
ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case TIOCSWINSZ:
case TCSETAF:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETS:
case TCSETSW:
case TCSBRK:
case TIOCSTI:
qp = tmxp->peerq;
break;
default:
cmn_err(CE_NOTE, "logdmuxlrput: received unexpected"
" request for ioctl 0x%x", ioc->ioc_cmd);
miocnak(q, mp, 0, 0);
return (0);
}
break;
case M_DATA:
case M_HANGUP:
qp = tmxp->peerq;
break;
case M_CTL:
if (LOGDMUX_PROTO_MBLK(mp)) {
messagep = (uint_t *)mp->b_rptr;
switch (*messagep) {
case LOGDMUX_UNLINK_REQ:
*messagep = LOGDMUX_UNLINK_RESP;
qp = tmxp->peerq;
mutex_enter(&logdmux_peerq_lock);
tmxp->peerq = NULL;
mutex_exit(&logdmux_peerq_lock);
put(RD(qp), mp);
return (0);
case LOGDMUX_UNLINK_RESP:
qp = tmxp->rdq;
mutex_enter(&unlinkinfop->state_lock);
ASSERT(unlinkinfop->state ==
LOGDMUX_UNLINK_PENDING);
unlinkinfop->state = LOGDMUX_UNLINKED;
mutex_exit(&unlinkinfop->state_lock);
logdmux_finish_unlink(WR(qp), mp->b_cont);
return (0);
}
}
qp = tmxp->rdq;
if (q->q_first != NULL || !canputnext(qp)) {
(void) putq(q, mp);
return (0);
}
if ((MBLKL(mp) == 1) && (*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
savemp = mp->b_cont;
freeb(mp);
mp = savemp;
}
putnext(qp, mp);
return (0);
case M_IOCACK:
case M_IOCNAK:
case M_PROTO:
case M_PCPROTO:
case M_PCSIG:
case M_SETOPTS:
qp = tmxp->rdq;
break;
case M_ERROR:
if (tmxp->isptm) {
freemsg(mp);
return (0);
}
mp->b_datap->db_type = M_HANGUP;
mp->b_wptr = mp->b_rptr;
qp = tmxp->peerq;
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHR)
flushq_dataonly(q);
if (mp->b_flag & MSGMARK) {
qp = tmxp->rdq;
mp->b_flag &= ~MSGMARK;
} else {
qp = tmxp->peerq;
flush = *mp->b_rptr;
*mp->b_rptr &= ~(FLUSHR | FLUSHW);
if (flush & FLUSHW)
*mp->b_rptr |= FLUSHR;
if (flush & FLUSHR)
*mp->b_rptr |= FLUSHW;
}
break;
case M_START:
case M_STOP:
case M_STARTI:
case M_STOPI:
freemsg(mp);
return (0);
default:
cmn_err(CE_NOTE, "logdmuxlrput: received unexpected "
"message of type 0x%x", mp->b_datap->db_type);
freemsg(mp);
return (0);
}
if (queclass(mp) < QPCTL) {
if (q->q_first != NULL || !canputnext(qp)) {
(void) putq(q, mp);
return (0);
}
}
putnext(qp, mp);
return (0);
}
static int
logdmuxlrsrv(queue_t *q)
{
mblk_t *mp, *savemp;
queue_t *qp;
struct iocblk *ioc;
struct tmx *tmxp = q->q_ptr;
while ((mp = getq(q)) != NULL) {
if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
freemsg(mp);
continue;
}
switch (mp->b_datap->db_type) {
case M_IOCTL:
ioc = (struct iocblk *)mp->b_rptr;
switch (ioc->ioc_cmd) {
case TIOCSWINSZ:
case TCSETAF:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETS:
case TCSETSW:
case TCSBRK:
case TIOCSTI:
qp = tmxp->peerq;
break;
default:
cmn_err(CE_NOTE, "logdmuxlrsrv: received "
"unexpected request for ioctl 0x%x",
ioc->ioc_cmd);
miocnak(q, mp, 0, 0);
continue;
}
break;
case M_DATA:
case M_HANGUP:
qp = tmxp->peerq;
break;
case M_CTL:
qp = tmxp->rdq;
if (!canputnext(qp)) {
(void) putbq(q, mp);
return (0);
}
if (MBLKL(mp) == 1 &&
(*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
savemp = mp->b_cont;
freeb(mp);
mp = savemp;
}
putnext(qp, mp);
continue;
case M_PROTO:
case M_SETOPTS:
qp = tmxp->rdq;
break;
default:
cmn_err(CE_NOTE, "logdmuxlrsrv: received unexpected "
"message of type 0x%x", mp->b_datap->db_type);
freemsg(mp);
continue;
}
ASSERT(queclass(mp) < QPCTL);
if (!canputnext(qp)) {
(void) putbq(q, mp);
return (0);
}
putnext(qp, mp);
}
return (0);
}
static int
logdmuxlwsrv(queue_t *q)
{
struct tmx *tmxp = q->q_ptr;
qenable(WR(tmxp->rdq));
mutex_enter(&logdmux_peerq_lock);
if (tmxp->peerq != NULL)
qenable(RD(tmxp->peerq));
mutex_exit(&logdmux_peerq_lock);
return (0);
}
static void
logdmuxlink(queue_t *q, mblk_t *mp)
{
struct tmx *tmxp = q->q_ptr;
struct linkblk *lp = (struct linkblk *)mp->b_cont->b_rptr;
if (tmxp->muxq != NULL) {
miocnak(q, mp, 0, EINVAL);
return;
}
tmxp->muxq = lp->l_qbot;
tmxp->muxq->q_ptr = tmxp;
RD(tmxp->muxq)->q_ptr = tmxp;
miocack(q, mp, 0, 0);
}
static void
logdmuxunlink(queue_t *q, mblk_t *mp)
{
struct tmx *tmxp = q->q_ptr;
unlinkinfo_t *unlinkinfop;
mutex_enter(&logdmux_qexch_lock);
unlinkinfop = tmxp->unlinkinfop;
if (unlinkinfop == NULL) {
ASSERT(tmxp->peerq == NULL);
tmxp->muxq = NULL;
mutex_exit(&logdmux_qexch_lock);
miocack(q, mp, 0, 0);
return;
}
mutex_exit(&logdmux_qexch_lock);
mutex_enter(&unlinkinfop->state_lock);
switch (unlinkinfop->state) {
case LOGDMUX_LINKED:
unlinkinfop->state = LOGDMUX_UNLINK_PENDING;
mutex_exit(&unlinkinfop->state_lock);
unlinkinfop->prot_mp->b_cont = mp;
put(RD(tmxp->peerq), unlinkinfop->prot_mp);
break;
case LOGDMUX_UNLINK_PENDING:
mutex_exit(&unlinkinfop->state_lock);
tmxp->unlink_mp = mp;
tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
drv_usectohz(LOGDMUX_POLL_WAIT));
break;
case LOGDMUX_UNLINKED:
mutex_exit(&unlinkinfop->state_lock);
mutex_destroy(&unlinkinfop->state_lock);
freeb(unlinkinfop->prot_mp);
kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
logdmux_finish_unlink(q, mp);
break;
default:
mutex_exit(&unlinkinfop->state_lock);
cmn_err(CE_PANIC,
"logdmuxunlink: peer linkage is in an unrecognized state");
break;
}
}
static void
logdmux_finish_unlink(queue_t *q, mblk_t *unlink_mp)
{
struct tmx *tmxp = q->q_ptr;
mblk_t *mp;
while ((mp = getq(WR(q))) != NULL)
putnext(tmxp->muxq, mp);
tmxp->muxq = NULL;
tmxp->unlinkinfop = NULL;
tmxp->peerq = NULL;
miocack(q, unlink_mp, 0, 0);
}
static void
logdmux_unlink_timer(void *arg)
{
queue_t *q = arg;
struct tmx *tmxp = q->q_ptr;
unlinkinfo_t *unlinkinfop = tmxp->unlinkinfop;
tmxp->utimoutid = 0;
mutex_enter(&unlinkinfop->state_lock);
if (unlinkinfop->state != LOGDMUX_UNLINKED) {
ASSERT(unlinkinfop->state == LOGDMUX_UNLINK_PENDING);
mutex_exit(&unlinkinfop->state_lock);
tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
drv_usectohz(LOGDMUX_POLL_WAIT));
} else {
mutex_exit(&unlinkinfop->state_lock);
mutex_destroy(&unlinkinfop->state_lock);
freeb(unlinkinfop->prot_mp);
kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
logdmux_finish_unlink(q, tmxp->unlink_mp);
}
}
static void
logdmux_timer(void *arg)
{
queue_t *q = arg;
struct tmx *tmxp = q->q_ptr;
ASSERT(tmxp != NULL);
if (q->q_flag & QREADR) {
ASSERT(tmxp->rtimoutid != 0);
tmxp->rtimoutid = 0;
} else {
ASSERT(tmxp->wtimoutid != 0);
tmxp->wtimoutid = 0;
}
enableok(q);
qenable(q);
}
static void
logdmux_buffer(void *arg)
{
queue_t *q = arg;
struct tmx *tmxp = q->q_ptr;
ASSERT(tmxp != NULL);
if (q->q_flag & QREADR) {
ASSERT(tmxp->rbufcid != 0);
tmxp->rbufcid = 0;
} else {
ASSERT(tmxp->wbufcid != 0);
tmxp->wbufcid = 0;
}
enableok(q);
qenable(q);
}
static void
recover(queue_t *q, mblk_t *mp, size_t size)
{
timeout_id_t tid;
bufcall_id_t bid;
struct tmx *tmxp = q->q_ptr;
ASSERT(queclass(mp) < QPCTL);
ASSERT(WR(q)->q_next == NULL);
noenable(q);
(void) putbq(q, mp);
if (q->q_flag & QREADR) {
if (tmxp->rtimoutid != 0 || tmxp->rbufcid != 0)
return;
} else {
if (tmxp->wtimoutid != 0 || tmxp->wbufcid != 0)
return;
}
if (!(bid = qbufcall(RD(q), size, BPRI_MED, logdmux_buffer, q))) {
tid = qtimeout(RD(q), logdmux_timer, q, drv_usectohz(SIMWAIT));
if (q->q_flag & QREADR)
tmxp->rtimoutid = tid;
else
tmxp->wtimoutid = tid;
} else {
if (q->q_flag & QREADR)
tmxp->rbufcid = bid;
else
tmxp->wbufcid = bid;
}
}
static void
flushq_dataonly(queue_t *q)
{
mblk_t *mp, *nmp;
mp = q->q_first;
while (mp != NULL) {
if (mp->b_datap->db_type == M_DATA) {
nmp = mp->b_next;
rmvq(q, mp);
freemsg(mp);
mp = nmp;
} else {
mp = mp->b_next;
}
}
}
static int
logdmux_alloc_unlinkinfo(struct tmx *t0, struct tmx *t1)
{
unlinkinfo_t *p;
uint_t *messagep;
if ((p = kmem_zalloc(sizeof (unlinkinfo_t), KM_NOSLEEP)) == NULL)
return (ENOSR);
if ((p->prot_mp = allocb(sizeof (uint_t), BPRI_MED)) == NULL) {
kmem_free(p, sizeof (unlinkinfo_t));
return (ENOSR);
}
DB_TYPE(p->prot_mp) = M_CTL;
messagep = (uint_t *)p->prot_mp->b_wptr;
*messagep = LOGDMUX_UNLINK_REQ;
p->prot_mp->b_wptr += sizeof (*messagep);
p->state = LOGDMUX_LINKED;
mutex_init(&p->state_lock, NULL, MUTEX_DRIVER, NULL);
t0->unlinkinfop = t1->unlinkinfop = p;
return (0);
}