xref: /onnv-gate/usr/src/uts/common/io/logindmux.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * Description: logindmux.c
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  * The logindmux driver is used with login modules (like telmod/rlmod).
310Sstevel@tonic-gate  * This is a 1x1 cloning mux and two of these muxes are used. The lower link
320Sstevel@tonic-gate  * of one of the muxes receives input from net and the lower link of the
330Sstevel@tonic-gate  * other mux receives input from pseudo terminal subsystem.
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * The logdmux_qexch_lock mutex manages the race between LOGDMX_IOC_QEXCHANGE,
360Sstevel@tonic-gate  * logdmuxunlink() and logdmuxclose(), so that the instance selected as a peer
370Sstevel@tonic-gate  * in LOGDMX_IOC_QEXCHANGE cannot be unlinked or closed until the qexchange
380Sstevel@tonic-gate  * is complete; see the inline comments in the code for details.
390Sstevel@tonic-gate  *
400Sstevel@tonic-gate  * The logdmux_peerq_lock mutex manages the race between logdmuxlwsrv() and
410Sstevel@tonic-gate  * logdmuxlrput() (when null'ing tmxp->peerq during LOGDMUX_UNLINK_REQ
420Sstevel@tonic-gate  * processing).
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  * The logdmux_minor_lock mutex serializes the growth of logdmux_minor_arena
450Sstevel@tonic-gate  * (the arena is grown gradually rather than allocated all at once so that
460Sstevel@tonic-gate  * minor numbers are recycled sooner; for simplicity it is never shrunk).
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  * The unlink operation is implemented using protocol messages that flow
490Sstevel@tonic-gate  * between the two logindmux peer instances. The instance processing the
500Sstevel@tonic-gate  * I_UNLINK ioctl will send a LOGDMUX_UNLINK_REQ protocol message to its
510Sstevel@tonic-gate  * peer to indicate that it wishes to unlink; the peer will process this
520Sstevel@tonic-gate  * message in its lrput, null its tmxp->peerq and then send a
530Sstevel@tonic-gate  * LOGDMUX_UNLINK_RESP protocol message in reply to indicate that the
540Sstevel@tonic-gate  * unlink can proceed; having received the reply in its lrput, the
550Sstevel@tonic-gate  * instance processing the I_UNLINK can then continue. To ensure that only
560Sstevel@tonic-gate  * one of the peer instances will be actively processing an I_UNLINK at
570Sstevel@tonic-gate  * any one time, a single structure (an unlinkinfo_t containing a mutex,
580Sstevel@tonic-gate  * state variable and pointer to an M_CTL mblk) is allocated during
590Sstevel@tonic-gate  * the processing of the LOGDMX_IOC_QEXCHANGE ioctl. The two instances, if
600Sstevel@tonic-gate  * trying to unlink simultaneously, will race to get control of this
610Sstevel@tonic-gate  * structure which contains the resources necessary to process the
620Sstevel@tonic-gate  * I_UNLINK. The instance that wins this race will be able to continue
630Sstevel@tonic-gate  * with the unlink whilst the other instance will be obliged to wait.
640Sstevel@tonic-gate  */
650Sstevel@tonic-gate 
660Sstevel@tonic-gate #include <sys/types.h>
670Sstevel@tonic-gate #include <sys/param.h>
680Sstevel@tonic-gate #include <sys/errno.h>
690Sstevel@tonic-gate #include <sys/debug.h>
700Sstevel@tonic-gate #include <sys/stropts.h>
710Sstevel@tonic-gate #include <sys/stream.h>
720Sstevel@tonic-gate #include <sys/logindmux.h>
730Sstevel@tonic-gate #include <sys/logindmux_impl.h>
740Sstevel@tonic-gate #include <sys/stat.h>
750Sstevel@tonic-gate #include <sys/kmem.h>
760Sstevel@tonic-gate #include <sys/vmem.h>
770Sstevel@tonic-gate #include <sys/strsun.h>
780Sstevel@tonic-gate #include <sys/sysmacros.h>
790Sstevel@tonic-gate #include <sys/mkdev.h>
800Sstevel@tonic-gate #include <sys/ddi.h>
810Sstevel@tonic-gate #include <sys/sunddi.h>
820Sstevel@tonic-gate #include <sys/modctl.h>
830Sstevel@tonic-gate #include <sys/termios.h>
840Sstevel@tonic-gate #include <sys/cmn_err.h>
850Sstevel@tonic-gate 
860Sstevel@tonic-gate static int logdmuxopen(queue_t *, dev_t *, int, int, cred_t *);
870Sstevel@tonic-gate static int logdmuxclose(queue_t *, int, cred_t *);
880Sstevel@tonic-gate static int logdmuxursrv(queue_t *);
890Sstevel@tonic-gate static int logdmuxuwput(queue_t *, mblk_t *);
900Sstevel@tonic-gate static int logdmuxlrput(queue_t *, mblk_t *);
910Sstevel@tonic-gate static int logdmuxlrsrv(queue_t *);
920Sstevel@tonic-gate static int logdmuxlwsrv(queue_t *);
930Sstevel@tonic-gate static int logdmuxuwsrv(queue_t *);
940Sstevel@tonic-gate static int logdmux_alloc_unlinkinfo(struct tmx *, struct tmx *);
950Sstevel@tonic-gate 
960Sstevel@tonic-gate static void logdmuxlink(queue_t *, mblk_t *);
970Sstevel@tonic-gate static void logdmuxunlink(queue_t *, mblk_t *);
980Sstevel@tonic-gate static void logdmux_finish_unlink(queue_t *, mblk_t *);
990Sstevel@tonic-gate static void logdmux_unlink_timer(void *arg);
1000Sstevel@tonic-gate static void recover(queue_t *, mblk_t *, size_t);
1010Sstevel@tonic-gate static void flushq_dataonly(queue_t *);
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate static kmutex_t logdmux_qexch_lock;
1040Sstevel@tonic-gate static kmutex_t logdmux_peerq_lock;
1050Sstevel@tonic-gate static kmutex_t logdmux_minor_lock;
1060Sstevel@tonic-gate static minor_t	logdmux_maxminor = 256;	/* grown as necessary */
1070Sstevel@tonic-gate static vmem_t	*logdmux_minor_arena;
1080Sstevel@tonic-gate static void	*logdmux_statep;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static struct module_info logdmuxm_info = {
1110Sstevel@tonic-gate 	LOGDMX_ID,
1120Sstevel@tonic-gate 	"logindmux",
1130Sstevel@tonic-gate 	0,
1140Sstevel@tonic-gate 	256,
1150Sstevel@tonic-gate 	512,
1160Sstevel@tonic-gate 	256
1170Sstevel@tonic-gate };
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate static struct qinit logdmuxurinit = {
1200Sstevel@tonic-gate 	NULL,
1210Sstevel@tonic-gate 	logdmuxursrv,
1220Sstevel@tonic-gate 	logdmuxopen,
1230Sstevel@tonic-gate 	logdmuxclose,
1240Sstevel@tonic-gate 	NULL,
1250Sstevel@tonic-gate 	&logdmuxm_info
1260Sstevel@tonic-gate };
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate static struct qinit logdmuxuwinit = {
1290Sstevel@tonic-gate 	logdmuxuwput,
1300Sstevel@tonic-gate 	logdmuxuwsrv,
1310Sstevel@tonic-gate 	NULL,
1320Sstevel@tonic-gate 	NULL,
1330Sstevel@tonic-gate 	NULL,
1340Sstevel@tonic-gate 	&logdmuxm_info
1350Sstevel@tonic-gate };
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate static struct qinit logdmuxlrinit = {
1380Sstevel@tonic-gate 	logdmuxlrput,
1390Sstevel@tonic-gate 	logdmuxlrsrv,
1400Sstevel@tonic-gate 	NULL,
1410Sstevel@tonic-gate 	NULL,
1420Sstevel@tonic-gate 	NULL,
1430Sstevel@tonic-gate 	&logdmuxm_info
1440Sstevel@tonic-gate };
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate static struct qinit logdmuxlwinit = {
1470Sstevel@tonic-gate 	NULL,
1480Sstevel@tonic-gate 	logdmuxlwsrv,
1490Sstevel@tonic-gate 	NULL,
1500Sstevel@tonic-gate 	NULL,
1510Sstevel@tonic-gate 	NULL,
1520Sstevel@tonic-gate 	&logdmuxm_info
1530Sstevel@tonic-gate };
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate struct streamtab logdmuxinfo = {
1560Sstevel@tonic-gate 	&logdmuxurinit,
1570Sstevel@tonic-gate 	&logdmuxuwinit,
1580Sstevel@tonic-gate 	&logdmuxlrinit,
1590Sstevel@tonic-gate 	&logdmuxlwinit
1600Sstevel@tonic-gate };
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate static int logdmux_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1630Sstevel@tonic-gate static int logdmux_attach(dev_info_t *, ddi_attach_cmd_t);
1640Sstevel@tonic-gate static int logdmux_detach(dev_info_t *, ddi_detach_cmd_t);
1650Sstevel@tonic-gate static dev_info_t *logdmux_dip;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(logdmux_ops, nulldev, nulldev, logdmux_attach,
168*7656SSherry.Moore@Sun.COM     logdmux_detach, nulldev, logdmux_info, D_MP | D_MTPERQ, &logdmuxinfo,
169*7656SSherry.Moore@Sun.COM     ddi_quiesce_not_needed);
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate static struct modldrv modldrv = {
1720Sstevel@tonic-gate 	&mod_driverops,
173*7656SSherry.Moore@Sun.COM 	"logindmux driver",
1740Sstevel@tonic-gate 	&logdmux_ops
1750Sstevel@tonic-gate };
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate static struct modlinkage modlinkage = {
1780Sstevel@tonic-gate 	MODREV_1, &modldrv, NULL
1790Sstevel@tonic-gate };
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate int
_init(void)1820Sstevel@tonic-gate _init(void)
1830Sstevel@tonic-gate {
1840Sstevel@tonic-gate 	int	ret;
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 	mutex_init(&logdmux_peerq_lock, NULL, MUTEX_DRIVER, NULL);
1870Sstevel@tonic-gate 	mutex_init(&logdmux_qexch_lock, NULL, MUTEX_DRIVER, NULL);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	if ((ret = mod_install(&modlinkage)) != 0) {
1900Sstevel@tonic-gate 		mutex_destroy(&logdmux_peerq_lock);
1910Sstevel@tonic-gate 		mutex_destroy(&logdmux_qexch_lock);
1920Sstevel@tonic-gate 		return (ret);
1930Sstevel@tonic-gate 	}
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	logdmux_minor_arena = vmem_create("logdmux_minor", (void *)1,
1960Sstevel@tonic-gate 	    logdmux_maxminor, 1, NULL, NULL, NULL, 0,
1970Sstevel@tonic-gate 	    VM_SLEEP | VMC_IDENTIFIER);
1980Sstevel@tonic-gate 	(void) ddi_soft_state_init(&logdmux_statep, sizeof (struct tmx), 1);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	return (0);
2010Sstevel@tonic-gate }
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate int
_fini(void)2040Sstevel@tonic-gate _fini(void)
2050Sstevel@tonic-gate {
2060Sstevel@tonic-gate 	int	ret;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	if ((ret = mod_remove(&modlinkage)) == 0) {
2090Sstevel@tonic-gate 		mutex_destroy(&logdmux_peerq_lock);
2100Sstevel@tonic-gate 		mutex_destroy(&logdmux_qexch_lock);
2110Sstevel@tonic-gate 		ddi_soft_state_fini(&logdmux_statep);
2120Sstevel@tonic-gate 		vmem_destroy(logdmux_minor_arena);
2130Sstevel@tonic-gate 		logdmux_minor_arena = NULL;
2140Sstevel@tonic-gate 	}
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	return (ret);
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2200Sstevel@tonic-gate _info(struct modinfo *modinfop)
2210Sstevel@tonic-gate {
2220Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate static int
logdmux_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2260Sstevel@tonic-gate logdmux_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
2290Sstevel@tonic-gate 		return (DDI_FAILURE);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "logindmux", S_IFCHR, 0, DDI_PSEUDO,
2320Sstevel@tonic-gate 	    CLONE_DEV) == DDI_FAILURE)
2330Sstevel@tonic-gate 		return (DDI_FAILURE);
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	logdmux_dip = devi;
2360Sstevel@tonic-gate 	return (DDI_SUCCESS);
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate static int
logdmux_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)2400Sstevel@tonic-gate logdmux_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
2430Sstevel@tonic-gate 		return (DDI_FAILURE);
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
2460Sstevel@tonic-gate 	return (DDI_SUCCESS);
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /* ARGSUSED */
2500Sstevel@tonic-gate static int
logdmux_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2510Sstevel@tonic-gate logdmux_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2520Sstevel@tonic-gate {
2530Sstevel@tonic-gate 	int error;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	switch (infocmd) {
2560Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2570Sstevel@tonic-gate 		if (logdmux_dip == NULL) {
2580Sstevel@tonic-gate 			error = DDI_FAILURE;
2590Sstevel@tonic-gate 		} else {
2600Sstevel@tonic-gate 			*result = logdmux_dip;
2610Sstevel@tonic-gate 			error = DDI_SUCCESS;
2620Sstevel@tonic-gate 		}
2630Sstevel@tonic-gate 		break;
2640Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2650Sstevel@tonic-gate 		*result = (void *)0;
2660Sstevel@tonic-gate 		error = DDI_SUCCESS;
2670Sstevel@tonic-gate 		break;
2680Sstevel@tonic-gate 	default:
2690Sstevel@tonic-gate 		error = DDI_FAILURE;
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate 	return (error);
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate /*
2750Sstevel@tonic-gate  * Logindmux open routine
2760Sstevel@tonic-gate  */
2770Sstevel@tonic-gate /*ARGSUSED*/
2780Sstevel@tonic-gate static int
logdmuxopen(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * crp)2790Sstevel@tonic-gate logdmuxopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
2800Sstevel@tonic-gate {
2810Sstevel@tonic-gate 	struct	tmx *tmxp;
2820Sstevel@tonic-gate 	minor_t	minor, omaxminor;
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	if (sflag != CLONEOPEN)
2850Sstevel@tonic-gate 		return (EINVAL);
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	mutex_enter(&logdmux_minor_lock);
2880Sstevel@tonic-gate 	if (vmem_size(logdmux_minor_arena, VMEM_FREE) == 0) {
2890Sstevel@tonic-gate 		/*
2900Sstevel@tonic-gate 		 * The arena has been exhausted; grow by powers of two
2910Sstevel@tonic-gate 		 * up to MAXMIN; bail if we've run out of minors.
2920Sstevel@tonic-gate 		 */
2930Sstevel@tonic-gate 		if (logdmux_maxminor == MAXMIN) {
2940Sstevel@tonic-gate 			mutex_exit(&logdmux_minor_lock);
2950Sstevel@tonic-gate 			return (ENOMEM);
2960Sstevel@tonic-gate 		}
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 		omaxminor = logdmux_maxminor;
2990Sstevel@tonic-gate 		logdmux_maxminor = MIN(logdmux_maxminor << 1, MAXMIN);
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 		(void) vmem_add(logdmux_minor_arena,
3020Sstevel@tonic-gate 		    (void *)(uintptr_t)(omaxminor + 1),
3030Sstevel@tonic-gate 		    logdmux_maxminor - omaxminor, VM_SLEEP);
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 	minor = (minor_t)(uintptr_t)
3060Sstevel@tonic-gate 	    vmem_alloc(logdmux_minor_arena, 1, VM_SLEEP);
3070Sstevel@tonic-gate 	mutex_exit(&logdmux_minor_lock);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(logdmux_statep, minor) == DDI_FAILURE) {
3100Sstevel@tonic-gate 		vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
3110Sstevel@tonic-gate 		return (ENOMEM);
3120Sstevel@tonic-gate 	}
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	tmxp = ddi_get_soft_state(logdmux_statep, minor);
3150Sstevel@tonic-gate 	tmxp->rdq = q;
3160Sstevel@tonic-gate 	tmxp->muxq = NULL;
3170Sstevel@tonic-gate 	tmxp->peerq = NULL;
3180Sstevel@tonic-gate 	tmxp->unlinkinfop = NULL;
3190Sstevel@tonic-gate 	tmxp->dev0 = minor;
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), tmxp->dev0);
3220Sstevel@tonic-gate 	q->q_ptr = tmxp;
3230Sstevel@tonic-gate 	WR(q)->q_ptr = tmxp;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	qprocson(q);
3260Sstevel@tonic-gate 	return (0);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * Logindmux close routine gets called when telnet connection is closed
3310Sstevel@tonic-gate  */
3320Sstevel@tonic-gate /*ARGSUSED*/
3330Sstevel@tonic-gate static int
logdmuxclose(queue_t * q,int flag,cred_t * crp)3340Sstevel@tonic-gate logdmuxclose(queue_t *q, int flag, cred_t *crp)
3350Sstevel@tonic-gate {
3360Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
3370Sstevel@tonic-gate 	minor_t		minor = tmxp->dev0;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	ASSERT(tmxp->muxq == NULL);
3400Sstevel@tonic-gate 	ASSERT(tmxp->peerq == NULL);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	qprocsoff(q);
3430Sstevel@tonic-gate 	if (tmxp->wbufcid != 0) {
3440Sstevel@tonic-gate 		qunbufcall(q, tmxp->wbufcid);
3450Sstevel@tonic-gate 		tmxp->wbufcid = 0;
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 	if (tmxp->rbufcid != 0) {
3480Sstevel@tonic-gate 		qunbufcall(q, tmxp->rbufcid);
3490Sstevel@tonic-gate 		tmxp->rbufcid = 0;
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 	if (tmxp->rtimoutid != 0) {
3520Sstevel@tonic-gate 		(void) quntimeout(q, tmxp->rtimoutid);
3530Sstevel@tonic-gate 		tmxp->rtimoutid = 0;
3540Sstevel@tonic-gate 	}
3550Sstevel@tonic-gate 	if (tmxp->wtimoutid != 0) {
3560Sstevel@tonic-gate 		(void) quntimeout(q, tmxp->wtimoutid);
3570Sstevel@tonic-gate 		tmxp->wtimoutid = 0;
3580Sstevel@tonic-gate 	}
3590Sstevel@tonic-gate 	if (tmxp->utimoutid != 0) {
3600Sstevel@tonic-gate 		(void) quntimeout(q, tmxp->utimoutid);
3610Sstevel@tonic-gate 		tmxp->utimoutid = 0;
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	/*
3650Sstevel@tonic-gate 	 * Hold logdmux_qexch_lock to prevent another thread that might be
3660Sstevel@tonic-gate 	 * in LOGDMX_IOC_QEXCHANGE from looking up our state while we're
3670Sstevel@tonic-gate 	 * disposing of it.
3680Sstevel@tonic-gate 	 */
3690Sstevel@tonic-gate 	mutex_enter(&logdmux_qexch_lock);
3700Sstevel@tonic-gate 	ddi_soft_state_free(logdmux_statep, minor);
3710Sstevel@tonic-gate 	vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
3720Sstevel@tonic-gate 	mutex_exit(&logdmux_qexch_lock);
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	q->q_ptr = NULL;
3750Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	return (0);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate /*
3810Sstevel@tonic-gate  * Upper read service routine
3820Sstevel@tonic-gate  */
3830Sstevel@tonic-gate static int
logdmuxursrv(queue_t * q)3840Sstevel@tonic-gate logdmuxursrv(queue_t *q)
3850Sstevel@tonic-gate {
3860Sstevel@tonic-gate 	struct tmx *tmxp = q->q_ptr;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	if (tmxp->muxq != NULL)
3890Sstevel@tonic-gate 		qenable(RD(tmxp->muxq));
3900Sstevel@tonic-gate 	return (0);
3910Sstevel@tonic-gate }
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate /*
3940Sstevel@tonic-gate  * This routine gets called when telnet daemon sends data or ioctl messages
3950Sstevel@tonic-gate  * to upper mux queue.
3960Sstevel@tonic-gate  */
3970Sstevel@tonic-gate static int
logdmuxuwput(queue_t * q,mblk_t * mp)3980Sstevel@tonic-gate logdmuxuwput(queue_t *q, mblk_t *mp)
3990Sstevel@tonic-gate {
4000Sstevel@tonic-gate 	queue_t		*qp;
4010Sstevel@tonic-gate 	mblk_t		*newmp;
4020Sstevel@tonic-gate 	struct iocblk	*ioc;
4030Sstevel@tonic-gate 	minor_t		minor;
4040Sstevel@tonic-gate 	STRUCT_HANDLE(protocol_arg, protoh);
4050Sstevel@tonic-gate 	struct tmx	*tmxp, *tmxpeerp;
4060Sstevel@tonic-gate 	int		error;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	tmxp = q->q_ptr;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	case M_IOCTL:
4130Sstevel@tonic-gate 		ASSERT(MBLKL(mp) == sizeof (struct iocblk));
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 		ioc = (struct iocblk *)mp->b_rptr;
4160Sstevel@tonic-gate 		switch (ioc->ioc_cmd) {
4170Sstevel@tonic-gate 		/*
4180Sstevel@tonic-gate 		 * This is a special ioctl which exchanges q info
4190Sstevel@tonic-gate 		 * of the two peers, connected to netf and ptmx.
4200Sstevel@tonic-gate 		 */
4210Sstevel@tonic-gate 		case LOGDMX_IOC_QEXCHANGE:
4220Sstevel@tonic-gate 			error = miocpullup(mp,
4230Sstevel@tonic-gate 			    SIZEOF_STRUCT(protocol_arg, ioc->ioc_flag));
4240Sstevel@tonic-gate 			if (error != 0) {
4250Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
4260Sstevel@tonic-gate 				break;
4270Sstevel@tonic-gate 			}
4280Sstevel@tonic-gate 			STRUCT_SET_HANDLE(protoh, ioc->ioc_flag,
4290Sstevel@tonic-gate 			    (struct protocol_arg *)mp->b_cont->b_rptr);
4300Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
4310Sstevel@tonic-gate 			if ((ioc->ioc_flag & DATAMODEL_MASK) ==
4320Sstevel@tonic-gate 			    DATAMODEL_ILP32) {
4330Sstevel@tonic-gate 				minor = getminor(expldev(
4340Sstevel@tonic-gate 				    STRUCT_FGET(protoh, dev)));
4350Sstevel@tonic-gate 			} else
4360Sstevel@tonic-gate #endif
4370Sstevel@tonic-gate 			{
4380Sstevel@tonic-gate 				minor = getminor(STRUCT_FGET(protoh, dev));
4390Sstevel@tonic-gate 			}
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 			/*
4420Sstevel@tonic-gate 			 * The second argument to ddi_get_soft_state() is
4430Sstevel@tonic-gate 			 * interpreted as an `int', so prohibit negative
4440Sstevel@tonic-gate 			 * values.
4450Sstevel@tonic-gate 			 */
4460Sstevel@tonic-gate 			if ((int)minor < 0) {
4470Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
4480Sstevel@tonic-gate 				break;
4490Sstevel@tonic-gate 			}
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 			/*
4520Sstevel@tonic-gate 			 * We must hold logdmux_qexch_lock while looking up
4530Sstevel@tonic-gate 			 * the proposed peer to prevent another thread from
4540Sstevel@tonic-gate 			 * simultaneously I_UNLINKing or closing it.
4550Sstevel@tonic-gate 			 */
4560Sstevel@tonic-gate 			mutex_enter(&logdmux_qexch_lock);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 			/*
4590Sstevel@tonic-gate 			 * For LOGDMX_IOC_QEXCHANGE to succeed, our peer must
4600Sstevel@tonic-gate 			 * exist (and not be us), and both we and our peer
4610Sstevel@tonic-gate 			 * must be I_LINKed (i.e., muxq must not be NULL) and
4620Sstevel@tonic-gate 			 * not already have a peer.
4630Sstevel@tonic-gate 			 */
4640Sstevel@tonic-gate 			tmxpeerp = ddi_get_soft_state(logdmux_statep, minor);
4650Sstevel@tonic-gate 			if (tmxpeerp == NULL || tmxpeerp == tmxp ||
4660Sstevel@tonic-gate 			    tmxpeerp->muxq == NULL || tmxpeerp->peerq != NULL ||
4670Sstevel@tonic-gate 			    tmxp->muxq == NULL || tmxp->peerq != NULL) {
4680Sstevel@tonic-gate 				mutex_exit(&logdmux_qexch_lock);
4690Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
4700Sstevel@tonic-gate 				break;
4710Sstevel@tonic-gate 			}
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 			/*
4740Sstevel@tonic-gate 			 * If `flag' is set then exchange queues and assume
4750Sstevel@tonic-gate 			 * tmxp refers to the ptmx stream.
4760Sstevel@tonic-gate 			 */
4770Sstevel@tonic-gate 			if (STRUCT_FGET(protoh, flag)) {
4780Sstevel@tonic-gate 				/*
4790Sstevel@tonic-gate 				 * Allocate and populate the structure we
4800Sstevel@tonic-gate 				 * need when processing an I_UNLINK ioctl.
4810Sstevel@tonic-gate 				 * Give both logindmux instances a pointer
4820Sstevel@tonic-gate 				 * to it from their tmx structure.
4830Sstevel@tonic-gate 				 */
4840Sstevel@tonic-gate 				if ((error = logdmux_alloc_unlinkinfo(
4850Sstevel@tonic-gate 				    tmxp, tmxpeerp)) != 0) {
4860Sstevel@tonic-gate 					mutex_exit(&logdmux_qexch_lock);
4870Sstevel@tonic-gate 					miocnak(q, mp, 0, error);
4880Sstevel@tonic-gate 					break;
4890Sstevel@tonic-gate 				}
4900Sstevel@tonic-gate 				tmxp->peerq = tmxpeerp->muxq;
4910Sstevel@tonic-gate 				tmxpeerp->peerq = tmxp->muxq;
4920Sstevel@tonic-gate 				tmxp->isptm = B_TRUE;
4930Sstevel@tonic-gate 			}
4940Sstevel@tonic-gate 			mutex_exit(&logdmux_qexch_lock);
4950Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
4960Sstevel@tonic-gate 			break;
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 		case I_LINK:
4990Sstevel@tonic-gate 			ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
5000Sstevel@tonic-gate 			logdmuxlink(q, mp);
5010Sstevel@tonic-gate 			break;
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		case I_UNLINK:
5040Sstevel@tonic-gate 			ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
5050Sstevel@tonic-gate 			logdmuxunlink(q, mp);
5060Sstevel@tonic-gate 			break;
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 		default:
5090Sstevel@tonic-gate 			if (tmxp->muxq == NULL) {
5100Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
5110Sstevel@tonic-gate 				return (0);
5120Sstevel@tonic-gate 			}
5130Sstevel@tonic-gate 			putnext(tmxp->muxq, mp);
5140Sstevel@tonic-gate 			break;
5150Sstevel@tonic-gate 		}
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 		break;
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	case M_DATA:
5200Sstevel@tonic-gate 		if (!tmxp->isptm) {
5210Sstevel@tonic-gate 			if ((newmp = allocb(sizeof (char), BPRI_MED)) == NULL) {
5220Sstevel@tonic-gate 				recover(q, mp, sizeof (char));
5230Sstevel@tonic-gate 				return (0);
5240Sstevel@tonic-gate 			}
5250Sstevel@tonic-gate 			newmp->b_datap->db_type = M_CTL;
5260Sstevel@tonic-gate 			*newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
5270Sstevel@tonic-gate 			newmp->b_cont = mp;
5280Sstevel@tonic-gate 			mp = newmp;
5290Sstevel@tonic-gate 		}
5300Sstevel@tonic-gate 		/* FALLTHRU */
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	case M_PROTO:
5330Sstevel@tonic-gate 	case M_PCPROTO:
5340Sstevel@tonic-gate 		qp = tmxp->muxq;
5350Sstevel@tonic-gate 		if (qp == NULL) {
5360Sstevel@tonic-gate 			merror(q, mp, EINVAL);
5370Sstevel@tonic-gate 			return (0);
5380Sstevel@tonic-gate 		}
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		if (queclass(mp) < QPCTL) {
5410Sstevel@tonic-gate 			if (q->q_first != NULL || !canputnext(qp)) {
5420Sstevel@tonic-gate 				(void) putq(q, mp);
5430Sstevel@tonic-gate 				return (0);
5440Sstevel@tonic-gate 			}
5450Sstevel@tonic-gate 		}
5460Sstevel@tonic-gate 		putnext(qp, mp);
5470Sstevel@tonic-gate 		break;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	case M_FLUSH:
5500Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW)
5510Sstevel@tonic-gate 			flushq(q, FLUSHALL);
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 		if (tmxp->muxq != NULL) {
5540Sstevel@tonic-gate 			putnext(tmxp->muxq, mp);
5550Sstevel@tonic-gate 			return (0);
5560Sstevel@tonic-gate 		}
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 		*mp->b_rptr &= ~FLUSHW;
5590Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
5600Sstevel@tonic-gate 			qreply(q, mp);
5610Sstevel@tonic-gate 		else
5620Sstevel@tonic-gate 			freemsg(mp);
5630Sstevel@tonic-gate 		break;
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	default:
5660Sstevel@tonic-gate 		cmn_err(CE_NOTE, "logdmuxuwput: received unexpected message"
5670Sstevel@tonic-gate 		    " of type 0x%x", mp->b_datap->db_type);
5680Sstevel@tonic-gate 		freemsg(mp);
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 	return (0);
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate /*
5740Sstevel@tonic-gate  * Upper write service routine
5750Sstevel@tonic-gate  */
5760Sstevel@tonic-gate static int
logdmuxuwsrv(queue_t * q)5770Sstevel@tonic-gate logdmuxuwsrv(queue_t *q)
5780Sstevel@tonic-gate {
5790Sstevel@tonic-gate 	mblk_t		*mp, *newmp;
5800Sstevel@tonic-gate 	queue_t		*qp;
5810Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
5840Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
5850Sstevel@tonic-gate 		case M_DATA:
5860Sstevel@tonic-gate 			if (!tmxp->isptm) {
5870Sstevel@tonic-gate 				if ((newmp = allocb(sizeof (char), BPRI_MED)) ==
5880Sstevel@tonic-gate 				    NULL) {
5890Sstevel@tonic-gate 					recover(q, mp, sizeof (char));
5900Sstevel@tonic-gate 					return (0);
5910Sstevel@tonic-gate 				}
5920Sstevel@tonic-gate 				newmp->b_datap->db_type = M_CTL;
5930Sstevel@tonic-gate 				*newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
5940Sstevel@tonic-gate 				newmp->b_cont = mp;
5950Sstevel@tonic-gate 				mp = newmp;
5960Sstevel@tonic-gate 			}
5970Sstevel@tonic-gate 			/* FALLTHRU */
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 		case M_CTL:
6000Sstevel@tonic-gate 		case M_PROTO:
6010Sstevel@tonic-gate 			if (tmxp->muxq == NULL) {
6020Sstevel@tonic-gate 				merror(q, mp, EIO);
6030Sstevel@tonic-gate 				break;
6040Sstevel@tonic-gate 			}
6050Sstevel@tonic-gate 			qp = tmxp->muxq;
6060Sstevel@tonic-gate 			if (!canputnext(qp)) {
6070Sstevel@tonic-gate 				(void) putbq(q, mp);
6080Sstevel@tonic-gate 				return (0);
6090Sstevel@tonic-gate 			}
6100Sstevel@tonic-gate 			putnext(qp, mp);
6110Sstevel@tonic-gate 			break;
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 		default:
6150Sstevel@tonic-gate 			cmn_err(CE_NOTE, "logdmuxuwsrv: received unexpected"
6160Sstevel@tonic-gate 			    " message of type 0x%x", mp->b_datap->db_type);
6170Sstevel@tonic-gate 			freemsg(mp);
6180Sstevel@tonic-gate 		}
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 	return (0);
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate /*
6240Sstevel@tonic-gate  * Logindmux lower put routine detects from which of the two lower queues
6250Sstevel@tonic-gate  * the data needs to be read from and writes it out to its peer queue.
6260Sstevel@tonic-gate  * For protocol, it detects M_CTL and sends its data to the daemon. Also,
6270Sstevel@tonic-gate  * for ioctl and other types of messages, it lets the daemon handle it.
6280Sstevel@tonic-gate  */
6290Sstevel@tonic-gate static int
logdmuxlrput(queue_t * q,mblk_t * mp)6300Sstevel@tonic-gate logdmuxlrput(queue_t *q, mblk_t *mp)
6310Sstevel@tonic-gate {
6320Sstevel@tonic-gate 	mblk_t		*savemp;
6330Sstevel@tonic-gate 	queue_t 	*qp;
6340Sstevel@tonic-gate 	struct iocblk	*ioc;
6350Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
6360Sstevel@tonic-gate 	uchar_t		flush;
6370Sstevel@tonic-gate 	uint_t		*messagep;
6380Sstevel@tonic-gate 	unlinkinfo_t	*unlinkinfop = tmxp->unlinkinfop;
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
6410Sstevel@tonic-gate 		freemsg(mp);
6420Sstevel@tonic-gate 		return (0);
6430Sstevel@tonic-gate 	}
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	/*
6460Sstevel@tonic-gate 	 * If there's already a message on our queue and the incoming
6470Sstevel@tonic-gate 	 * message is not of a high-priority, enqueue the message --
6480Sstevel@tonic-gate 	 * but not if it's a logindmux protocol message.
6490Sstevel@tonic-gate 	 */
6500Sstevel@tonic-gate 	if ((q->q_first != NULL) && (queclass(mp) < QPCTL) &&
6510Sstevel@tonic-gate 	    (!LOGDMUX_PROTO_MBLK(mp))) {
6520Sstevel@tonic-gate 		(void) putq(q, mp);
6530Sstevel@tonic-gate 		return (0);
6540Sstevel@tonic-gate 	}
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	case M_IOCTL:
6590Sstevel@tonic-gate 		ioc = (struct iocblk *)mp->b_rptr;
6600Sstevel@tonic-gate 		switch (ioc->ioc_cmd) {
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		case TIOCSWINSZ:
6630Sstevel@tonic-gate 		case TCSETAF:
6640Sstevel@tonic-gate 		case TCSETSF:
6650Sstevel@tonic-gate 		case TCSETA:
6660Sstevel@tonic-gate 		case TCSETAW:
6670Sstevel@tonic-gate 		case TCSETS:
6680Sstevel@tonic-gate 		case TCSETSW:
6690Sstevel@tonic-gate 		case TCSBRK:
6700Sstevel@tonic-gate 		case TIOCSTI:
6710Sstevel@tonic-gate 			qp = tmxp->peerq;
6720Sstevel@tonic-gate 			break;
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		default:
6750Sstevel@tonic-gate 			cmn_err(CE_NOTE, "logdmuxlrput: received unexpected"
6760Sstevel@tonic-gate 			    " request for ioctl 0x%x", ioc->ioc_cmd);
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 			/* NAK unrecognized ioctl's. */
6790Sstevel@tonic-gate 			miocnak(q, mp, 0, 0);
6800Sstevel@tonic-gate 			return (0);
6810Sstevel@tonic-gate 		}
6820Sstevel@tonic-gate 		break;
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 	case M_DATA:
6850Sstevel@tonic-gate 	case M_HANGUP:
6860Sstevel@tonic-gate 		qp = tmxp->peerq;
6870Sstevel@tonic-gate 		break;
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	case M_CTL:
6900Sstevel@tonic-gate 		/*
6910Sstevel@tonic-gate 		 * The protocol messages that flow between the peers
6920Sstevel@tonic-gate 		 * to implement the unlink functionality are M_CTLs
6930Sstevel@tonic-gate 		 * which have the M_IOCTL/I_UNLINK mblk of the ioctl
6940Sstevel@tonic-gate 		 * attached via b_cont.  LOGDMUX_PROTO_MBLK() uses
6950Sstevel@tonic-gate 		 * this to determine whether a particular M_CTL is a
6960Sstevel@tonic-gate 		 * peer protocol message.
6970Sstevel@tonic-gate 		 */
6980Sstevel@tonic-gate 		if (LOGDMUX_PROTO_MBLK(mp)) {
6990Sstevel@tonic-gate 			messagep = (uint_t *)mp->b_rptr;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 			switch (*messagep) {
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 			case LOGDMUX_UNLINK_REQ:
7040Sstevel@tonic-gate 				/*
7050Sstevel@tonic-gate 				 * We've received a message from our
7060Sstevel@tonic-gate 				 * peer indicating that it wants to
7070Sstevel@tonic-gate 				 * unlink.
7080Sstevel@tonic-gate 				 */
7090Sstevel@tonic-gate 				*messagep = LOGDMUX_UNLINK_RESP;
7100Sstevel@tonic-gate 				qp = tmxp->peerq;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 				mutex_enter(&logdmux_peerq_lock);
7130Sstevel@tonic-gate 				tmxp->peerq = NULL;
7140Sstevel@tonic-gate 				mutex_exit(&logdmux_peerq_lock);
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 				put(RD(qp), mp);
7170Sstevel@tonic-gate 				return (0);
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 			case LOGDMUX_UNLINK_RESP:
7200Sstevel@tonic-gate 				/*
7210Sstevel@tonic-gate 				 * We've received a positive response
7220Sstevel@tonic-gate 				 * from our peer to an earlier
7230Sstevel@tonic-gate 				 * LOGDMUX_UNLINK_REQ that we sent.
7240Sstevel@tonic-gate 				 * We can now carry on with the unlink.
7250Sstevel@tonic-gate 				 */
7260Sstevel@tonic-gate 				qp = tmxp->rdq;
7270Sstevel@tonic-gate 				mutex_enter(&unlinkinfop->state_lock);
7280Sstevel@tonic-gate 				ASSERT(unlinkinfop->state ==
7290Sstevel@tonic-gate 				    LOGDMUX_UNLINK_PENDING);
7300Sstevel@tonic-gate 				unlinkinfop->state = LOGDMUX_UNLINKED;
7310Sstevel@tonic-gate 				mutex_exit(&unlinkinfop->state_lock);
7320Sstevel@tonic-gate 				logdmux_finish_unlink(WR(qp), mp->b_cont);
7330Sstevel@tonic-gate 				return (0);
7340Sstevel@tonic-gate 			}
7350Sstevel@tonic-gate 		}
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 		qp = tmxp->rdq;
7380Sstevel@tonic-gate 		if (q->q_first != NULL || !canputnext(qp)) {
7390Sstevel@tonic-gate 			(void) putq(q, mp);
7400Sstevel@tonic-gate 			return (0);
7410Sstevel@tonic-gate 		}
7420Sstevel@tonic-gate 		if ((MBLKL(mp) == 1) && (*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
7430Sstevel@tonic-gate 			savemp = mp->b_cont;
7440Sstevel@tonic-gate 			freeb(mp);
7450Sstevel@tonic-gate 			mp = savemp;
7460Sstevel@tonic-gate 		}
7470Sstevel@tonic-gate 		putnext(qp, mp);
7480Sstevel@tonic-gate 		return (0);
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	case M_IOCACK:
7510Sstevel@tonic-gate 	case M_IOCNAK:
7520Sstevel@tonic-gate 	case M_PROTO:
7530Sstevel@tonic-gate 	case M_PCPROTO:
7540Sstevel@tonic-gate 	case M_PCSIG:
7550Sstevel@tonic-gate 	case M_SETOPTS:
7560Sstevel@tonic-gate 		qp = tmxp->rdq;
7570Sstevel@tonic-gate 		break;
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	case M_ERROR:
7600Sstevel@tonic-gate 		if (tmxp->isptm) {
7610Sstevel@tonic-gate 			/*
7620Sstevel@tonic-gate 			 * This error is from ptm.  We could tell TCP to
7630Sstevel@tonic-gate 			 * shutdown the connection, but it's easier to just
7640Sstevel@tonic-gate 			 * wait for the daemon to get SIGCHLD and close from
7650Sstevel@tonic-gate 			 * above.
7660Sstevel@tonic-gate 			 */
7670Sstevel@tonic-gate 			freemsg(mp);
7680Sstevel@tonic-gate 			return (0);
7690Sstevel@tonic-gate 		}
7700Sstevel@tonic-gate 		/*
7710Sstevel@tonic-gate 		 * This is from TCP.  Don't really know why we'd
7720Sstevel@tonic-gate 		 * get this, but we have a pretty good idea what
7730Sstevel@tonic-gate 		 * to do:  Send M_HANGUP to the pty.
7740Sstevel@tonic-gate 		 */
7750Sstevel@tonic-gate 		mp->b_datap->db_type = M_HANGUP;
7760Sstevel@tonic-gate 		mp->b_wptr = mp->b_rptr;
7770Sstevel@tonic-gate 		qp = tmxp->peerq;
7780Sstevel@tonic-gate 		break;
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 	case M_FLUSH:
7810Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
7820Sstevel@tonic-gate 			flushq_dataonly(q);
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 		if (mp->b_flag & MSGMARK) {
7850Sstevel@tonic-gate 			/*
7860Sstevel@tonic-gate 			 * This M_FLUSH has been marked by the module
7870Sstevel@tonic-gate 			 * below as intended for the upper queue,
7880Sstevel@tonic-gate 			 * not the peer queue.
7890Sstevel@tonic-gate 			 */
7900Sstevel@tonic-gate 			qp = tmxp->rdq;
7910Sstevel@tonic-gate 			mp->b_flag &= ~MSGMARK;
7920Sstevel@tonic-gate 		} else {
7930Sstevel@tonic-gate 			/*
7940Sstevel@tonic-gate 			 * Wrap this M_FLUSH through the mux.
7950Sstevel@tonic-gate 			 * The FLUSHR and FLUSHW bits must be
7960Sstevel@tonic-gate 			 * reversed.
7970Sstevel@tonic-gate 			 */
7980Sstevel@tonic-gate 			qp = tmxp->peerq;
7990Sstevel@tonic-gate 			flush = *mp->b_rptr;
8000Sstevel@tonic-gate 			*mp->b_rptr &= ~(FLUSHR | FLUSHW);
8010Sstevel@tonic-gate 			if (flush & FLUSHW)
8020Sstevel@tonic-gate 				*mp->b_rptr |= FLUSHR;
8030Sstevel@tonic-gate 			if (flush & FLUSHR)
8040Sstevel@tonic-gate 				*mp->b_rptr |= FLUSHW;
8050Sstevel@tonic-gate 		}
8060Sstevel@tonic-gate 		break;
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	case M_START:
8090Sstevel@tonic-gate 	case M_STOP:
8100Sstevel@tonic-gate 	case M_STARTI:
8110Sstevel@tonic-gate 	case M_STOPI:
8120Sstevel@tonic-gate 		freemsg(mp);
8130Sstevel@tonic-gate 		return (0);
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	default:
8160Sstevel@tonic-gate 		cmn_err(CE_NOTE, "logdmuxlrput: received unexpected "
8170Sstevel@tonic-gate 		    "message of type 0x%x", mp->b_datap->db_type);
8180Sstevel@tonic-gate 		freemsg(mp);
8190Sstevel@tonic-gate 		return (0);
8200Sstevel@tonic-gate 	}
8210Sstevel@tonic-gate 	if (queclass(mp) < QPCTL) {
8220Sstevel@tonic-gate 		if (q->q_first != NULL || !canputnext(qp)) {
8230Sstevel@tonic-gate 			(void) putq(q, mp);
8240Sstevel@tonic-gate 			return (0);
8250Sstevel@tonic-gate 		}
8260Sstevel@tonic-gate 	}
8270Sstevel@tonic-gate 	putnext(qp, mp);
8280Sstevel@tonic-gate 	return (0);
8290Sstevel@tonic-gate }
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate /*
8320Sstevel@tonic-gate  * Lower read service routine
8330Sstevel@tonic-gate  */
8340Sstevel@tonic-gate static int
logdmuxlrsrv(queue_t * q)8350Sstevel@tonic-gate logdmuxlrsrv(queue_t *q)
8360Sstevel@tonic-gate {
8370Sstevel@tonic-gate 	mblk_t		*mp, *savemp;
8380Sstevel@tonic-gate 	queue_t 	*qp;
8390Sstevel@tonic-gate 	struct iocblk	*ioc;
8400Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
8430Sstevel@tonic-gate 		if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
8440Sstevel@tonic-gate 			freemsg(mp);
8450Sstevel@tonic-gate 			continue;
8460Sstevel@tonic-gate 		}
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 		case M_IOCTL:
8510Sstevel@tonic-gate 			ioc = (struct iocblk *)mp->b_rptr;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 			switch (ioc->ioc_cmd) {
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 			case TIOCSWINSZ:
8560Sstevel@tonic-gate 			case TCSETAF:
8570Sstevel@tonic-gate 			case TCSETSF:
8580Sstevel@tonic-gate 			case TCSETA:
8590Sstevel@tonic-gate 			case TCSETAW:
8600Sstevel@tonic-gate 			case TCSETS:
8610Sstevel@tonic-gate 			case TCSETSW:
8620Sstevel@tonic-gate 			case TCSBRK:
8630Sstevel@tonic-gate 			case TIOCSTI:
8640Sstevel@tonic-gate 				qp = tmxp->peerq;
8650Sstevel@tonic-gate 				break;
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 			default:
8680Sstevel@tonic-gate 				cmn_err(CE_NOTE, "logdmuxlrsrv: received "
8690Sstevel@tonic-gate 				    "unexpected request for ioctl 0x%x",
8700Sstevel@tonic-gate 				    ioc->ioc_cmd);
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 				/* NAK unrecognized ioctl's. */
8730Sstevel@tonic-gate 				miocnak(q, mp, 0, 0);
8740Sstevel@tonic-gate 				continue;
8750Sstevel@tonic-gate 			}
8760Sstevel@tonic-gate 			break;
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 		case M_DATA:
8790Sstevel@tonic-gate 		case M_HANGUP:
8800Sstevel@tonic-gate 			qp = tmxp->peerq;
8810Sstevel@tonic-gate 			break;
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 		case M_CTL:
8840Sstevel@tonic-gate 			qp = tmxp->rdq;
8850Sstevel@tonic-gate 			if (!canputnext(qp)) {
8860Sstevel@tonic-gate 				(void) putbq(q, mp);
8870Sstevel@tonic-gate 				return (0);
8880Sstevel@tonic-gate 			}
8890Sstevel@tonic-gate 			if (MBLKL(mp) == 1 &&
8900Sstevel@tonic-gate 			    (*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
8910Sstevel@tonic-gate 				savemp = mp->b_cont;
8920Sstevel@tonic-gate 				freeb(mp);
8930Sstevel@tonic-gate 				mp = savemp;
8940Sstevel@tonic-gate 			}
8950Sstevel@tonic-gate 			putnext(qp, mp);
8960Sstevel@tonic-gate 			continue;
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 		case M_PROTO:
8990Sstevel@tonic-gate 		case M_SETOPTS:
9000Sstevel@tonic-gate 			qp = tmxp->rdq;
9010Sstevel@tonic-gate 			break;
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 		default:
9040Sstevel@tonic-gate 			cmn_err(CE_NOTE, "logdmuxlrsrv: received unexpected "
9050Sstevel@tonic-gate 			    "message of type 0x%x", mp->b_datap->db_type);
9060Sstevel@tonic-gate 			freemsg(mp);
9070Sstevel@tonic-gate 			continue;
9080Sstevel@tonic-gate 		}
9090Sstevel@tonic-gate 		ASSERT(queclass(mp) < QPCTL);
9100Sstevel@tonic-gate 		if (!canputnext(qp)) {
9110Sstevel@tonic-gate 			(void) putbq(q, mp);
9120Sstevel@tonic-gate 			return (0);
9130Sstevel@tonic-gate 		}
9140Sstevel@tonic-gate 		putnext(qp, mp);
9150Sstevel@tonic-gate 	}
9160Sstevel@tonic-gate 	return (0);
9170Sstevel@tonic-gate }
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate /*
9200Sstevel@tonic-gate  * Lower side write service procedure.  No messages are ever placed on
9210Sstevel@tonic-gate  * the write queue here, this just back-enables all of the upper side
9220Sstevel@tonic-gate  * write service procedures.
9230Sstevel@tonic-gate  */
9240Sstevel@tonic-gate static int
logdmuxlwsrv(queue_t * q)9250Sstevel@tonic-gate logdmuxlwsrv(queue_t *q)
9260Sstevel@tonic-gate {
9270Sstevel@tonic-gate 	struct tmx *tmxp = q->q_ptr;
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	/*
9300Sstevel@tonic-gate 	 * Qenable upper write queue and find out which lower
9310Sstevel@tonic-gate 	 * queue needs to be restarted with flow control.
9320Sstevel@tonic-gate 	 * Qenable the peer queue so canputnext will
9330Sstevel@tonic-gate 	 * succeed on next call to logdmuxlrput.
9340Sstevel@tonic-gate 	 */
9350Sstevel@tonic-gate 	qenable(WR(tmxp->rdq));
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	mutex_enter(&logdmux_peerq_lock);
9380Sstevel@tonic-gate 	if (tmxp->peerq != NULL)
9390Sstevel@tonic-gate 		qenable(RD(tmxp->peerq));
9400Sstevel@tonic-gate 	mutex_exit(&logdmux_peerq_lock);
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 	return (0);
9430Sstevel@tonic-gate }
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate /*
9460Sstevel@tonic-gate  * This routine does I_LINK operation.
9470Sstevel@tonic-gate  */
9480Sstevel@tonic-gate static void
logdmuxlink(queue_t * q,mblk_t * mp)9490Sstevel@tonic-gate logdmuxlink(queue_t *q, mblk_t *mp)
9500Sstevel@tonic-gate {
9510Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
9520Sstevel@tonic-gate 	struct linkblk	*lp = (struct linkblk *)mp->b_cont->b_rptr;
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	/*
9550Sstevel@tonic-gate 	 * Fail if we're already linked.
9560Sstevel@tonic-gate 	 */
9570Sstevel@tonic-gate 	if (tmxp->muxq != NULL) {
9580Sstevel@tonic-gate 		miocnak(q, mp, 0, EINVAL);
9590Sstevel@tonic-gate 		return;
9600Sstevel@tonic-gate 	}
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	tmxp->muxq = lp->l_qbot;
9630Sstevel@tonic-gate 	tmxp->muxq->q_ptr = tmxp;
9640Sstevel@tonic-gate 	RD(tmxp->muxq)->q_ptr = tmxp;
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	miocack(q, mp, 0, 0);
9670Sstevel@tonic-gate }
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate /*
9700Sstevel@tonic-gate  * logdmuxunlink() is called from logdmuxuwput() and is the first of two
9710Sstevel@tonic-gate  * functions which process an I_UNLINK ioctl. logdmuxunlink() will determine
9720Sstevel@tonic-gate  * the state of logindmux peer linkage and, based on this, control when the
9730Sstevel@tonic-gate  * second function, logdmux_finish_unlink(), is called.  It's
9740Sstevel@tonic-gate  * logdmux_finish_unlink() that's sending the M_IOCACK upstream and
9750Sstevel@tonic-gate  * resetting the link state.
9760Sstevel@tonic-gate  */
9770Sstevel@tonic-gate static void
logdmuxunlink(queue_t * q,mblk_t * mp)9780Sstevel@tonic-gate logdmuxunlink(queue_t *q, mblk_t *mp)
9790Sstevel@tonic-gate {
9800Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
9810Sstevel@tonic-gate 	unlinkinfo_t	*unlinkinfop;
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	/*
9840Sstevel@tonic-gate 	 * If we don't have a peer, just unlink.  Note that this check needs
9850Sstevel@tonic-gate 	 * to be done under logdmux_qexch_lock to prevent racing with
9860Sstevel@tonic-gate 	 * LOGDMX_IOC_QEXCHANGE, and we *must* set muxq to NULL prior to
9870Sstevel@tonic-gate 	 * releasing the lock so that LOGDMX_IOC_QEXCHANGE will not consider
9880Sstevel@tonic-gate 	 * us as a possible peer anymore (if it already considers us to be a
9890Sstevel@tonic-gate 	 * peer, then unlinkinfop will not be NULL) -- NULLing muxq precludes
9900Sstevel@tonic-gate 	 * use of logdmux_finish_unlink() here.
9910Sstevel@tonic-gate 	 */
9920Sstevel@tonic-gate 	mutex_enter(&logdmux_qexch_lock);
9930Sstevel@tonic-gate 	unlinkinfop = tmxp->unlinkinfop;
9940Sstevel@tonic-gate 	if (unlinkinfop == NULL) {
9950Sstevel@tonic-gate 		ASSERT(tmxp->peerq == NULL);
9960Sstevel@tonic-gate 		tmxp->muxq = NULL;
9970Sstevel@tonic-gate 		mutex_exit(&logdmux_qexch_lock);
9980Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
9990Sstevel@tonic-gate 		return;
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 	mutex_exit(&logdmux_qexch_lock);
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	mutex_enter(&unlinkinfop->state_lock);
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	switch (unlinkinfop->state) {
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	case LOGDMUX_LINKED:
10080Sstevel@tonic-gate 		/*
10090Sstevel@tonic-gate 		 * We're the first instance to process an I_UNLINK --
10100Sstevel@tonic-gate 		 * ie, the peer instance is still there. We'll change
10110Sstevel@tonic-gate 		 * the state so that only one instance is executing an
10120Sstevel@tonic-gate 		 * I_UNLINK at any one time.
10130Sstevel@tonic-gate 		 */
10140Sstevel@tonic-gate 		unlinkinfop->state = LOGDMUX_UNLINK_PENDING;
10150Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
10160Sstevel@tonic-gate 		/*
10170Sstevel@tonic-gate 		 * Attach the original M_IOCTL message to a
10180Sstevel@tonic-gate 		 * LOGDMUX_UNLINK_REQ message and send it to our peer to
10190Sstevel@tonic-gate 		 * tell it to unlink from us. When it has completed the
10200Sstevel@tonic-gate 		 * task, it will send us a LOGDMUX_UNLINK_RESP message
10210Sstevel@tonic-gate 		 * with the original M_IOCTL still attached, which will be
10220Sstevel@tonic-gate 		 * processed in our logdmuxlrput(). At that point, we will
10230Sstevel@tonic-gate 		 * call logdmux_finish_unlink() to complete the unlink
10240Sstevel@tonic-gate 		 * operation using the attached M_IOCTL.
10250Sstevel@tonic-gate 		 */
10260Sstevel@tonic-gate 		unlinkinfop->prot_mp->b_cont = mp;
10270Sstevel@tonic-gate 		/*
10280Sstevel@tonic-gate 		 * Put the M_CTL directly to the peer's lower RQ.
10290Sstevel@tonic-gate 		 */
10300Sstevel@tonic-gate 		put(RD(tmxp->peerq), unlinkinfop->prot_mp);
10310Sstevel@tonic-gate 		break;
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate 	case LOGDMUX_UNLINK_PENDING:
10340Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
10350Sstevel@tonic-gate 		/*
10360Sstevel@tonic-gate 		 * Our peer is actively processing an I_UNLINK itself.
10370Sstevel@tonic-gate 		 * We have to wait for the peer to complete and we use
10380Sstevel@tonic-gate 		 * qtimeout as a way to poll for its completion.
10390Sstevel@tonic-gate 		 * We save a reference to our mblk so that we can send
10400Sstevel@tonic-gate 		 * it upstream once our peer is done.
10410Sstevel@tonic-gate 		 */
10420Sstevel@tonic-gate 		tmxp->unlink_mp = mp;
10430Sstevel@tonic-gate 		tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
10440Sstevel@tonic-gate 		    drv_usectohz(LOGDMUX_POLL_WAIT));
10450Sstevel@tonic-gate 		break;
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	case LOGDMUX_UNLINKED:
10480Sstevel@tonic-gate 		/*
10490Sstevel@tonic-gate 		 * Our peer is no longer linked so we can proceed.
10500Sstevel@tonic-gate 		 */
10510Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
10520Sstevel@tonic-gate 		mutex_destroy(&unlinkinfop->state_lock);
10530Sstevel@tonic-gate 		freeb(unlinkinfop->prot_mp);
10540Sstevel@tonic-gate 		kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
10550Sstevel@tonic-gate 		logdmux_finish_unlink(q, mp);
10560Sstevel@tonic-gate 		break;
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	default:
10590Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
10600Sstevel@tonic-gate 		cmn_err(CE_PANIC,
10610Sstevel@tonic-gate 		    "logdmuxunlink: peer linkage is in an unrecognized state");
10620Sstevel@tonic-gate 		break;
10630Sstevel@tonic-gate 	}
10640Sstevel@tonic-gate }
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate /*
10670Sstevel@tonic-gate  * Finish the unlink operation.  Note that no locks should be held since
10680Sstevel@tonic-gate  * this routine calls into other queues.
10690Sstevel@tonic-gate  */
10700Sstevel@tonic-gate static void
logdmux_finish_unlink(queue_t * q,mblk_t * unlink_mp)10710Sstevel@tonic-gate logdmux_finish_unlink(queue_t *q, mblk_t *unlink_mp)
10720Sstevel@tonic-gate {
10730Sstevel@tonic-gate 	struct tmx *tmxp = q->q_ptr;
10740Sstevel@tonic-gate 	mblk_t *mp;
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	/*
10770Sstevel@tonic-gate 	 * Flush any write side data downstream.
10780Sstevel@tonic-gate 	 */
10790Sstevel@tonic-gate 	while ((mp = getq(WR(q))) != NULL)
10800Sstevel@tonic-gate 		putnext(tmxp->muxq, mp);
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 	/*
10830Sstevel@tonic-gate 	 * Note that we do not NULL out q_ptr since another thread (e.g., a
10840Sstevel@tonic-gate 	 * STREAMS service thread) might call logdmuxlrput() between the time
10850Sstevel@tonic-gate 	 * we exit the logindmux perimeter and the time the STREAMS framework
10860Sstevel@tonic-gate 	 * resets q_ptr to stdata (since muxq is set to NULL, any messages
10870Sstevel@tonic-gate 	 * will just be discarded).
10880Sstevel@tonic-gate 	 */
10890Sstevel@tonic-gate 	tmxp->muxq = NULL;
10900Sstevel@tonic-gate 	tmxp->unlinkinfop = NULL;
10910Sstevel@tonic-gate 	tmxp->peerq = NULL;
10920Sstevel@tonic-gate 	miocack(q, unlink_mp, 0, 0);
10930Sstevel@tonic-gate }
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate /*
10960Sstevel@tonic-gate  * logdmux_unlink_timer() is executed by qtimeout(). This function will
10970Sstevel@tonic-gate  * check unlinkinfop->state to determine whether the peer has completed
10980Sstevel@tonic-gate  * its I_UNLINK. If it hasn't, we use qtimeout() to initiate another poll.
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate static void
logdmux_unlink_timer(void * arg)11010Sstevel@tonic-gate logdmux_unlink_timer(void *arg)
11020Sstevel@tonic-gate {
11030Sstevel@tonic-gate 	queue_t		*q = arg;
11040Sstevel@tonic-gate 	struct	tmx	*tmxp = q->q_ptr;
11050Sstevel@tonic-gate 	unlinkinfo_t	*unlinkinfop = tmxp->unlinkinfop;
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	tmxp->utimoutid = 0;
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 	mutex_enter(&unlinkinfop->state_lock);
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 	if (unlinkinfop->state != LOGDMUX_UNLINKED) {
11120Sstevel@tonic-gate 		ASSERT(unlinkinfop->state == LOGDMUX_UNLINK_PENDING);
11130Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
11140Sstevel@tonic-gate 		/*
11150Sstevel@tonic-gate 		 * We need to wait longer for our peer to complete.
11160Sstevel@tonic-gate 		 */
11170Sstevel@tonic-gate 		tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
11180Sstevel@tonic-gate 		    drv_usectohz(LOGDMUX_POLL_WAIT));
11190Sstevel@tonic-gate 	} else {
11200Sstevel@tonic-gate 		/*
11210Sstevel@tonic-gate 		 * Our peer is no longer linked so we can proceed with
11220Sstevel@tonic-gate 		 * the cleanup.
11230Sstevel@tonic-gate 		 */
11240Sstevel@tonic-gate 		mutex_exit(&unlinkinfop->state_lock);
11250Sstevel@tonic-gate 		mutex_destroy(&unlinkinfop->state_lock);
11260Sstevel@tonic-gate 		freeb(unlinkinfop->prot_mp);
11270Sstevel@tonic-gate 		kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
11280Sstevel@tonic-gate 		logdmux_finish_unlink(q, tmxp->unlink_mp);
11290Sstevel@tonic-gate 	}
11300Sstevel@tonic-gate }
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate static void
logdmux_timer(void * arg)11330Sstevel@tonic-gate logdmux_timer(void *arg)
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	queue_t		*q = arg;
11360Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	ASSERT(tmxp != NULL);
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
11410Sstevel@tonic-gate 		ASSERT(tmxp->rtimoutid != 0);
11420Sstevel@tonic-gate 		tmxp->rtimoutid = 0;
11430Sstevel@tonic-gate 	} else {
11440Sstevel@tonic-gate 		ASSERT(tmxp->wtimoutid != 0);
11450Sstevel@tonic-gate 		tmxp->wtimoutid = 0;
11460Sstevel@tonic-gate 	}
11470Sstevel@tonic-gate 	enableok(q);
11480Sstevel@tonic-gate 	qenable(q);
11490Sstevel@tonic-gate }
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate static void
logdmux_buffer(void * arg)11520Sstevel@tonic-gate logdmux_buffer(void *arg)
11530Sstevel@tonic-gate {
11540Sstevel@tonic-gate 	queue_t		*q = arg;
11550Sstevel@tonic-gate 	struct tmx	*tmxp = q->q_ptr;
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	ASSERT(tmxp != NULL);
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
11600Sstevel@tonic-gate 		ASSERT(tmxp->rbufcid != 0);
11610Sstevel@tonic-gate 		tmxp->rbufcid = 0;
11620Sstevel@tonic-gate 	} else {
11630Sstevel@tonic-gate 		ASSERT(tmxp->wbufcid != 0);
11640Sstevel@tonic-gate 		tmxp->wbufcid = 0;
11650Sstevel@tonic-gate 	}
11660Sstevel@tonic-gate 	enableok(q);
11670Sstevel@tonic-gate 	qenable(q);
11680Sstevel@tonic-gate }
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate static void
recover(queue_t * q,mblk_t * mp,size_t size)11710Sstevel@tonic-gate recover(queue_t *q, mblk_t *mp, size_t size)
11720Sstevel@tonic-gate {
11730Sstevel@tonic-gate 	timeout_id_t	tid;
11740Sstevel@tonic-gate 	bufcall_id_t	bid;
11750Sstevel@tonic-gate 	struct	tmx	*tmxp = q->q_ptr;
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	/*
11780Sstevel@tonic-gate 	 * Avoid re-enabling the queue.
11790Sstevel@tonic-gate 	 */
11800Sstevel@tonic-gate 	ASSERT(queclass(mp) < QPCTL);
11810Sstevel@tonic-gate 	ASSERT(WR(q)->q_next == NULL); /* Called from upper queue only */
11820Sstevel@tonic-gate 	noenable(q);
11830Sstevel@tonic-gate 	(void) putbq(q, mp);
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 	/*
11860Sstevel@tonic-gate 	 * Make sure there is at most one outstanding request per queue.
11870Sstevel@tonic-gate 	 */
11880Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
11890Sstevel@tonic-gate 		if (tmxp->rtimoutid != 0 || tmxp->rbufcid != 0)
11900Sstevel@tonic-gate 			return;
11910Sstevel@tonic-gate 	} else {
11920Sstevel@tonic-gate 		if (tmxp->wtimoutid != 0 || tmxp->wbufcid != 0)
11930Sstevel@tonic-gate 			return;
11940Sstevel@tonic-gate 	}
11950Sstevel@tonic-gate 	if (!(bid = qbufcall(RD(q), size, BPRI_MED, logdmux_buffer, q))) {
11960Sstevel@tonic-gate 		tid = qtimeout(RD(q), logdmux_timer, q, drv_usectohz(SIMWAIT));
11970Sstevel@tonic-gate 		if (q->q_flag & QREADR)
11980Sstevel@tonic-gate 			tmxp->rtimoutid = tid;
11990Sstevel@tonic-gate 		else
12000Sstevel@tonic-gate 			tmxp->wtimoutid = tid;
12010Sstevel@tonic-gate 	} else	{
12020Sstevel@tonic-gate 		if (q->q_flag & QREADR)
12030Sstevel@tonic-gate 			tmxp->rbufcid = bid;
12040Sstevel@tonic-gate 		else
12050Sstevel@tonic-gate 			tmxp->wbufcid = bid;
12060Sstevel@tonic-gate 	}
12070Sstevel@tonic-gate }
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate static void
flushq_dataonly(queue_t * q)12100Sstevel@tonic-gate flushq_dataonly(queue_t *q)
12110Sstevel@tonic-gate {
12120Sstevel@tonic-gate 	mblk_t *mp, *nmp;
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	/*
12150Sstevel@tonic-gate 	 * Since we are already in the perimeter, and we are not a put-shared
12160Sstevel@tonic-gate 	 * perimeter, we don't need to freeze the stream or anything to
12170Sstevel@tonic-gate 	 * be ensured of exclusivity.
12180Sstevel@tonic-gate 	 */
12190Sstevel@tonic-gate 	mp = q->q_first;
12200Sstevel@tonic-gate 	while (mp != NULL) {
12210Sstevel@tonic-gate 		if (mp->b_datap->db_type == M_DATA) {
12220Sstevel@tonic-gate 			nmp = mp->b_next;
12230Sstevel@tonic-gate 			rmvq(q, mp);
12240Sstevel@tonic-gate 			freemsg(mp);
12250Sstevel@tonic-gate 			mp = nmp;
12260Sstevel@tonic-gate 		} else {
12270Sstevel@tonic-gate 			mp = mp->b_next;
12280Sstevel@tonic-gate 		}
12290Sstevel@tonic-gate 	}
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate /*
12330Sstevel@tonic-gate  * logdmux_alloc_unlinkinfo() is called from logdmuxuwput() during the
12340Sstevel@tonic-gate  * processing of a LOGDMX_IOC_QEXCHANGE ioctl() to allocate the
12350Sstevel@tonic-gate  * unlinkinfo_t which is needed during the processing of an I_UNLINK.
12360Sstevel@tonic-gate  */
12370Sstevel@tonic-gate static int
logdmux_alloc_unlinkinfo(struct tmx * t0,struct tmx * t1)12380Sstevel@tonic-gate logdmux_alloc_unlinkinfo(struct tmx *t0, struct tmx *t1)
12390Sstevel@tonic-gate {
12400Sstevel@tonic-gate 	unlinkinfo_t	*p;
12410Sstevel@tonic-gate 	uint_t		*messagep;
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	if ((p = kmem_zalloc(sizeof (unlinkinfo_t), KM_NOSLEEP)) == NULL)
12440Sstevel@tonic-gate 		return (ENOSR);
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	if ((p->prot_mp = allocb(sizeof (uint_t), BPRI_MED)) == NULL) {
12470Sstevel@tonic-gate 		kmem_free(p, sizeof (unlinkinfo_t));
12480Sstevel@tonic-gate 		return (ENOSR);
12490Sstevel@tonic-gate 	}
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 	DB_TYPE(p->prot_mp) = M_CTL;
12520Sstevel@tonic-gate 	messagep = (uint_t *)p->prot_mp->b_wptr;
12530Sstevel@tonic-gate 	*messagep = LOGDMUX_UNLINK_REQ;
12540Sstevel@tonic-gate 	p->prot_mp->b_wptr += sizeof (*messagep);
12550Sstevel@tonic-gate 	p->state = LOGDMUX_LINKED;
12560Sstevel@tonic-gate 	mutex_init(&p->state_lock, NULL, MUTEX_DRIVER, NULL);
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	t0->unlinkinfop = t1->unlinkinfop = p;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	return (0);
12610Sstevel@tonic-gate }
1262