/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* STREAMS Buffering module
*
* This streams module collects incoming messages from modules below
* it on the stream and buffers them up into a smaller number of
* aggregated messages. Its main purpose is to reduce overhead by
* cutting down on the number of read (or getmsg) calls its client
* user process makes.
* - only M_DATA is buffered.
* - multithreading assumes configured as D_MTQPAIR
* - packets are lost only if flag SB_NO_HEADER is clear and buffer
* allocation fails.
* - in order message transmission. This is enforced for messages other
* than high priority messages.
* - zero length messages on the read side are not passed up the
* stream but used internally for synchronization.
* FLAGS:
* - SB_NO_PROTO_CVT - no conversion of M_PROTO messages to M_DATA.
* (conversion is the default for backwards compatibility
* hence the negative logic).
* - SB_NO_HEADER - no headers in buffered data.
* (adding headers is the default for backwards compatibility
* hence the negative logic).
* - SB_DEFER_CHUNK - provides improved response time in question-answer
* applications. Buffering is not enabled until the second message
* is received on the read side within the sb_ticks interval.
* This option will often be used in combination with flag SB_SEND_ON_WRITE.
* - SB_SEND_ON_WRITE - a write message results in any pending buffered read
* data being immediately sent upstream.
* - SB_NO_DROPS - bufmod behaves transparently in flow control and propagates
* the blocked flow condition downstream. If this flag is clear (default)
* messages will be dropped if the upstream flow is blocked.
*/
#include <sys/isa_defs.h>
/*
* Per-Stream state information.
*
* If sb_ticks is negative, we don't deliver chunks until they're
* full. If it's zero, we deliver every packet as it arrives. (In
* this case we force sb_chunk to zero, to make the implementation
* easier.) Otherwise, sb_ticks gives the number of ticks in a
* buffering interval. The interval begins when the a read side data
* message is received and a timeout is not active. If sb_snap is
* zero, no truncation of the msg is done.
*/
struct sb {
};
/*
* Function prototypes.
*/
static void sbtick(void *);
static void sbclosechunk(struct sb *);
21, /* mi_idnum */
"bufmod", /* mi_idname */
0, /* mi_minpsz */
INFPSZ, /* mi_maxpsz */
1, /* mi_hiwat */
0 /* mi_lowat */
};
(int (*)())sbrput, /* qi_putp */
(int (*)())sbrsrv, /* qi_srvp */
sbopen, /* qi_qopen */
sbclose, /* qi_qclose */
NULL, /* qi_qadmin */
&sb_minfo, /* qi_minfo */
NULL /* qi_mstat */
};
(int (*)())sbwput, /* qi_putp */
NULL, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&sb_minfo, /* qi_minfo */
NULL /* qi_mstat */
};
&sb_rinit, /* st_rdinit */
&sb_winit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL /* st_muxwinit */
};
/*
* This is the loadable module wrapper.
*/
"bufmod",
&sb_info,
};
/*
* Module linkage information for the kernel.
*/
};
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
/* ARGSUSED */
static int
{
return (EINVAL);
return (0);
/*
* Allocate and initialize per-Stream structure.
*/
sbp->sb_timeoutid = 0;
return (0);
}
/* ARGSUSED1 */
static int
{
/*
* Cancel an outstanding timeout
*/
if (sbp->sb_timeoutid != 0) {
sbp->sb_timeoutid = 0;
}
/*
* Free the current chunk.
*/
}
/*
* Free the per-Stream structure.
*/
return (0);
}
/*
* the correction factor is introduced to compensate for
* whatever assumptions the modules below have made about
* how much traffic is flowing through the stream and the fact
* that bufmod may be snipping messages with the sb_snap length.
*/
static void
{
case SBIOCGCHUNK:
case SBIOCGSNAP:
case SBIOCGFLAGS:
case SBIOCGTIME:
return;
case SBIOCSTIME:
#ifdef _SYSCALL32_IMPL
break;
}
} else
#endif /* _SYSCALL32_IMPL */
{
break;
}
}
if (ticks == 0)
return;
case SBIOCSCHUNK:
/*
* unlikely to run out of resources. Fix at later date.
*/
}
return;
case SBIOCSFLAGS:
return;
case SBIOCSSNAP:
/*
* if chunking dont worry about effects of
* snipping of message size on head flow control
* since it has a relatively small bearing on the
* data rate onto the streamn head.
*/
/*
* unlikely to run out of resources. Fix at later date.
*/
int fudge;
1;
}
}
return;
default:
ASSERT(0);
return;
}
}
/*
* Write-side put procedure. Its main task is to detect ioctls
* for manipulating the buffering state and hand them to sbioctl.
* Other message types are passed on through.
*/
static void
{
case M_IOCTL:
break;
case M_IOCDATA:
/*
* Just free message on failure.
*/
break;
}
case SBIOCSTIME:
case SBIOCSCHUNK:
case SBIOCSFLAGS:
case SBIOCSSNAP:
case SBIOCGTIME:
case SBIOCGCHUNK:
case SBIOCGSNAP:
case SBIOCGFLAGS:
break;
default:
break;
}
break;
default:
break;
}
}
/*
* Read-side put procedure. It's responsible for buffering up incoming
* messages and grouping them into aggregates according to the current
* buffering parameters.
*/
static void
{
case M_PROTO:
break;
} else {
/*
* Convert M_PROTO to M_DATA.
*/
}
/* FALLTHRU */
case M_DATA:
} else
break;
case M_FLUSH:
/*
* Reset timeout, flush the chunk currently in
* progress, and start a new chunk.
*/
if (sbp->sb_timeoutid) {
sbp->sb_timeoutid);
sbp->sb_timeoutid = 0;
}
}
}
break;
case M_CTL:
/*
* Zero-length M_CTL means our timeout() popped.
*/
} else {
}
break;
default:
} else {
/* Note: out of band */
}
break;
}
}
/*
* read service procedure.
*/
/* ARGSUSED */
static void
{
/*
* High priority messages shouldn't get here but if
* one does, jam it through to avoid infinite loop.
*/
/* should only get here if SB_NO_SROPS */
return;
}
}
}
/*
* Handle write-side M_IOCTL messages.
*/
static void
{
struct timeval *t;
int error;
case SBIOCSTIME:
#ifdef _SYSCALL32_IMPL
NULL);
} else
#endif /* _SYSCALL32_IMPL */
{
}
} else {
/*
* Verify argument length.
*/
#ifdef _SYSCALL32_IMPL
sizeof (struct timeval32));
if (error != 0) {
break;
}
break;
}
} else
#endif /* _SYSCALL32_IMPL */
{
if (error != 0) {
break;
}
break;
}
ticks = TIMEVAL_TO_TICK(t);
}
if (ticks == 0)
}
break;
case SBIOCGTIME: {
struct timeval *t;
/*
* Verify argument length.
*/
if (transparent != TRANSPARENT) {
#ifdef _SYSCALL32_IMPL
sizeof (struct timeval32));
if (error != 0) {
break;
}
} else
#endif /* _SYSCALL32_IMPL */
if (error != 0) {
break;
}
}
/*
* If infinite timeout, return range error
* for the ioctl.
*/
break;
}
#ifdef _SYSCALL32_IMPL
if (transparent == TRANSPARENT) {
break;
}
}
if (transparent == TRANSPARENT)
else
} else
#endif /* _SYSCALL32_IMPL */
{
if (transparent == TRANSPARENT) {
break;
}
}
if (transparent == TRANSPARENT)
else
}
break;
}
case SBIOCCTIME:
break;
case SBIOCSCHUNK:
} else {
/*
* Verify argument length.
*/
if (error != 0) {
break;
}
/*
* unlikely to run out of resources. Fix at later date.
*/
}
}
break;
case SBIOCGCHUNK:
/*
* Verify argument length.
*/
if (transparent != TRANSPARENT) {
if (error != 0) {
break;
}
}
if (transparent == TRANSPARENT) {
break;
}
}
if (transparent == TRANSPARENT)
else
break;
case SBIOCSSNAP:
} else {
/*
* Verify argument length.
*/
if (error != 0) {
break;
}
/*
* if chunking dont worry about effects of
* snipping of message size on head flow control
* since it has a relatively small bearing on the
* data rate onto the streamn head.
*/
/*
* head read queue. unlikely to run out
* of resources. Fix at later date.
*/
int fudge;
}
}
}
break;
case SBIOCGSNAP:
/*
* Verify argument length
*/
if (transparent != TRANSPARENT) {
if (error != 0) {
break;
}
}
if (transparent == TRANSPARENT) {
break;
}
}
if (transparent == TRANSPARENT)
else
break;
case SBIOCSFLAGS:
/*
* set the flags.
*/
} else {
if (error != 0) {
break;
}
}
break;
case SBIOCGFLAGS:
/*
* Verify argument length
*/
if (transparent != TRANSPARENT) {
if (error != 0) {
break;
}
}
if (transparent == TRANSPARENT) {
break;
}
}
if (transparent == TRANSPARENT)
else
break;
default:
break;
}
}
/*
* Given a length l, calculate the amount of extra storage
* required to round it up to the next multiple of the alignment a.
*/
#define RoundUpAmt(l, a) ((l) % (a) ? (a) - ((l) % (a)) : 0)
/*
* Calculate additional amount of space required for alignment.
*/
/*
* Smallest possible message size when headers are enabled.
* This is used to calculate whether a chunk is nearly full.
*/
/*
* Process a read-side M_DATA message.
*
* If the currently accumulating chunk doesn't have enough room
* for the message, close off the chunk, pass it upward, and start
* a new one. Then add the message to the current chunk, taking
* account of the possibility that the message's size exceeds the
* chunk size.
*
* If headers are enabled add an sb_hdr header and trailing alignment padding.
*
* To optimise performance the total number of msgbs should be kept
* to a minimum. This is achieved by using any remaining space in message N
* for both its own padding as well as the header of message N+1 if possible.
* If there's insufficient space we allocate one message to hold this 'wrapper'.
* (there's likely to be space beyond message N, since allocb would have
* rounded up the required size to one of the dblk_sizes).
*
*/
static void
{
struct timeval t;
/*
* Truncate the message.
*/
else
/*
* Would the inclusion of this message overflow the current
* chunk? If so close the chunk off and start a new one.
*/
/*
* First message too big for chunk - just send it up.
* This will always be true when we're not chunking.
*/
return;
}
/*
* We now know that the msg will fit in the chunk.
* Link it onto the end of the chunk.
* Since linkb() walks the entire chain, we keep a pointer to
* the first mblk of the last msgb added and call linkb on that
* that last message, rather than performing the
* O(n) linkb() operation on the whole chain.
* sb_head isn't needed in this SB_NO_HEADER mode.
*/
else
} else {
/* Timestamp must be done immediately */
uniqtime(&t);
/*
* Would the inclusion of this message overflow the current
* chunk? If so close the chunk off and start a new one.
*/
/* Allocate leading header of new chunk */
/*
* Memory allocation failure.
* This will need to be revisited
* since using certain flag combinations
* can result in messages being dropped
* silently.
*/
return;
}
}
/*
* Copy header into message
*/
/*
* Join message to the chunk
*/
/*
* If the first message alone is too big for the chunk close
* the chunk now.
* If the next message would immediately cause the chunk to
* overflow we may as well close the chunk now. The next
* message is certain to be at least SMALLEST_MESSAGE size.
*/
return;
}
/*
* Find space for the wrapper. The wrapper consists of:
*
* 1) Padding for this message (this is to ensure each header
* begins on an 8 byte boundary in the userland buffer).
*
* 2) Space for the next message's header, in case the next
* next message will fit in this chunk.
*
* It may be possible to append the wrapper to the last mblk
* of the message, but only if we 'own' the data. If the dblk
* has been shared through dupmsg() we mustn't alter it.
*/
/* Is there space for the wrapper beyond the message's data ? */
;
if (pad > 0) {
/*
* Pad with zeroes to the next pointer boundary
* (we don't want to disclose kernel data to
* users), then advance wptr.
*/
}
/* Remember where to write the header information */
} else {
/* Have to allocate additional space for the wrapper */
return;
}
if (pad > 0) {
/*
* Pad with zeroes (we don't want to disclose
* kernel data to users).
*/
}
/* Link the wrapper msg onto the end of the chunk */
/* Remember to write the next header in this wrapper */
}
}
}
/*
* Called from timeout().
* Signal a timeout by passing a zero-length M_CTL msg in the read-side
* to synchronize with any active module threads (open, close, wput, rput).
*/
static void
{
}
/*
* Close off the currently accumulating chunk and pass
* it upward. Takes care of resetting timers as well.
*
* This routine is called both directly and as a result
* of the chunk timeout expiring.
*/
static void
{
if (sbp->sb_timeoutid) {
sbp->sb_timeoutid = 0;
}
/*
* If there's currently a chunk in progress, close it off
* and try to send it up.
*/
if (mp) {
}
/*
* Clear old chunk. Ready for new msgs.
*/
}
static void
{
if (!canputnext(rq)) {
else {
}
return;
}
/*
* If there are messages on the q already, keep
* queueing them since they need to be processed in order.
*/
/* should only get here if SB_NO_DROPS */
}
else
}