xref: /illumos-gate/usr/src/uts/common/io/bufmod.c (revision eb00302c9a73ef4cd8d5e1958b3356988094c00f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*eb00302cSRyan Zezeski  * Copyright 2018 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * STREAMS Buffering module
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * This streams module collects incoming messages from modules below
327c478bd9Sstevel@tonic-gate  * it on the stream and buffers them up into a smaller number of
337c478bd9Sstevel@tonic-gate  * aggregated messages.  Its main purpose is to reduce overhead by
347c478bd9Sstevel@tonic-gate  * cutting down on the number of read (or getmsg) calls its client
357c478bd9Sstevel@tonic-gate  * user process makes.
367c478bd9Sstevel@tonic-gate  *  - only M_DATA is buffered.
377c478bd9Sstevel@tonic-gate  *  - multithreading assumes configured as D_MTQPAIR
387c478bd9Sstevel@tonic-gate  *  - packets are lost only if flag SB_NO_HEADER is clear and buffer
397c478bd9Sstevel@tonic-gate  *    allocation fails.
407c478bd9Sstevel@tonic-gate  *  - in order message transmission. This is enforced for messages other
417c478bd9Sstevel@tonic-gate  *    than high priority messages.
427c478bd9Sstevel@tonic-gate  *  - zero length messages on the read side are not passed up the
437c478bd9Sstevel@tonic-gate  *    stream but used internally for synchronization.
447c478bd9Sstevel@tonic-gate  * FLAGS:
457c478bd9Sstevel@tonic-gate  * - SB_NO_PROTO_CVT - no conversion of M_PROTO messages to M_DATA.
467c478bd9Sstevel@tonic-gate  *   (conversion is the default for backwards compatibility
477c478bd9Sstevel@tonic-gate  *    hence the negative logic).
487c478bd9Sstevel@tonic-gate  * - SB_NO_HEADER - no headers in buffered data.
497c478bd9Sstevel@tonic-gate  *   (adding headers is the default for backwards compatibility
507c478bd9Sstevel@tonic-gate  *    hence the negative logic).
517c478bd9Sstevel@tonic-gate  * - SB_DEFER_CHUNK - provides improved response time in question-answer
527c478bd9Sstevel@tonic-gate  *   applications. Buffering is not enabled until the second message
537c478bd9Sstevel@tonic-gate  *   is received on the read side within the sb_ticks interval.
547c478bd9Sstevel@tonic-gate  *   This option will often be used in combination with flag SB_SEND_ON_WRITE.
557c478bd9Sstevel@tonic-gate  * - SB_SEND_ON_WRITE - a write message results in any pending buffered read
567c478bd9Sstevel@tonic-gate  *   data being immediately sent upstream.
577c478bd9Sstevel@tonic-gate  * - SB_NO_DROPS - bufmod behaves transparently in flow control and propagates
587c478bd9Sstevel@tonic-gate  *   the blocked flow condition downstream. If this flag is clear (default)
597c478bd9Sstevel@tonic-gate  *   messages will be dropped if the upstream flow is blocked.
607c478bd9Sstevel@tonic-gate  */
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include	<sys/types.h>
647c478bd9Sstevel@tonic-gate #include	<sys/errno.h>
657c478bd9Sstevel@tonic-gate #include	<sys/debug.h>
667c478bd9Sstevel@tonic-gate #include	<sys/stropts.h>
677c478bd9Sstevel@tonic-gate #include	<sys/time.h>
687c478bd9Sstevel@tonic-gate #include	<sys/stream.h>
697c478bd9Sstevel@tonic-gate #include	<sys/conf.h>
707c478bd9Sstevel@tonic-gate #include	<sys/ddi.h>
717c478bd9Sstevel@tonic-gate #include	<sys/sunddi.h>
727c478bd9Sstevel@tonic-gate #include	<sys/kmem.h>
737c478bd9Sstevel@tonic-gate #include	<sys/strsun.h>
747c478bd9Sstevel@tonic-gate #include	<sys/bufmod.h>
757c478bd9Sstevel@tonic-gate #include	<sys/modctl.h>
767c478bd9Sstevel@tonic-gate #include	<sys/isa_defs.h>
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * Per-Stream state information.
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  * If sb_ticks is negative, we don't deliver chunks until they're
827c478bd9Sstevel@tonic-gate  * full.  If it's zero, we deliver every packet as it arrives.  (In
837c478bd9Sstevel@tonic-gate  * this case we force sb_chunk to zero, to make the implementation
847c478bd9Sstevel@tonic-gate  * easier.)  Otherwise, sb_ticks gives the number of ticks in a
857c478bd9Sstevel@tonic-gate  * buffering interval. The interval begins when the a read side data
867c478bd9Sstevel@tonic-gate  * message is received and a timeout is not active. If sb_snap is
877c478bd9Sstevel@tonic-gate  * zero, no truncation of the msg is done.
887c478bd9Sstevel@tonic-gate  */
897c478bd9Sstevel@tonic-gate struct sb {
907c478bd9Sstevel@tonic-gate 	queue_t	*sb_rq;		/* our rq */
917c478bd9Sstevel@tonic-gate 	mblk_t	*sb_mp;		/* partial chunk */
927c478bd9Sstevel@tonic-gate 	mblk_t  *sb_head;	/* pre-allocated space for the next header */
937c478bd9Sstevel@tonic-gate 	mblk_t	*sb_tail;	/* first mblk of last message appended */
947c478bd9Sstevel@tonic-gate 	uint_t	sb_mlen;	/* sb_mp length */
957c478bd9Sstevel@tonic-gate 	uint_t	sb_mcount;	/* input msg count in sb_mp */
967c478bd9Sstevel@tonic-gate 	uint_t	sb_chunk;	/* max chunk size */
977c478bd9Sstevel@tonic-gate 	clock_t	sb_ticks;	/* timeout interval */
987c478bd9Sstevel@tonic-gate 	timeout_id_t sb_timeoutid; /* qtimeout() id */
997c478bd9Sstevel@tonic-gate 	uint_t	sb_drops;	/* cumulative # discarded msgs */
1007c478bd9Sstevel@tonic-gate 	uint_t	sb_snap;	/* snapshot length */
1017c478bd9Sstevel@tonic-gate 	uint_t	sb_flags;	/* flags field */
1027c478bd9Sstevel@tonic-gate 	uint_t	sb_state;	/* state variable */
1037c478bd9Sstevel@tonic-gate };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Function prototypes.
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate static	int	sbopen(queue_t *, dev_t *, int, int, cred_t *);
1097c478bd9Sstevel@tonic-gate static	int	sbclose(queue_t *, int, cred_t *);
1107c478bd9Sstevel@tonic-gate static	void	sbwput(queue_t *, mblk_t *);
1117c478bd9Sstevel@tonic-gate static	void	sbrput(queue_t *, mblk_t *);
1127c478bd9Sstevel@tonic-gate static	void	sbrsrv(queue_t *);
1137c478bd9Sstevel@tonic-gate static	void	sbioctl(queue_t *, mblk_t *);
1147c478bd9Sstevel@tonic-gate static	void	sbaddmsg(queue_t *, mblk_t *);
1157c478bd9Sstevel@tonic-gate static	void	sbtick(void *);
1167c478bd9Sstevel@tonic-gate static	void	sbclosechunk(struct sb *);
1177c478bd9Sstevel@tonic-gate static	void	sbsendit(queue_t *, mblk_t *);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate static struct module_info	sb_minfo = {
1207c478bd9Sstevel@tonic-gate 	21,		/* mi_idnum */
1217c478bd9Sstevel@tonic-gate 	"bufmod",	/* mi_idname */
1227c478bd9Sstevel@tonic-gate 	0,		/* mi_minpsz */
1237c478bd9Sstevel@tonic-gate 	INFPSZ,		/* mi_maxpsz */
1247c478bd9Sstevel@tonic-gate 	1,		/* mi_hiwat */
1257c478bd9Sstevel@tonic-gate 	0		/* mi_lowat */
1267c478bd9Sstevel@tonic-gate };
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate static struct qinit	sb_rinit = {
1297c478bd9Sstevel@tonic-gate 	(int (*)())sbrput,	/* qi_putp */
1307c478bd9Sstevel@tonic-gate 	(int (*)())sbrsrv,	/* qi_srvp */
1317c478bd9Sstevel@tonic-gate 	sbopen,			/* qi_qopen */
1327c478bd9Sstevel@tonic-gate 	sbclose,		/* qi_qclose */
1337c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1347c478bd9Sstevel@tonic-gate 	&sb_minfo,		/* qi_minfo */
1357c478bd9Sstevel@tonic-gate 	NULL			/* qi_mstat */
1367c478bd9Sstevel@tonic-gate };
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate static struct qinit	sb_winit = {
1397c478bd9Sstevel@tonic-gate 	(int (*)())sbwput,	/* qi_putp */
1407c478bd9Sstevel@tonic-gate 	NULL,			/* qi_srvp */
1417c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qopen */
1427c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qclose */
1437c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1447c478bd9Sstevel@tonic-gate 	&sb_minfo,		/* qi_minfo */
1457c478bd9Sstevel@tonic-gate 	NULL			/* qi_mstat */
1467c478bd9Sstevel@tonic-gate };
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate static struct streamtab	sb_info = {
1497c478bd9Sstevel@tonic-gate 	&sb_rinit,	/* st_rdinit */
1507c478bd9Sstevel@tonic-gate 	&sb_winit,	/* st_wrinit */
1517c478bd9Sstevel@tonic-gate 	NULL,		/* st_muxrinit */
1527c478bd9Sstevel@tonic-gate 	NULL		/* st_muxwinit */
1537c478bd9Sstevel@tonic-gate };
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate  * This is the loadable module wrapper.
1587c478bd9Sstevel@tonic-gate  */
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate static struct fmodsw fsw = {
1617c478bd9Sstevel@tonic-gate 	"bufmod",
1627c478bd9Sstevel@tonic-gate 	&sb_info,
1637c478bd9Sstevel@tonic-gate 	D_MTQPAIR | D_MP
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /*
1677c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
1687c478bd9Sstevel@tonic-gate  */
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate static struct modlstrmod modlstrmod = {
1717c478bd9Sstevel@tonic-gate 	&mod_strmodops, "streams buffer mod", &fsw
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1757c478bd9Sstevel@tonic-gate 	MODREV_1, &modlstrmod, NULL
1767c478bd9Sstevel@tonic-gate };
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate int
1807c478bd9Sstevel@tonic-gate _init(void)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1837c478bd9Sstevel@tonic-gate }
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate int
1867c478bd9Sstevel@tonic-gate _fini(void)
1877c478bd9Sstevel@tonic-gate {
1887c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate int
1927c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate /* ARGSUSED */
1997c478bd9Sstevel@tonic-gate static int
2007c478bd9Sstevel@tonic-gate sbopen(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp)
2017c478bd9Sstevel@tonic-gate {
2027c478bd9Sstevel@tonic-gate 	struct sb	*sbp;
2037c478bd9Sstevel@tonic-gate 	ASSERT(rq);
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
2067c478bd9Sstevel@tonic-gate 		return (EINVAL);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	if (rq->q_ptr)
2097c478bd9Sstevel@tonic-gate 		return (0);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	/*
2127c478bd9Sstevel@tonic-gate 	 * Allocate and initialize per-Stream structure.
2137c478bd9Sstevel@tonic-gate 	 */
2147c478bd9Sstevel@tonic-gate 	sbp = kmem_alloc(sizeof (struct sb), KM_SLEEP);
2157c478bd9Sstevel@tonic-gate 	sbp->sb_rq = rq;
2167c478bd9Sstevel@tonic-gate 	sbp->sb_ticks = -1;
2177c478bd9Sstevel@tonic-gate 	sbp->sb_chunk = SB_DFLT_CHUNK;
2187c478bd9Sstevel@tonic-gate 	sbp->sb_tail = sbp->sb_mp = sbp->sb_head = NULL;
2197c478bd9Sstevel@tonic-gate 	sbp->sb_mlen = 0;
2207c478bd9Sstevel@tonic-gate 	sbp->sb_mcount = 0;
2217c478bd9Sstevel@tonic-gate 	sbp->sb_timeoutid = 0;
2227c478bd9Sstevel@tonic-gate 	sbp->sb_drops = 0;
2237c478bd9Sstevel@tonic-gate 	sbp->sb_snap = 0;
2247c478bd9Sstevel@tonic-gate 	sbp->sb_flags = 0;
2257c478bd9Sstevel@tonic-gate 	sbp->sb_state = 0;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = sbp;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	qprocson(rq);
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	return (0);
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2367c478bd9Sstevel@tonic-gate static int
2377c478bd9Sstevel@tonic-gate sbclose(queue_t *rq, int flag, cred_t *credp)
2387c478bd9Sstevel@tonic-gate {
2397c478bd9Sstevel@tonic-gate 	struct	sb	*sbp = (struct sb *)rq->q_ptr;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	ASSERT(sbp);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	qprocsoff(rq);
2447c478bd9Sstevel@tonic-gate 	/*
2457c478bd9Sstevel@tonic-gate 	 * Cancel an outstanding timeout
2467c478bd9Sstevel@tonic-gate 	 */
2477c478bd9Sstevel@tonic-gate 	if (sbp->sb_timeoutid != 0) {
2487c478bd9Sstevel@tonic-gate 		(void) quntimeout(rq, sbp->sb_timeoutid);
2497c478bd9Sstevel@tonic-gate 		sbp->sb_timeoutid = 0;
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	 * Free the current chunk.
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	if (sbp->sb_mp) {
2557c478bd9Sstevel@tonic-gate 		freemsg(sbp->sb_mp);
2567c478bd9Sstevel@tonic-gate 		sbp->sb_tail = sbp->sb_mp = sbp->sb_head = NULL;
2577c478bd9Sstevel@tonic-gate 		sbp->sb_mlen = 0;
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	/*
2617c478bd9Sstevel@tonic-gate 	 * Free the per-Stream structure.
2627c478bd9Sstevel@tonic-gate 	 */
2637c478bd9Sstevel@tonic-gate 	kmem_free((caddr_t)sbp, sizeof (struct sb));
2647c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = NULL;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	return (0);
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate /*
2707c478bd9Sstevel@tonic-gate  * the correction factor is introduced to compensate for
2717c478bd9Sstevel@tonic-gate  * whatever assumptions the modules below have made about
2727c478bd9Sstevel@tonic-gate  * how much traffic is flowing through the stream and the fact
2737c478bd9Sstevel@tonic-gate  * that bufmod may be snipping messages with the sb_snap length.
2747c478bd9Sstevel@tonic-gate  */
2757c478bd9Sstevel@tonic-gate #define	SNIT_HIWAT(msgsize, fudge)	((4 * msgsize * fudge) + 512)
2767c478bd9Sstevel@tonic-gate #define	SNIT_LOWAT(msgsize, fudge)	((2 * msgsize * fudge) + 256)
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate static void
2807c478bd9Sstevel@tonic-gate sbioc(queue_t *wq, mblk_t *mp)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
2837c478bd9Sstevel@tonic-gate 	struct sb *sbp = (struct sb *)wq->q_ptr;
2847c478bd9Sstevel@tonic-gate 	clock_t	ticks;
2857c478bd9Sstevel@tonic-gate 	mblk_t	*mop;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
2907c478bd9Sstevel@tonic-gate 	case SBIOCGCHUNK:
2917c478bd9Sstevel@tonic-gate 	case SBIOCGSNAP:
2927c478bd9Sstevel@tonic-gate 	case SBIOCGFLAGS:
2937c478bd9Sstevel@tonic-gate 	case SBIOCGTIME:
2947c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
2957c478bd9Sstevel@tonic-gate 		return;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	case SBIOCSTIME:
2987c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
2997c478bd9Sstevel@tonic-gate 		if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
3007c478bd9Sstevel@tonic-gate 			struct timeval32 *t32;
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 			t32 = (struct timeval32 *)mp->b_cont->b_rptr;
3037c478bd9Sstevel@tonic-gate 			if (t32->tv_sec < 0 || t32->tv_usec < 0) {
3047c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, EINVAL);
3057c478bd9Sstevel@tonic-gate 				break;
3067c478bd9Sstevel@tonic-gate 			}
3077c478bd9Sstevel@tonic-gate 			ticks = TIMEVAL_TO_TICK(t32);
3087c478bd9Sstevel@tonic-gate 		} else
3097c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */
3107c478bd9Sstevel@tonic-gate 		{
3117c478bd9Sstevel@tonic-gate 			struct timeval *tb;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 			tb = (struct timeval *)mp->b_cont->b_rptr;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 			if (tb->tv_sec < 0 || tb->tv_usec < 0) {
3167c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, EINVAL);
3177c478bd9Sstevel@tonic-gate 				break;
3187c478bd9Sstevel@tonic-gate 			}
3197c478bd9Sstevel@tonic-gate 			ticks = TIMEVAL_TO_TICK(tb);
3207c478bd9Sstevel@tonic-gate 		}
3217c478bd9Sstevel@tonic-gate 		sbp->sb_ticks = ticks;
3227c478bd9Sstevel@tonic-gate 		if (ticks == 0)
3237c478bd9Sstevel@tonic-gate 			sbp->sb_chunk = 0;
3247c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
3257c478bd9Sstevel@tonic-gate 		sbclosechunk(sbp);
3267c478bd9Sstevel@tonic-gate 		return;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	case SBIOCSCHUNK:
3297c478bd9Sstevel@tonic-gate 		/*
3307c478bd9Sstevel@tonic-gate 		 * set up hi/lo water marks on stream head read queue.
3317c478bd9Sstevel@tonic-gate 		 * unlikely to run out of resources. Fix at later date.
3327c478bd9Sstevel@tonic-gate 		 */
3337c478bd9Sstevel@tonic-gate 		if ((mop = allocb(sizeof (struct stroptions),
3347c478bd9Sstevel@tonic-gate 		    BPRI_MED)) != NULL) {
3357c478bd9Sstevel@tonic-gate 			struct stroptions *sop;
3367c478bd9Sstevel@tonic-gate 			uint_t chunk;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 			chunk = *(uint_t *)mp->b_cont->b_rptr;
3397c478bd9Sstevel@tonic-gate 			mop->b_datap->db_type = M_SETOPTS;
3407c478bd9Sstevel@tonic-gate 			mop->b_wptr += sizeof (struct stroptions);
3417c478bd9Sstevel@tonic-gate 			sop = (struct stroptions *)mop->b_rptr;
3427c478bd9Sstevel@tonic-gate 			sop->so_flags = SO_HIWAT | SO_LOWAT;
3437c478bd9Sstevel@tonic-gate 			sop->so_hiwat = SNIT_HIWAT(chunk, 1);
3447c478bd9Sstevel@tonic-gate 			sop->so_lowat = SNIT_LOWAT(chunk, 1);
3457c478bd9Sstevel@tonic-gate 			qreply(wq, mop);
3467c478bd9Sstevel@tonic-gate 		}
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 		sbp->sb_chunk = *(uint_t *)mp->b_cont->b_rptr;
3497c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
3507c478bd9Sstevel@tonic-gate 		sbclosechunk(sbp);
3517c478bd9Sstevel@tonic-gate 		return;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	case SBIOCSFLAGS:
3547c478bd9Sstevel@tonic-gate 		sbp->sb_flags = *(uint_t *)mp->b_cont->b_rptr;
3557c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
3567c478bd9Sstevel@tonic-gate 		return;
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	case SBIOCSSNAP:
3597c478bd9Sstevel@tonic-gate 		/*
3607c478bd9Sstevel@tonic-gate 		 * if chunking dont worry about effects of
3617c478bd9Sstevel@tonic-gate 		 * snipping of message size on head flow control
3627c478bd9Sstevel@tonic-gate 		 * since it has a relatively small bearing on the
3637c478bd9Sstevel@tonic-gate 		 * data rate onto the streamn head.
3647c478bd9Sstevel@tonic-gate 		 */
3657c478bd9Sstevel@tonic-gate 		if (!sbp->sb_chunk) {
3667c478bd9Sstevel@tonic-gate 			/*
3677c478bd9Sstevel@tonic-gate 			 * set up hi/lo water marks on stream head read queue.
3687c478bd9Sstevel@tonic-gate 			 * unlikely to run out of resources. Fix at later date.
3697c478bd9Sstevel@tonic-gate 			 */
3707c478bd9Sstevel@tonic-gate 			if ((mop = allocb(sizeof (struct stroptions),
3717c478bd9Sstevel@tonic-gate 			    BPRI_MED)) != NULL) {
3727c478bd9Sstevel@tonic-gate 				struct stroptions *sop;
3737c478bd9Sstevel@tonic-gate 				uint_t snap;
3747c478bd9Sstevel@tonic-gate 				int fudge;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 				snap = *(uint_t *)mp->b_cont->b_rptr;
3777c478bd9Sstevel@tonic-gate 				mop->b_datap->db_type = M_SETOPTS;
3787c478bd9Sstevel@tonic-gate 				mop->b_wptr += sizeof (struct stroptions);
3797c478bd9Sstevel@tonic-gate 				sop = (struct stroptions *)mop->b_rptr;
3807c478bd9Sstevel@tonic-gate 				sop->so_flags = SO_HIWAT | SO_LOWAT;
3817c478bd9Sstevel@tonic-gate 				fudge = snap <= 100 ?   4 :
3827c478bd9Sstevel@tonic-gate 				    snap <= 400 ?   2 :
3837c478bd9Sstevel@tonic-gate 				    1;
3847c478bd9Sstevel@tonic-gate 				sop->so_hiwat = SNIT_HIWAT(snap, fudge);
3857c478bd9Sstevel@tonic-gate 				sop->so_lowat = SNIT_LOWAT(snap, fudge);
3867c478bd9Sstevel@tonic-gate 				qreply(wq, mop);
3877c478bd9Sstevel@tonic-gate 			}
3887c478bd9Sstevel@tonic-gate 		}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 		sbp->sb_snap = *(uint_t *)mp->b_cont->b_rptr;
3917c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
3927c478bd9Sstevel@tonic-gate 		return;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	default:
3957c478bd9Sstevel@tonic-gate 		ASSERT(0);
3967c478bd9Sstevel@tonic-gate 		return;
3977c478bd9Sstevel@tonic-gate 	}
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate /*
4017c478bd9Sstevel@tonic-gate  * Write-side put procedure.  Its main task is to detect ioctls
4027c478bd9Sstevel@tonic-gate  * for manipulating the buffering state and hand them to sbioctl.
4037c478bd9Sstevel@tonic-gate  * Other message types are passed on through.
4047c478bd9Sstevel@tonic-gate  */
4057c478bd9Sstevel@tonic-gate static void
4067c478bd9Sstevel@tonic-gate sbwput(queue_t *wq, mblk_t *mp)
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	struct	sb	*sbp = (struct sb *)wq->q_ptr;
4097c478bd9Sstevel@tonic-gate 	struct copyresp *resp;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	if (sbp->sb_flags & SB_SEND_ON_WRITE)
4127c478bd9Sstevel@tonic-gate 		sbclosechunk(sbp);
4137c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
4147c478bd9Sstevel@tonic-gate 	case M_IOCTL:
4157c478bd9Sstevel@tonic-gate 		sbioctl(wq, mp);
4167c478bd9Sstevel@tonic-gate 		break;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	case M_IOCDATA:
4197c478bd9Sstevel@tonic-gate 		resp = (struct copyresp *)mp->b_rptr;
4207c478bd9Sstevel@tonic-gate 		if (resp->cp_rval) {
4217c478bd9Sstevel@tonic-gate 			/*
4227c478bd9Sstevel@tonic-gate 			 * Just free message on failure.
4237c478bd9Sstevel@tonic-gate 			 */
4247c478bd9Sstevel@tonic-gate 			freemsg(mp);
4257c478bd9Sstevel@tonic-gate 			break;
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		switch (resp->cp_cmd) {
4297c478bd9Sstevel@tonic-gate 		case SBIOCSTIME:
4307c478bd9Sstevel@tonic-gate 		case SBIOCSCHUNK:
4317c478bd9Sstevel@tonic-gate 		case SBIOCSFLAGS:
4327c478bd9Sstevel@tonic-gate 		case SBIOCSSNAP:
4337c478bd9Sstevel@tonic-gate 		case SBIOCGTIME:
4347c478bd9Sstevel@tonic-gate 		case SBIOCGCHUNK:
4357c478bd9Sstevel@tonic-gate 		case SBIOCGSNAP:
4367c478bd9Sstevel@tonic-gate 		case SBIOCGFLAGS:
4377c478bd9Sstevel@tonic-gate 			sbioc(wq, mp);
4387c478bd9Sstevel@tonic-gate 			break;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 		default:
4417c478bd9Sstevel@tonic-gate 			putnext(wq, mp);
4427c478bd9Sstevel@tonic-gate 			break;
4437c478bd9Sstevel@tonic-gate 		}
4447c478bd9Sstevel@tonic-gate 		break;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	default:
4477c478bd9Sstevel@tonic-gate 		putnext(wq, mp);
4487c478bd9Sstevel@tonic-gate 		break;
4497c478bd9Sstevel@tonic-gate 	}
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate /*
4537c478bd9Sstevel@tonic-gate  * Read-side put procedure.  It's responsible for buffering up incoming
4547c478bd9Sstevel@tonic-gate  * messages and grouping them into aggregates according to the current
4557c478bd9Sstevel@tonic-gate  * buffering parameters.
4567c478bd9Sstevel@tonic-gate  */
4577c478bd9Sstevel@tonic-gate static void
4587c478bd9Sstevel@tonic-gate sbrput(queue_t *rq, mblk_t *mp)
4597c478bd9Sstevel@tonic-gate {
4607c478bd9Sstevel@tonic-gate 	struct	sb	*sbp = (struct sb *)rq->q_ptr;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	ASSERT(sbp);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
4657c478bd9Sstevel@tonic-gate 	case M_PROTO:
4667c478bd9Sstevel@tonic-gate 		if (sbp->sb_flags & SB_NO_PROTO_CVT) {
4677c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
4687c478bd9Sstevel@tonic-gate 			sbsendit(rq, mp);
4697c478bd9Sstevel@tonic-gate 			break;
4707c478bd9Sstevel@tonic-gate 		} else {
4717c478bd9Sstevel@tonic-gate 			/*
4727c478bd9Sstevel@tonic-gate 			 * Convert M_PROTO to M_DATA.
4737c478bd9Sstevel@tonic-gate 			 */
4747c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_DATA;
4757c478bd9Sstevel@tonic-gate 		}
4767c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	case M_DATA:
4797c478bd9Sstevel@tonic-gate 		if ((sbp->sb_flags & SB_DEFER_CHUNK) &&
4807c478bd9Sstevel@tonic-gate 		    !(sbp->sb_state & SB_FRCVD)) {
4817c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
4827c478bd9Sstevel@tonic-gate 			sbsendit(rq, mp);
4837c478bd9Sstevel@tonic-gate 			sbp->sb_state |= SB_FRCVD;
4847c478bd9Sstevel@tonic-gate 		} else
4857c478bd9Sstevel@tonic-gate 			sbaddmsg(rq, mp);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		if ((sbp->sb_ticks > 0) && !(sbp->sb_timeoutid))
4887c478bd9Sstevel@tonic-gate 			sbp->sb_timeoutid = qtimeout(sbp->sb_rq, sbtick,
4897c478bd9Sstevel@tonic-gate 			    sbp, sbp->sb_ticks);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 		break;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	case M_FLUSH:
4947c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
4957c478bd9Sstevel@tonic-gate 			/*
4967c478bd9Sstevel@tonic-gate 			 * Reset timeout, flush the chunk currently in
4977c478bd9Sstevel@tonic-gate 			 * progress, and start a new chunk.
4987c478bd9Sstevel@tonic-gate 			 */
4997c478bd9Sstevel@tonic-gate 			if (sbp->sb_timeoutid) {
5007c478bd9Sstevel@tonic-gate 				(void) quntimeout(sbp->sb_rq,
5017c478bd9Sstevel@tonic-gate 				    sbp->sb_timeoutid);
5027c478bd9Sstevel@tonic-gate 				sbp->sb_timeoutid = 0;
5037c478bd9Sstevel@tonic-gate 			}
5047c478bd9Sstevel@tonic-gate 			if (sbp->sb_mp) {
5057c478bd9Sstevel@tonic-gate 				freemsg(sbp->sb_mp);
5067c478bd9Sstevel@tonic-gate 				sbp->sb_tail = sbp->sb_mp = sbp->sb_head = NULL;
5077c478bd9Sstevel@tonic-gate 				sbp->sb_mlen = 0;
5087c478bd9Sstevel@tonic-gate 				sbp->sb_mcount = 0;
5097c478bd9Sstevel@tonic-gate 			}
5107c478bd9Sstevel@tonic-gate 			flushq(rq, FLUSHALL);
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 		putnext(rq, mp);
5137c478bd9Sstevel@tonic-gate 		break;
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	case M_CTL:
5167c478bd9Sstevel@tonic-gate 		/*
5177c478bd9Sstevel@tonic-gate 		 * Zero-length M_CTL means our timeout() popped.
5187c478bd9Sstevel@tonic-gate 		 */
5197c478bd9Sstevel@tonic-gate 		if (MBLKL(mp) == 0) {
5207c478bd9Sstevel@tonic-gate 			freemsg(mp);
5217c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
5227c478bd9Sstevel@tonic-gate 		} else {
5237c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
5247c478bd9Sstevel@tonic-gate 			sbsendit(rq, mp);
5257c478bd9Sstevel@tonic-gate 		}
5267c478bd9Sstevel@tonic-gate 		break;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	default:
5297c478bd9Sstevel@tonic-gate 		if (mp->b_datap->db_type <= QPCTL) {
5307c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
5317c478bd9Sstevel@tonic-gate 			sbsendit(rq, mp);
5327c478bd9Sstevel@tonic-gate 		} else {
5337c478bd9Sstevel@tonic-gate 			/* Note: out of band */
5347c478bd9Sstevel@tonic-gate 			putnext(rq, mp);
5357c478bd9Sstevel@tonic-gate 		}
5367c478bd9Sstevel@tonic-gate 		break;
5377c478bd9Sstevel@tonic-gate 	}
5387c478bd9Sstevel@tonic-gate }
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate /*
5417c478bd9Sstevel@tonic-gate  *  read service procedure.
5427c478bd9Sstevel@tonic-gate  */
5437c478bd9Sstevel@tonic-gate /* ARGSUSED */
5447c478bd9Sstevel@tonic-gate static void
5457c478bd9Sstevel@tonic-gate sbrsrv(queue_t *rq)
5467c478bd9Sstevel@tonic-gate {
5477c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	/*
5507c478bd9Sstevel@tonic-gate 	 * High priority messages shouldn't get here but if
5517c478bd9Sstevel@tonic-gate 	 * one does, jam it through to avoid infinite loop.
5527c478bd9Sstevel@tonic-gate 	 */
5537c478bd9Sstevel@tonic-gate 	while ((mp = getq(rq)) != NULL) {
5547c478bd9Sstevel@tonic-gate 		if (!canputnext(rq) && (mp->b_datap->db_type <= QPCTL)) {
5557c478bd9Sstevel@tonic-gate 			/* should only get here if SB_NO_SROPS */
5567c478bd9Sstevel@tonic-gate 			(void) putbq(rq, mp);
5577c478bd9Sstevel@tonic-gate 			return;
5587c478bd9Sstevel@tonic-gate 		}
5597c478bd9Sstevel@tonic-gate 		putnext(rq, mp);
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate /*
5647c478bd9Sstevel@tonic-gate  * Handle write-side M_IOCTL messages.
5657c478bd9Sstevel@tonic-gate  */
5667c478bd9Sstevel@tonic-gate static void
5677c478bd9Sstevel@tonic-gate sbioctl(queue_t *wq, mblk_t *mp)
5687c478bd9Sstevel@tonic-gate {
5697c478bd9Sstevel@tonic-gate 	struct	sb	*sbp = (struct sb *)wq->q_ptr;
5707c478bd9Sstevel@tonic-gate 	struct iocblk	*iocp = (struct iocblk *)mp->b_rptr;
5717c478bd9Sstevel@tonic-gate 	struct	timeval	*t;
5727c478bd9Sstevel@tonic-gate 	clock_t	ticks;
5737c478bd9Sstevel@tonic-gate 	mblk_t	*mop;
5747c478bd9Sstevel@tonic-gate 	int	transparent = iocp->ioc_count;
5757c478bd9Sstevel@tonic-gate 	mblk_t	*datamp;
5767c478bd9Sstevel@tonic-gate 	int	error;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
5797c478bd9Sstevel@tonic-gate 	case SBIOCSTIME:
5807c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
5817c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
5827c478bd9Sstevel@tonic-gate 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
5837c478bd9Sstevel@tonic-gate 				mcopyin(mp, NULL, sizeof (struct timeval32),
5847c478bd9Sstevel@tonic-gate 				    NULL);
5857c478bd9Sstevel@tonic-gate 			} else
5867c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */
5877c478bd9Sstevel@tonic-gate 			{
5887c478bd9Sstevel@tonic-gate 				mcopyin(mp, NULL, sizeof (*t), NULL);
5897c478bd9Sstevel@tonic-gate 			}
5907c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
5917c478bd9Sstevel@tonic-gate 		} else {
5927c478bd9Sstevel@tonic-gate 			/*
5937c478bd9Sstevel@tonic-gate 			 * Verify argument length.
5947c478bd9Sstevel@tonic-gate 			 */
5957c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
5967c478bd9Sstevel@tonic-gate 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
5977c478bd9Sstevel@tonic-gate 				struct timeval32 *t32;
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 				error = miocpullup(mp,
6007c478bd9Sstevel@tonic-gate 				    sizeof (struct timeval32));
6017c478bd9Sstevel@tonic-gate 				if (error != 0) {
6027c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, error);
6037c478bd9Sstevel@tonic-gate 					break;
6047c478bd9Sstevel@tonic-gate 				}
6057c478bd9Sstevel@tonic-gate 				t32 = (struct timeval32 *)mp->b_cont->b_rptr;
6067c478bd9Sstevel@tonic-gate 				if (t32->tv_sec < 0 || t32->tv_usec < 0) {
6077c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, EINVAL);
6087c478bd9Sstevel@tonic-gate 					break;
6097c478bd9Sstevel@tonic-gate 				}
6107c478bd9Sstevel@tonic-gate 				ticks = TIMEVAL_TO_TICK(t32);
6117c478bd9Sstevel@tonic-gate 			} else
6127c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */
6137c478bd9Sstevel@tonic-gate 			{
6147c478bd9Sstevel@tonic-gate 				error = miocpullup(mp, sizeof (struct timeval));
6157c478bd9Sstevel@tonic-gate 				if (error != 0) {
6167c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, error);
6177c478bd9Sstevel@tonic-gate 					break;
6187c478bd9Sstevel@tonic-gate 				}
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 				t = (struct timeval *)mp->b_cont->b_rptr;
6217c478bd9Sstevel@tonic-gate 				if (t->tv_sec < 0 || t->tv_usec < 0) {
6227c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, EINVAL);
6237c478bd9Sstevel@tonic-gate 					break;
6247c478bd9Sstevel@tonic-gate 				}
6257c478bd9Sstevel@tonic-gate 				ticks = TIMEVAL_TO_TICK(t);
6267c478bd9Sstevel@tonic-gate 			}
6277c478bd9Sstevel@tonic-gate 			sbp->sb_ticks = ticks;
6287c478bd9Sstevel@tonic-gate 			if (ticks == 0)
6297c478bd9Sstevel@tonic-gate 				sbp->sb_chunk = 0;
6307c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
6317c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
6327c478bd9Sstevel@tonic-gate 		}
6337c478bd9Sstevel@tonic-gate 		break;
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 	case SBIOCGTIME: {
6367c478bd9Sstevel@tonic-gate 		struct timeval *t;
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 		/*
6397c478bd9Sstevel@tonic-gate 		 * Verify argument length.
6407c478bd9Sstevel@tonic-gate 		 */
6417c478bd9Sstevel@tonic-gate 		if (transparent != TRANSPARENT) {
6427c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
6437c478bd9Sstevel@tonic-gate 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
6447c478bd9Sstevel@tonic-gate 				error = miocpullup(mp,
6457c478bd9Sstevel@tonic-gate 				    sizeof (struct timeval32));
6467c478bd9Sstevel@tonic-gate 				if (error != 0) {
6477c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, error);
6487c478bd9Sstevel@tonic-gate 					break;
6497c478bd9Sstevel@tonic-gate 				}
6507c478bd9Sstevel@tonic-gate 			} else
6517c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */
6527c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct timeval));
6537c478bd9Sstevel@tonic-gate 			if (error != 0) {
6547c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
6557c478bd9Sstevel@tonic-gate 				break;
6567c478bd9Sstevel@tonic-gate 			}
6577c478bd9Sstevel@tonic-gate 		}
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 		/*
6607c478bd9Sstevel@tonic-gate 		 * If infinite timeout, return range error
6617c478bd9Sstevel@tonic-gate 		 * for the ioctl.
6627c478bd9Sstevel@tonic-gate 		 */
6637c478bd9Sstevel@tonic-gate 		if (sbp->sb_ticks < 0) {
6647c478bd9Sstevel@tonic-gate 			miocnak(wq, mp, 0, ERANGE);
6657c478bd9Sstevel@tonic-gate 			break;
6667c478bd9Sstevel@tonic-gate 		}
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
6697c478bd9Sstevel@tonic-gate 		if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
6707c478bd9Sstevel@tonic-gate 			struct timeval32 *t32;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 			if (transparent == TRANSPARENT) {
6737c478bd9Sstevel@tonic-gate 				datamp = allocb(sizeof (*t32), BPRI_MED);
6747c478bd9Sstevel@tonic-gate 				if (datamp == NULL) {
6757c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, EAGAIN);
6767c478bd9Sstevel@tonic-gate 					break;
6777c478bd9Sstevel@tonic-gate 				}
6787c478bd9Sstevel@tonic-gate 				mcopyout(mp, NULL, sizeof (*t32), NULL, datamp);
6797c478bd9Sstevel@tonic-gate 			}
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 			t32 = (struct timeval32 *)mp->b_cont->b_rptr;
6827c478bd9Sstevel@tonic-gate 			TICK_TO_TIMEVAL32(sbp->sb_ticks, t32);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 			if (transparent == TRANSPARENT)
6857c478bd9Sstevel@tonic-gate 				qreply(wq, mp);
6867c478bd9Sstevel@tonic-gate 			else
6877c478bd9Sstevel@tonic-gate 				miocack(wq, mp, sizeof (*t32), 0);
6887c478bd9Sstevel@tonic-gate 		} else
6897c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */
6907c478bd9Sstevel@tonic-gate 		{
6917c478bd9Sstevel@tonic-gate 			if (transparent == TRANSPARENT) {
6927c478bd9Sstevel@tonic-gate 				datamp = allocb(sizeof (*t), BPRI_MED);
6937c478bd9Sstevel@tonic-gate 				if (datamp == NULL) {
6947c478bd9Sstevel@tonic-gate 					miocnak(wq, mp, 0, EAGAIN);
6957c478bd9Sstevel@tonic-gate 					break;
6967c478bd9Sstevel@tonic-gate 				}
6977c478bd9Sstevel@tonic-gate 				mcopyout(mp, NULL, sizeof (*t), NULL, datamp);
6987c478bd9Sstevel@tonic-gate 			}
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 			t = (struct timeval *)mp->b_cont->b_rptr;
7017c478bd9Sstevel@tonic-gate 			TICK_TO_TIMEVAL(sbp->sb_ticks, t);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 			if (transparent == TRANSPARENT)
7047c478bd9Sstevel@tonic-gate 				qreply(wq, mp);
7057c478bd9Sstevel@tonic-gate 			else
7067c478bd9Sstevel@tonic-gate 				miocack(wq, mp, sizeof (*t), 0);
7077c478bd9Sstevel@tonic-gate 		}
7087c478bd9Sstevel@tonic-gate 		break;
7097c478bd9Sstevel@tonic-gate 	}
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	case SBIOCCTIME:
7127c478bd9Sstevel@tonic-gate 		sbp->sb_ticks = -1;
7137c478bd9Sstevel@tonic-gate 		miocack(wq, mp, 0, 0);
7147c478bd9Sstevel@tonic-gate 		break;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	case SBIOCSCHUNK:
7177c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
7187c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (uint_t), NULL);
7197c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
7207c478bd9Sstevel@tonic-gate 		} else {
7217c478bd9Sstevel@tonic-gate 			/*
7227c478bd9Sstevel@tonic-gate 			 * Verify argument length.
7237c478bd9Sstevel@tonic-gate 			 */
7247c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
7257c478bd9Sstevel@tonic-gate 			if (error != 0) {
7267c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
7277c478bd9Sstevel@tonic-gate 				break;
7287c478bd9Sstevel@tonic-gate 			}
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 			/*
7317c478bd9Sstevel@tonic-gate 			 * set up hi/lo water marks on stream head read queue.
7327c478bd9Sstevel@tonic-gate 			 * unlikely to run out of resources. Fix at later date.
7337c478bd9Sstevel@tonic-gate 			 */
7347c478bd9Sstevel@tonic-gate 			if ((mop = allocb(sizeof (struct stroptions),
7357c478bd9Sstevel@tonic-gate 			    BPRI_MED)) != NULL) {
7367c478bd9Sstevel@tonic-gate 				struct stroptions *sop;
7377c478bd9Sstevel@tonic-gate 				uint_t chunk;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 				chunk = *(uint_t *)mp->b_cont->b_rptr;
7407c478bd9Sstevel@tonic-gate 				mop->b_datap->db_type = M_SETOPTS;
7417c478bd9Sstevel@tonic-gate 				mop->b_wptr += sizeof (struct stroptions);
7427c478bd9Sstevel@tonic-gate 				sop = (struct stroptions *)mop->b_rptr;
7437c478bd9Sstevel@tonic-gate 				sop->so_flags = SO_HIWAT | SO_LOWAT;
7447c478bd9Sstevel@tonic-gate 				sop->so_hiwat = SNIT_HIWAT(chunk, 1);
7457c478bd9Sstevel@tonic-gate 				sop->so_lowat = SNIT_LOWAT(chunk, 1);
7467c478bd9Sstevel@tonic-gate 				qreply(wq, mop);
7477c478bd9Sstevel@tonic-gate 			}
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 			sbp->sb_chunk = *(uint_t *)mp->b_cont->b_rptr;
7507c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
7517c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
7527c478bd9Sstevel@tonic-gate 		}
7537c478bd9Sstevel@tonic-gate 		break;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	case SBIOCGCHUNK:
7567c478bd9Sstevel@tonic-gate 		/*
7577c478bd9Sstevel@tonic-gate 		 * Verify argument length.
7587c478bd9Sstevel@tonic-gate 		 */
7597c478bd9Sstevel@tonic-gate 		if (transparent != TRANSPARENT) {
7607c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
7617c478bd9Sstevel@tonic-gate 			if (error != 0) {
7627c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
7637c478bd9Sstevel@tonic-gate 				break;
7647c478bd9Sstevel@tonic-gate 			}
7657c478bd9Sstevel@tonic-gate 		}
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT) {
7687c478bd9Sstevel@tonic-gate 			datamp = allocb(sizeof (uint_t), BPRI_MED);
7697c478bd9Sstevel@tonic-gate 			if (datamp == NULL) {
7707c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, EAGAIN);
7717c478bd9Sstevel@tonic-gate 				break;
7727c478bd9Sstevel@tonic-gate 			}
7737c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (uint_t), NULL, datamp);
7747c478bd9Sstevel@tonic-gate 		}
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 		*(uint_t *)mp->b_cont->b_rptr = sbp->sb_chunk;
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT)
7797c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
7807c478bd9Sstevel@tonic-gate 		else
7817c478bd9Sstevel@tonic-gate 			miocack(wq, mp, sizeof (uint_t), 0);
7827c478bd9Sstevel@tonic-gate 		break;
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	case SBIOCSSNAP:
7857c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
7867c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (uint_t), NULL);
7877c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
7887c478bd9Sstevel@tonic-gate 		} else {
7897c478bd9Sstevel@tonic-gate 			/*
7907c478bd9Sstevel@tonic-gate 			 * Verify argument length.
7917c478bd9Sstevel@tonic-gate 			 */
7927c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
7937c478bd9Sstevel@tonic-gate 			if (error != 0) {
7947c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
7957c478bd9Sstevel@tonic-gate 				break;
7967c478bd9Sstevel@tonic-gate 			}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 			/*
7997c478bd9Sstevel@tonic-gate 			 * if chunking dont worry about effects of
8007c478bd9Sstevel@tonic-gate 			 * snipping of message size on head flow control
8017c478bd9Sstevel@tonic-gate 			 * since it has a relatively small bearing on the
8027c478bd9Sstevel@tonic-gate 			 * data rate onto the streamn head.
8037c478bd9Sstevel@tonic-gate 			 */
8047c478bd9Sstevel@tonic-gate 			if (!sbp->sb_chunk) {
8057c478bd9Sstevel@tonic-gate 				/*
8067c478bd9Sstevel@tonic-gate 				 * set up hi/lo water marks on stream
8077c478bd9Sstevel@tonic-gate 				 * head read queue.  unlikely to run out
8087c478bd9Sstevel@tonic-gate 				 * of resources. Fix at later date.
8097c478bd9Sstevel@tonic-gate 				 */
8107c478bd9Sstevel@tonic-gate 				if ((mop = allocb(sizeof (struct stroptions),
8117c478bd9Sstevel@tonic-gate 				    BPRI_MED)) != NULL) {
8127c478bd9Sstevel@tonic-gate 					struct stroptions *sop;
8137c478bd9Sstevel@tonic-gate 					uint_t snap;
8147c478bd9Sstevel@tonic-gate 					int fudge;
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 					snap = *(uint_t *)mp->b_cont->b_rptr;
8177c478bd9Sstevel@tonic-gate 					mop->b_datap->db_type = M_SETOPTS;
8187c478bd9Sstevel@tonic-gate 					mop->b_wptr += sizeof (*sop);
8197c478bd9Sstevel@tonic-gate 					sop = (struct stroptions *)mop->b_rptr;
8207c478bd9Sstevel@tonic-gate 					sop->so_flags = SO_HIWAT | SO_LOWAT;
8217c478bd9Sstevel@tonic-gate 					fudge = (snap <= 100) ? 4 :
8227c478bd9Sstevel@tonic-gate 					    (snap <= 400) ? 2 : 1;
8237c478bd9Sstevel@tonic-gate 					sop->so_hiwat = SNIT_HIWAT(snap, fudge);
8247c478bd9Sstevel@tonic-gate 					sop->so_lowat = SNIT_LOWAT(snap, fudge);
8257c478bd9Sstevel@tonic-gate 					qreply(wq, mop);
8267c478bd9Sstevel@tonic-gate 				}
8277c478bd9Sstevel@tonic-gate 			}
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 			sbp->sb_snap = *(uint_t *)mp->b_cont->b_rptr;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
8327c478bd9Sstevel@tonic-gate 		}
8337c478bd9Sstevel@tonic-gate 		break;
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	case SBIOCGSNAP:
8367c478bd9Sstevel@tonic-gate 		/*
8377c478bd9Sstevel@tonic-gate 		 * Verify argument length
8387c478bd9Sstevel@tonic-gate 		 */
8397c478bd9Sstevel@tonic-gate 		if (transparent != TRANSPARENT) {
8407c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
8417c478bd9Sstevel@tonic-gate 			if (error != 0) {
8427c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
8437c478bd9Sstevel@tonic-gate 				break;
8447c478bd9Sstevel@tonic-gate 			}
8457c478bd9Sstevel@tonic-gate 		}
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT) {
8487c478bd9Sstevel@tonic-gate 			datamp = allocb(sizeof (uint_t), BPRI_MED);
8497c478bd9Sstevel@tonic-gate 			if (datamp == NULL) {
8507c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, EAGAIN);
8517c478bd9Sstevel@tonic-gate 				break;
8527c478bd9Sstevel@tonic-gate 			}
8537c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (uint_t), NULL, datamp);
8547c478bd9Sstevel@tonic-gate 		}
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 		*(uint_t *)mp->b_cont->b_rptr = sbp->sb_snap;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT)
8597c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
8607c478bd9Sstevel@tonic-gate 		else
8617c478bd9Sstevel@tonic-gate 			miocack(wq, mp, sizeof (uint_t), 0);
8627c478bd9Sstevel@tonic-gate 		break;
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	case SBIOCSFLAGS:
8657c478bd9Sstevel@tonic-gate 		/*
8667c478bd9Sstevel@tonic-gate 		 * set the flags.
8677c478bd9Sstevel@tonic-gate 		 */
8687c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
8697c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (uint_t), NULL);
8707c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
8717c478bd9Sstevel@tonic-gate 		} else {
8727c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
8737c478bd9Sstevel@tonic-gate 			if (error != 0) {
8747c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
8757c478bd9Sstevel@tonic-gate 				break;
8767c478bd9Sstevel@tonic-gate 			}
8777c478bd9Sstevel@tonic-gate 			sbp->sb_flags = *(uint_t *)mp->b_cont->b_rptr;
8787c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
8797c478bd9Sstevel@tonic-gate 		}
8807c478bd9Sstevel@tonic-gate 		break;
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	case SBIOCGFLAGS:
8837c478bd9Sstevel@tonic-gate 		/*
8847c478bd9Sstevel@tonic-gate 		 * Verify argument length
8857c478bd9Sstevel@tonic-gate 		 */
8867c478bd9Sstevel@tonic-gate 		if (transparent != TRANSPARENT) {
8877c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uint_t));
8887c478bd9Sstevel@tonic-gate 			if (error != 0) {
8897c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, error);
8907c478bd9Sstevel@tonic-gate 				break;
8917c478bd9Sstevel@tonic-gate 			}
8927c478bd9Sstevel@tonic-gate 		}
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT) {
8957c478bd9Sstevel@tonic-gate 			datamp = allocb(sizeof (uint_t), BPRI_MED);
8967c478bd9Sstevel@tonic-gate 			if (datamp == NULL) {
8977c478bd9Sstevel@tonic-gate 				miocnak(wq, mp, 0, EAGAIN);
8987c478bd9Sstevel@tonic-gate 				break;
8997c478bd9Sstevel@tonic-gate 			}
9007c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (uint_t), NULL, datamp);
9017c478bd9Sstevel@tonic-gate 		}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 		*(uint_t *)mp->b_cont->b_rptr = sbp->sb_flags;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 		if (transparent == TRANSPARENT)
9067c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
9077c478bd9Sstevel@tonic-gate 		else
9087c478bd9Sstevel@tonic-gate 			miocack(wq, mp, sizeof (uint_t), 0);
9097c478bd9Sstevel@tonic-gate 		break;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	default:
9137c478bd9Sstevel@tonic-gate 		putnext(wq, mp);
9147c478bd9Sstevel@tonic-gate 		break;
9157c478bd9Sstevel@tonic-gate 	}
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate /*
9197c478bd9Sstevel@tonic-gate  * Given a length l, calculate the amount of extra storage
9207c478bd9Sstevel@tonic-gate  * required to round it up to the next multiple of the alignment a.
9217c478bd9Sstevel@tonic-gate  */
9227c478bd9Sstevel@tonic-gate #define	RoundUpAmt(l, a)	((l) % (a) ? (a) - ((l) % (a)) : 0)
9237c478bd9Sstevel@tonic-gate /*
9247c478bd9Sstevel@tonic-gate  * Calculate additional amount of space required for alignment.
9257c478bd9Sstevel@tonic-gate  */
9267c478bd9Sstevel@tonic-gate #define	Align(l)		RoundUpAmt(l, sizeof (ulong_t))
9277c478bd9Sstevel@tonic-gate /*
9287c478bd9Sstevel@tonic-gate  * Smallest possible message size when headers are enabled.
9297c478bd9Sstevel@tonic-gate  * This is used to calculate whether a chunk is nearly full.
9307c478bd9Sstevel@tonic-gate  */
9317c478bd9Sstevel@tonic-gate #define	SMALLEST_MESSAGE	sizeof (struct sb_hdr) + _POINTER_ALIGNMENT
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate /*
9347c478bd9Sstevel@tonic-gate  * Process a read-side M_DATA message.
9357c478bd9Sstevel@tonic-gate  *
9367c478bd9Sstevel@tonic-gate  * If the currently accumulating chunk doesn't have enough room
9377c478bd9Sstevel@tonic-gate  * for the message, close off the chunk, pass it upward, and start
9387c478bd9Sstevel@tonic-gate  * a new one.  Then add the message to the current chunk, taking
9397c478bd9Sstevel@tonic-gate  * account of the possibility that the message's size exceeds the
9407c478bd9Sstevel@tonic-gate  * chunk size.
9417c478bd9Sstevel@tonic-gate  *
9427c478bd9Sstevel@tonic-gate  * If headers are enabled add an sb_hdr header and trailing alignment padding.
9437c478bd9Sstevel@tonic-gate  *
9447c478bd9Sstevel@tonic-gate  * To optimise performance the total number of msgbs should be kept
9457c478bd9Sstevel@tonic-gate  * to a minimum. This is achieved by using any remaining space in message N
9467c478bd9Sstevel@tonic-gate  * for both its own padding as well as the header of message N+1 if possible.
9477c478bd9Sstevel@tonic-gate  * If there's insufficient space we allocate one message to hold this 'wrapper'.
9487c478bd9Sstevel@tonic-gate  * (there's likely to be space beyond message N, since allocb would have
9497c478bd9Sstevel@tonic-gate  * rounded up the required size to one of the dblk_sizes).
9507c478bd9Sstevel@tonic-gate  *
9517c478bd9Sstevel@tonic-gate  */
9527c478bd9Sstevel@tonic-gate static void
9537c478bd9Sstevel@tonic-gate sbaddmsg(queue_t *rq, mblk_t *mp)
9547c478bd9Sstevel@tonic-gate {
9557c478bd9Sstevel@tonic-gate 	struct sb	*sbp;
9567c478bd9Sstevel@tonic-gate 	struct timeval	t;
9577c478bd9Sstevel@tonic-gate 	struct sb_hdr	hp;
9587c478bd9Sstevel@tonic-gate 	mblk_t *wrapper;	/* padding for msg N, header for msg N+1 */
9597c478bd9Sstevel@tonic-gate 	mblk_t *last;		/* last mblk of current message */
9607c478bd9Sstevel@tonic-gate 	size_t wrapperlen;	/* length of header + padding */
9617c478bd9Sstevel@tonic-gate 	size_t origlen;		/* data length before truncation */
9627c478bd9Sstevel@tonic-gate 	size_t pad;		/* bytes required to align header */
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	sbp = (struct sb *)rq->q_ptr;
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 	origlen = msgdsize(mp);
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	/*
9697c478bd9Sstevel@tonic-gate 	 * Truncate the message.
9707c478bd9Sstevel@tonic-gate 	 */
9717c478bd9Sstevel@tonic-gate 	if ((sbp->sb_snap > 0) && (origlen > sbp->sb_snap) &&
9727c478bd9Sstevel@tonic-gate 			(adjmsg(mp, -(origlen - sbp->sb_snap)) == 1))
9737c478bd9Sstevel@tonic-gate 		hp.sbh_totlen = hp.sbh_msglen = sbp->sb_snap;
9747c478bd9Sstevel@tonic-gate 	else
9757c478bd9Sstevel@tonic-gate 		hp.sbh_totlen = hp.sbh_msglen = origlen;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	if (sbp->sb_flags & SB_NO_HEADER) {
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 		/*
9807c478bd9Sstevel@tonic-gate 		 * Would the inclusion of this message overflow the current
9817c478bd9Sstevel@tonic-gate 		 * chunk? If so close the chunk off and start a new one.
9827c478bd9Sstevel@tonic-gate 		 */
9837c478bd9Sstevel@tonic-gate 		if ((hp.sbh_totlen + sbp->sb_mlen) > sbp->sb_chunk)
9847c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
9857c478bd9Sstevel@tonic-gate 		/*
9867c478bd9Sstevel@tonic-gate 		 * First message too big for chunk - just send it up.
9877c478bd9Sstevel@tonic-gate 		 * This will always be true when we're not chunking.
9887c478bd9Sstevel@tonic-gate 		 */
9897c478bd9Sstevel@tonic-gate 		if (hp.sbh_totlen > sbp->sb_chunk) {
9907c478bd9Sstevel@tonic-gate 			sbsendit(rq, mp);
9917c478bd9Sstevel@tonic-gate 			return;
9927c478bd9Sstevel@tonic-gate 		}
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 		/*
9957c478bd9Sstevel@tonic-gate 		 * We now know that the msg will fit in the chunk.
9967c478bd9Sstevel@tonic-gate 		 * Link it onto the end of the chunk.
9977c478bd9Sstevel@tonic-gate 		 * Since linkb() walks the entire chain, we keep a pointer to
9987c478bd9Sstevel@tonic-gate 		 * the first mblk of the last msgb added and call linkb on that
9997c478bd9Sstevel@tonic-gate 		 * that last message, rather than performing the
10007c478bd9Sstevel@tonic-gate 		 * O(n) linkb() operation on the whole chain.
10017c478bd9Sstevel@tonic-gate 		 * sb_head isn't needed in this SB_NO_HEADER mode.
10027c478bd9Sstevel@tonic-gate 		 */
10037c478bd9Sstevel@tonic-gate 		if (sbp->sb_mp)
10047c478bd9Sstevel@tonic-gate 			linkb(sbp->sb_tail, mp);
10057c478bd9Sstevel@tonic-gate 		else
10067c478bd9Sstevel@tonic-gate 			sbp->sb_mp = mp;
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 		sbp->sb_tail = mp;
10097c478bd9Sstevel@tonic-gate 		sbp->sb_mlen += hp.sbh_totlen;
10107c478bd9Sstevel@tonic-gate 		sbp->sb_mcount++;
10117c478bd9Sstevel@tonic-gate 	} else {
10127c478bd9Sstevel@tonic-gate 		/* Timestamp must be done immediately */
10137c478bd9Sstevel@tonic-gate 		uniqtime(&t);
10147c478bd9Sstevel@tonic-gate 		TIMEVAL_TO_TIMEVAL32(&hp.sbh_timestamp, &t);
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 		pad = Align(hp.sbh_totlen);
10177c478bd9Sstevel@tonic-gate 		hp.sbh_totlen += sizeof (hp);
10187c478bd9Sstevel@tonic-gate 
1019*eb00302cSRyan Zezeski 		/* We can't fit this message on the current chunk. */
1020*eb00302cSRyan Zezeski 		if ((sbp->sb_mlen + hp.sbh_totlen) > sbp->sb_chunk)
10217c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
10227c478bd9Sstevel@tonic-gate 
1023*eb00302cSRyan Zezeski 		/*
1024*eb00302cSRyan Zezeski 		 * If we closed it (just now or during a previous
1025*eb00302cSRyan Zezeski 		 * call) then allocate the head of a new chunk.
1026*eb00302cSRyan Zezeski 		 */
10277c478bd9Sstevel@tonic-gate 		if (sbp->sb_head == NULL) {
10287c478bd9Sstevel@tonic-gate 			/* Allocate leading header of new chunk */
10297c478bd9Sstevel@tonic-gate 			sbp->sb_head = allocb(sizeof (hp), BPRI_MED);
10307c478bd9Sstevel@tonic-gate 			if (sbp->sb_head == NULL) {
10317c478bd9Sstevel@tonic-gate 				/*
10327c478bd9Sstevel@tonic-gate 				 * Memory allocation failure.
10337c478bd9Sstevel@tonic-gate 				 * This will need to be revisited
10347c478bd9Sstevel@tonic-gate 				 * since using certain flag combinations
10357c478bd9Sstevel@tonic-gate 				 * can result in messages being dropped
10367c478bd9Sstevel@tonic-gate 				 * silently.
10377c478bd9Sstevel@tonic-gate 				 */
10387c478bd9Sstevel@tonic-gate 				freemsg(mp);
10397c478bd9Sstevel@tonic-gate 				sbp->sb_drops++;
10407c478bd9Sstevel@tonic-gate 				return;
10417c478bd9Sstevel@tonic-gate 			}
10427c478bd9Sstevel@tonic-gate 			sbp->sb_mp = sbp->sb_head;
10437c478bd9Sstevel@tonic-gate 		}
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 		/*
1046*eb00302cSRyan Zezeski 		 * Set the header values and join the message to the
1047*eb00302cSRyan Zezeski 		 * chunk. The header values are copied into the chunk
1048*eb00302cSRyan Zezeski 		 * after we adjust for padding below.
10497c478bd9Sstevel@tonic-gate 		 */
10507c478bd9Sstevel@tonic-gate 		hp.sbh_drops = sbp->sb_drops;
10517c478bd9Sstevel@tonic-gate 		hp.sbh_origlen = origlen;
10527c478bd9Sstevel@tonic-gate 		linkb(sbp->sb_head, mp);
10537c478bd9Sstevel@tonic-gate 		sbp->sb_mcount++;
10547c478bd9Sstevel@tonic-gate 		sbp->sb_mlen += hp.sbh_totlen;
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 		/*
1057*eb00302cSRyan Zezeski 		 * There's no chance to fit another message on the
1058*eb00302cSRyan Zezeski 		 * chunk -- forgo the padding and close the chunk.
10597c478bd9Sstevel@tonic-gate 		 */
1060*eb00302cSRyan Zezeski 		if ((sbp->sb_mlen + pad + SMALLEST_MESSAGE) > sbp->sb_chunk) {
1061*eb00302cSRyan Zezeski 			(void) memcpy(sbp->sb_head->b_wptr, (char *)&hp,
1062*eb00302cSRyan Zezeski 			    sizeof (hp));
1063*eb00302cSRyan Zezeski 			sbp->sb_head->b_wptr += sizeof (hp);
1064*eb00302cSRyan Zezeski 			ASSERT(sbp->sb_head->b_wptr <=
1065*eb00302cSRyan Zezeski 			    sbp->sb_head->b_datap->db_lim);
10667c478bd9Sstevel@tonic-gate 			sbclosechunk(sbp);
10677c478bd9Sstevel@tonic-gate 			return;
10687c478bd9Sstevel@tonic-gate 		}
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 		/*
1071*eb00302cSRyan Zezeski 		 * We may add another message to this chunk -- adjust
1072*eb00302cSRyan Zezeski 		 * the headers for padding to be added below.
1073*eb00302cSRyan Zezeski 		 */
1074*eb00302cSRyan Zezeski 		hp.sbh_totlen += pad;
1075*eb00302cSRyan Zezeski 		(void) memcpy(sbp->sb_head->b_wptr, (char *)&hp, sizeof (hp));
1076*eb00302cSRyan Zezeski 		sbp->sb_head->b_wptr += sizeof (hp);
1077*eb00302cSRyan Zezeski 		ASSERT(sbp->sb_head->b_wptr <= sbp->sb_head->b_datap->db_lim);
1078*eb00302cSRyan Zezeski 		sbp->sb_mlen += pad;
1079*eb00302cSRyan Zezeski 
1080*eb00302cSRyan Zezeski 		/*
10817c478bd9Sstevel@tonic-gate 		 * Find space for the wrapper. The wrapper consists of:
10827c478bd9Sstevel@tonic-gate 		 *
10837c478bd9Sstevel@tonic-gate 		 * 1) Padding for this message (this is to ensure each header
10847c478bd9Sstevel@tonic-gate 		 * begins on an 8 byte boundary in the userland buffer).
10857c478bd9Sstevel@tonic-gate 		 *
10867c478bd9Sstevel@tonic-gate 		 * 2) Space for the next message's header, in case the next
10877c478bd9Sstevel@tonic-gate 		 * next message will fit in this chunk.
10887c478bd9Sstevel@tonic-gate 		 *
10897c478bd9Sstevel@tonic-gate 		 * It may be possible to append the wrapper to the last mblk
10907c478bd9Sstevel@tonic-gate 		 * of the message, but only if we 'own' the data. If the dblk
10917c478bd9Sstevel@tonic-gate 		 * has been shared through dupmsg() we mustn't alter it.
10927c478bd9Sstevel@tonic-gate 		 */
10937c478bd9Sstevel@tonic-gate 		wrapperlen = (sizeof (hp) + pad);
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 		/* Is there space for the wrapper beyond the message's data ? */
10967c478bd9Sstevel@tonic-gate 		for (last = mp; last->b_cont; last = last->b_cont)
10977c478bd9Sstevel@tonic-gate 			;
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 		if ((wrapperlen <= MBLKTAIL(last)) &&
11007c478bd9Sstevel@tonic-gate 			(last->b_datap->db_ref == 1)) {
11017c478bd9Sstevel@tonic-gate 			if (pad > 0) {
11027c478bd9Sstevel@tonic-gate 				/*
11037c478bd9Sstevel@tonic-gate 				 * Pad with zeroes to the next pointer boundary
11047c478bd9Sstevel@tonic-gate 				 * (we don't want to disclose kernel data to
11057c478bd9Sstevel@tonic-gate 				 * users), then advance wptr.
11067c478bd9Sstevel@tonic-gate 				 */
11077c478bd9Sstevel@tonic-gate 				(void) memset(last->b_wptr, 0, pad);
11087c478bd9Sstevel@tonic-gate 				last->b_wptr += pad;
11097c478bd9Sstevel@tonic-gate 			}
11107c478bd9Sstevel@tonic-gate 			/* Remember where to write the header information */
11117c478bd9Sstevel@tonic-gate 			sbp->sb_head = last;
11127c478bd9Sstevel@tonic-gate 		} else {
11137c478bd9Sstevel@tonic-gate 			/* Have to allocate additional space for the wrapper */
11147c478bd9Sstevel@tonic-gate 			wrapper = allocb(wrapperlen, BPRI_MED);
11157c478bd9Sstevel@tonic-gate 			if (wrapper == NULL) {
11167c478bd9Sstevel@tonic-gate 				sbclosechunk(sbp);
11177c478bd9Sstevel@tonic-gate 				return;
11187c478bd9Sstevel@tonic-gate 			}
11197c478bd9Sstevel@tonic-gate 			if (pad > 0) {
11207c478bd9Sstevel@tonic-gate 				/*
11217c478bd9Sstevel@tonic-gate 				 * Pad with zeroes (we don't want to disclose
11227c478bd9Sstevel@tonic-gate 				 * kernel data to users).
11237c478bd9Sstevel@tonic-gate 				 */
11247c478bd9Sstevel@tonic-gate 				(void) memset(wrapper->b_wptr, 0, pad);
11257c478bd9Sstevel@tonic-gate 				wrapper->b_wptr += pad;
11267c478bd9Sstevel@tonic-gate 			}
11277c478bd9Sstevel@tonic-gate 			/* Link the wrapper msg onto the end of the chunk */
11287c478bd9Sstevel@tonic-gate 			linkb(mp, wrapper);
11297c478bd9Sstevel@tonic-gate 			/* Remember to write the next header in this wrapper */
11307c478bd9Sstevel@tonic-gate 			sbp->sb_head = wrapper;
11317c478bd9Sstevel@tonic-gate 		}
11327c478bd9Sstevel@tonic-gate 	}
11337c478bd9Sstevel@tonic-gate }
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate /*
11367c478bd9Sstevel@tonic-gate  * Called from timeout().
11377c478bd9Sstevel@tonic-gate  * Signal a timeout by passing a zero-length M_CTL msg in the read-side
11387c478bd9Sstevel@tonic-gate  * to synchronize with any active module threads (open, close, wput, rput).
11397c478bd9Sstevel@tonic-gate  */
11407c478bd9Sstevel@tonic-gate static void
11417c478bd9Sstevel@tonic-gate sbtick(void *arg)
11427c478bd9Sstevel@tonic-gate {
11437c478bd9Sstevel@tonic-gate 	struct sb *sbp = arg;
11447c478bd9Sstevel@tonic-gate 	queue_t	*rq;
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	ASSERT(sbp);
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	rq = sbp->sb_rq;
11497c478bd9Sstevel@tonic-gate 	sbp->sb_timeoutid = 0;		/* timeout has fired */
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	if (putctl(rq, M_CTL) == 0)	/* failure */
11527c478bd9Sstevel@tonic-gate 		sbp->sb_timeoutid = qtimeout(rq, sbtick, sbp, sbp->sb_ticks);
11537c478bd9Sstevel@tonic-gate }
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate /*
11567c478bd9Sstevel@tonic-gate  * Close off the currently accumulating chunk and pass
11577c478bd9Sstevel@tonic-gate  * it upward.  Takes care of resetting timers as well.
11587c478bd9Sstevel@tonic-gate  *
11597c478bd9Sstevel@tonic-gate  * This routine is called both directly and as a result
11607c478bd9Sstevel@tonic-gate  * of the chunk timeout expiring.
11617c478bd9Sstevel@tonic-gate  */
11627c478bd9Sstevel@tonic-gate static void
11637c478bd9Sstevel@tonic-gate sbclosechunk(struct sb *sbp)
11647c478bd9Sstevel@tonic-gate {
11657c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
11667c478bd9Sstevel@tonic-gate 	queue_t	*rq;
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	ASSERT(sbp);
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	if (sbp->sb_timeoutid) {
11717c478bd9Sstevel@tonic-gate 		(void) quntimeout(sbp->sb_rq, sbp->sb_timeoutid);
11727c478bd9Sstevel@tonic-gate 		sbp->sb_timeoutid = 0;
11737c478bd9Sstevel@tonic-gate 	}
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	mp = sbp->sb_mp;
11767c478bd9Sstevel@tonic-gate 	rq = sbp->sb_rq;
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	/*
11797c478bd9Sstevel@tonic-gate 	 * If there's currently a chunk in progress, close it off
11807c478bd9Sstevel@tonic-gate 	 * and try to send it up.
11817c478bd9Sstevel@tonic-gate 	 */
11827c478bd9Sstevel@tonic-gate 	if (mp) {
11837c478bd9Sstevel@tonic-gate 		sbsendit(rq, mp);
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	/*
11877c478bd9Sstevel@tonic-gate 	 * Clear old chunk.  Ready for new msgs.
11887c478bd9Sstevel@tonic-gate 	 */
11897c478bd9Sstevel@tonic-gate 	sbp->sb_tail = sbp->sb_mp = sbp->sb_head = NULL;
11907c478bd9Sstevel@tonic-gate 	sbp->sb_mlen = 0;
11917c478bd9Sstevel@tonic-gate 	sbp->sb_mcount = 0;
11927c478bd9Sstevel@tonic-gate 	if (sbp->sb_flags & SB_DEFER_CHUNK)
11937c478bd9Sstevel@tonic-gate 		sbp->sb_state &= ~SB_FRCVD;
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate }
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate static void
11987c478bd9Sstevel@tonic-gate sbsendit(queue_t *rq, mblk_t *mp)
11997c478bd9Sstevel@tonic-gate {
12007c478bd9Sstevel@tonic-gate 	struct	sb	*sbp = (struct sb *)rq->q_ptr;
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 	if (!canputnext(rq)) {
12037c478bd9Sstevel@tonic-gate 		if (sbp->sb_flags & SB_NO_DROPS)
12047c478bd9Sstevel@tonic-gate 			(void) putq(rq, mp);
12057c478bd9Sstevel@tonic-gate 		else {
12067c478bd9Sstevel@tonic-gate 			freemsg(mp);
12077c478bd9Sstevel@tonic-gate 			sbp->sb_drops += sbp->sb_mcount;
12087c478bd9Sstevel@tonic-gate 		}
12097c478bd9Sstevel@tonic-gate 		return;
12107c478bd9Sstevel@tonic-gate 	}
12117c478bd9Sstevel@tonic-gate 	/*
12127c478bd9Sstevel@tonic-gate 	 * If there are messages on the q already, keep
12137c478bd9Sstevel@tonic-gate 	 * queueing them since they need to be processed in order.
12147c478bd9Sstevel@tonic-gate 	 */
12157c478bd9Sstevel@tonic-gate 	if (qsize(rq) > 0) {
12167c478bd9Sstevel@tonic-gate 		/* should only get here if SB_NO_DROPS */
12177c478bd9Sstevel@tonic-gate 		(void) putq(rq, mp);
12187c478bd9Sstevel@tonic-gate 	}
12197c478bd9Sstevel@tonic-gate 	else
12207c478bd9Sstevel@tonic-gate 		putnext(rq, mp);
12217c478bd9Sstevel@tonic-gate }
1222