Coherent4.2.10/conf/streams/src/strmlib.c
#define _DDI_DKI 1
#define _SYSV4 1
/*
* This file contains the fundamental STREAMS routines defined in Appendix
* C of the STREAMS Programmer's Guide for System V Release 4. Note that
* additional information about the definition of these routines has been
* taken from the System V Release 4 Multi-Processor DDI/DKI For Intel CPU's.
*
* Later documents significantly redefine the semantics of many STREAMS
* routines. If there is a conflict, the normal System V Release 4 version
* is preferred, and the conflict is noted. However, where possible the later
* semantics have been incorporated or anticipated.
*/
/*
*-IMPORTS:
* <common/ccompat.h>
* __USE_PROTO__
* __ARGS ()
* <kernel/defer.h>
* defer_int_any ()
* <sys/debug.h>
* ASSERT ()
* <sys/types.h>
* uchar_t
* uint_t
* ushort_t
* toid_t
* <sys/kmem.h>
* KM_NOSLEEP
* kmem_alloc ()
* kmem_free ()
* <sys/cmn_err.h>
* CE_WARN
* cmn_err ()
* <sys/strlog.h>
* SL_ERROR
* SL_TRACE
* SL_CONSOLE
* SL_NOTIFY
* SL_FATAL
* SL_WARN
* SL_NOTE
* SL_LEVEL_MASK
* NLOGARGS
* struct log_ctl
* <sys/errno.h>
* EINVAL
* ENOMEM
* EPERM
* <stdarg.h>
* va_start ()
* va_arg ()
* va_end ()
* <string.h>
* memcpy ()
*/
#include <common/ccompat.h>
#include <kernel/defer.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/strlog.h>
#include <sys/errno.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stream.h>
#include <kernel/strmlib.h>
/*
* The pre-SVR4 STREAMS system used a marker bit to determine when putq () and
* putbq () should enable a queue. This flag, documented as QWANTR in the SVR4
* STREAMS documentation, is no longer sufficient to deal with scheduling with
* priority bands. The DDI/DKI and STREAMS documentation in SVR4 do not
* contain detailed new rules for dealing with exactly when a queue should be
* enabled automatically.
*
* The rules that we have adopted to deal with band flow control use our own
* member of the queue structure, "q_lastband". The idea is for getq () and
* rmvq () to record the band of the last message that was dequeued (or to
* set QWANTR), so that when a message in a higher band is queued the stream
* will be enabled to process it. If the last message retrieved is returned
* to the queue because of a flow-control blockage, putq () will see that the
* message is not a member of a higher band that the last read, and thus will
* know not to enable the queue.
*/
/*
* Simple helper macro: is this message a priority message?
*/
#define IS_PRI_MSG(mp) pcmsg ((mp)->b_datap->db_type)
/*
* This internal function actually implements the queue freezing; in addition,
* it asserts that the queue is non-NULL, traces who froze the queue, and
* so forth. If a queue monitoring facility is introduced, this would be the
* place to do it.
*/
#if __USE_PROTO__
pl_t (QFREEZE_TRACE) (queue_t * q, __CONST__ char * name)
#else
pl_t
QFREEZE_TRACE __ARGS ((q, name))
queue_t * q;
__CONST__ char * name;
#endif
{
if (q == NULL)
cmn_err (CE_PANIC, "NULL queue passed to STREAMS utility %s",
name);
return SFREEZE_LOCK (q, name);
}
/*
* This internal function is similar to QFREEZE_TRACE (), but performs the
* assertions and tracing functions for routines that require that the queue
* already be frozen on entry.
*/
#if __USE_PROTO__
void (QFROZEN_TRACE) (queue_t * q, __CONST__ char * name)
#else
void
QFROZEN_TRACE __ARGS ((q, name))
queue_t * q;
__CONST__ char * name;
#endif
{
if (q == NULL)
cmn_err (CE_PANIC, "NULL queue passed to STREAMS utility %s",
name);
SFREEZE_ASSERT_FROZEN (q);
}
/*
* This internal function is for utility routines that walk over various
* queues (and hence perform multiple freeze/unfreeze operations in local
* procedures such as QUEUE_NEXT).
*/
#if __USE_PROTO__
void (QUEUE_TRACE) (queue_t * q, __CONST__ char * name)
#else
void
QUEUE_TRACE __ARGS ((q, name))
queue_t * q;
__CONST__ char * name;
#endif
{
if (q == NULL)
cmn_err (CE_PANIC, "NULL queue passed to STREAMS utility %s",
name);
}
/*
* This internal function follows the singly-threaded lists of priority band
* structures to find the "pri"th priority band attached to the queue. It
* returns NULL if no such band has been allocated.
*
* The queue passed to this function must be frozen.
*/
#if ! _VECTOR_BANDS || _VECTOR_BANDS_TEST
#if __USE_PROTO__
qband_t * (QUEUE_BAND) (queue_t * q, uchar_t pri)
#else
qband_t *
QUEUE_BAND __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
qband_t * qbandp;
uchar_t count = pri;
ASSERT (pri > 0);
QFROZEN_TRACE (q, "QUEUE_BAND");
/*
* Just iterate through the list of band structures.
*/
if ((qbandp = q->q_bandp) != NULL)
while (-- count > 0)
if ((qbandp = qbandp->qb_next) == NULL)
break;
ASSERT (pri > q->q_nband ? qbandp == NULL : qbandp != NULL);
#if _VECTOR_BANDS_TEST
/*
* For extra fun, we define a mode where we work everything out both
* ways to ensure correctness. The #undef makes all the later uses
* of the QUEUE_BAND name map to this function version so that the
* test gets included.
*/
ASSERT (qbandp == QUEUE_BAND (q, pri));
# undef QUEUE_BAND
#endif
return qbandp;
}
/*
* This internal function gets the predecessor to a band structure from a
* queue, or NULL if there is no such predecessor.
*
* The definition of the "qband" structure was public from its introduction in
* SVR4, but was made private in the SVR4 MP release. We could reasonably
* change the definition of this structure to include a back-link, but for
* now we'll do it the hard way and stay with the structure as defined.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
qband_t * (QBAND_PREV) (queue_t * q, qband_t * qbandp)
#else
qband_t *
QBAND_PREV __ARGS ((q, qbandp))
queue_t * q;
qband_t * qbandp;
#endif
{
qband_t * scan;
ASSERT (qbandp != NULL);
QFROZEN_TRACE (q, "QBAND_PREV");
if ((scan = q->q_bandp) == qbandp)
return NULL;
while (scan->qb_next != qbandp) {
scan = scan->qb_next;
ASSERT (scan != NULL);
}
#if _VECTOR_BANDS_TEST
ASSERT (scan == QBAND_PREV (q, qbandp));
# undef QBAND_PREV
#endif
return scan;
}
#endif /* ! _VECTOR_BANDS || _VECTOR_BANDS_TEST */
/*
* This local function attempts to allocate new storage for a priority-band
* structure associated with a queue. Since for a given band "n", band
* structures 1 through "n-1" must also be present, this function may attempt
* to acquire several structures simultaneously.
*
* The caller must have the queue frozen.
*/
#if __USE_PROTO__
__LOCAL__ qband_t * (QBAND_ALLOC) (queue_t * q, uchar_t pri)
#else
__LOCAL__ qband_t *
QBAND_ALLOC __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
qband_t * newband;
int nbands;
QFROZEN_TRACE (q, "QBAND_ALLOC");
ASSERT (pri > 0);
ASSERT (pri > q->q_nband);
/*
* We want to allocate band structures "q->q_nband + 1" ... "pri".
*
* Since the caller has the stream frozen, we want to do this fairly
* quickly, so we allocate the new structures as a vector. In order
* to speed up other operations, we may also try to reallocate any
* existing information so that the entire set of band structures is
* kept in a single block of memory.
*/
#if _VECTOR_BANDS
if (q->q_bandp != NULL) {
/*
* There are some existing band structures to be moved around
* (note that this will require that all the "qb_next"
* pointers be rethreaded).
*/
if ((newband = (qband_t *)
kmem_realloc (q->q_bandp, pri * sizeof (qband_t),
q->q_nband * sizeof (qband_t))) == NULL)
return NULL;
q->q_bandp = newband;
for (nbands = 0 ; nbands < q->q_nband ; newband ++, nbands ++)
newband->qb_next = newband + 1;
/*
* Now we have rethreaded the relocated version of the old
* band structures, "newband" is set correctly to initialize
* the new band structures.
*
* We zero the remaining memory to ease initialization.
*/
memset (newband, 0, (pri - q->q_nband) * sizeof (qband_t));
} else
#endif
if ((newband = (qband_t *)
kmem_zalloc ((pri - q->q_nband) * sizeof (qband_t),
KM_NOSLEEP)) == NULL)
return NULL;
#if ! _VECTOR_BANDS
newband->qb_flag = QB_FIRST;
/*
* Special-case update the "qb_next" pointer of the previously highest
* band.
*/
if (q->q_nband > 0)
QUEUE_BAND (q, q->q_nband)->qb_next = newband;
#endif
/*
* Thread the "qb_next" pointers of the new structures together.
*/
for (nbands = q->q_nband ; nbands < pri - 1 ; newband ++, nbands ++)
newband->qb_next = newband + 1;
return newband;
}
/*
* This internal function performs an atomic read of the "q_next" member of
* the queue passed to it, skipping over queues that are hidden from the
* STREAMS system.
*
* The queue passed to this function must be frozen. After this function, the
* passed queue is unfrozen and the returned queue is frozen (unless it is
* NULL).
*
* Note that this function illustrates a problem with using a fixed-hierarchy
* basic lock scheme; this code can't deadlock with itself because of the
* relationship expressed by the "q_next" member. Locks are only acquired in
* one direction along a stream axis, so each queue lock has an implicit
* hierarchy value lower that its successor.
*/
#if __USE_PROTO__
queue_t * (QUEUE_NEXT) (queue_t * q)
#else
queue_t *
QUEUE_NEXT __ARGS ((q))
queue_t * q;
#endif
{
QFROZEN_TRACE (q, "QUEUE_NEXT");
do {
queue_t * next;
if ((next = q->q_next) != NULL)
(void) QFREEZE_TRACE (next, "QUEUE_NEXT");
QUNFREEZE_TRACE (q, plstr);
q = next;
} while (q != NULL && (q->q_flag & QPROCSOFF) != 0);
return q;
}
/*
* This internal function attempts to schedule a queue. It will fail if the
* queue has no service procedure or if the service procedure has been
* disabled. It will do nothing, but return success, if the queue is currently
* scheduled for service.
*/
#if __USE_PROTO__
__LOCAL__ int (QUEUE_TRYSCHED) (queue_t * q)
#else
__LOCAL__ int
QUEUE_TRYSCHED __ARGS ((q))
queue_t * q;
#endif
{
QFROZEN_TRACE (q, "QUEUE_TRYSCHED");
if (q->q_qinfo->qi_srvp == NULL || (q->q_flag & QPROCSOFF) != 0)
return 0;
if ((q->q_flag & QENAB) != 0 ||
QSCHED_SCHEDULE (q, str_mem->sm_sched) != 0)
return 1;
/*
* Mark the queue as having been scheduled, and if the STREAMS service
* procedure scheduler has not been deferred, do so.
*/
q->q_flag |= QENAB;
if (ATOMIC_FETCH_AND_STORE_UCHAR (ddi_global_data ()->dg_run_strsched,
1) == 0)
defer_int_any (RUN_STREAMS);
return 1;
}
/*
* QUEUE_BACKENAB () is an internal function used to unblock a queue which
* has been blocked by flow control. When a queue with a service procedure
* becomes full, and a queue "behind" it finds that it cannot put into it
* with bcanput (), bcanputnext (), canput (), or canputnext (), the target
* queue is marked so that this procedure will be called when it is ready to
* receive more data.
*
* Since the "behind" queue should have a service procedure (otherwise, the
* flow-control tests would have no point), we scan the queues behind the
* passed queue "q" and enable the first one which has a service procedure.
*
* The queue passed to this function must *not* be frozen. To avoid deadlock,
* a single function cannot hold both sides of a queue frozen; this is the
* only function in the STREAMS library that might plausibly want to do so.
*
* This function is normally called as a result of action in QBAND_REDUCE ()
* or QUEUE_REDUCE (), and under normal circumstances this would not cause an
* inconvenience. However, the action of rvmq () means that we use a condition
* in the "q_flags" member of the queue to indirectly schedule this function
* at a safe time. We only need to test the flag in a path that includes one
* of the above named functions or in user calls to unfreezestr ().
*/
#if __USE_PROTO__
void (QUEUE_BACKENAB) (queue_t * q)
#else
void
QUEUE_BACKENAB __ARGS ((q))
queue_t * q;
#endif
{
int done;
queue_t * other;
pl_t prev_pl;
ASSERT (q != NULL);
/*
* To walk backwards along the queue list, we must first move to the
* other kind of queue and walk along that, which effectively moves us
* in the reverse direction with respect to the original queue type.
*/
other = OTHERQ (q);
prev_pl = QFREEZE_TRACE (other, "QUEUE_BACKENAB");
do {
/*
* Atomically read the "q_next" member of the queue.
*/
if ((other = QUEUE_NEXT (other)) == NULL) {
/*
* Cannot search further.
*/
(void) splx (prev_pl);
break;
}
/*
* To avoid deadlock, we have to unfreeze "other" before we
* look at freezing its partner. We ensure that it won't go
* away on us after we unfreeze it by incrementing the
* "active" count.
*/
other->q_active ++;
QUNFREEZE_TRACE (other, prev_pl);
/*
* We might be attempting to schedule an empty queue, which
* we should allow.
*/
q = OTHERQ (other);
prev_pl = QFREEZE_TRACE (q, "QUEUE_BACKENAB");
done = QUEUE_TRYSCHED (q);
QUNFREEZE_TRACE (q, prev_pl);
/*
* Now we go back and remove our reference to the partner
* queue after relocking it.
*/
prev_pl = QFREEZE_TRACE (other, "QUEUE_BACKENAB");
ASSERT (other->q_active > 0);
other->q_active --;
if ((q->q_flag & QPROCSOFF) != 0 && q->q_active == 0) {
/*
* Time to wake up the sleepers waiting for the put
* and service routines on a queue to exit. See
* qprocsoff () for a discussion of the locking
* protocol.
*/
(void) LOCK (str_mem->sm_proc_lock, plstr);
SV_BROADCAST (str_mem->sm_proc_sv, 0);
UNLOCK (str_mem->sm_proc_lock, plstr);
}
} while (! done);
QUNFREEZE_TRACE (other, prev_pl);
}
/*
* This local function is called whenever a queue's last message has been
* dequeued, and will wake up any function waiting for the queue to drain.
*
* The caller must have the stream frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QUEUE_DRAINED) (queue_t * q)
#else
__LOCAL__ void
QUEUE_DRAINED __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
QFROZEN_TRACE (q, "QUEUE_DRAINED");
if ((q->q_flag & QDRAIN) != 0) {
shead_t * sheadp;
/*
* Under normal circumstances, a call to SV_BROADCAST () would
* be all we needed to do. However, since we allow ourself to
* use a more primitive kind of lock that a basic lock to
* implement freezing a queue, the process that waits for us
* to drain cannot use the frozen queue as the lock to pass to
* SV_WAIT (). By making us wait for the lock, we make
* the wakeup multiprocessor-safe; otherwise, we might be able
* to try and wake the process before it has actually slept,
* causing infinite sleep.
*
* Since the synchronization variable we are going to
* broadcast to is part of the stream head, we ensure that
* this code is only executed for the module or driver which
* is immediately below the stream head, or the stream head
* write queue itself.
*/
ASSERT (RD (q)->q_next == NULL ||
RD (q)->q_next->q_next == NULL);
sheadp = (shead_t *)
(RD (q)->q_next == NULL ? q->q_ptr :
RD (q)->q_next->q_ptr);
prev_pl = SHEAD_LOCK (sheadp);
SV_BROADCAST (sheadp->sh_wait_sv, 0);
q->q_flag &= ~ QDRAIN;
SHEAD_UNLOCK (sheadp, prev_pl);
}
}
/*
* This local function updates the flow-control information for a priority
* band when the band current level is increasing.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QBAND_INCREASE) (queue_t * q, qband_t * qbandp, ulong_t adjust)
#else
__LOCAL__ void
QBAND_INCREASE __ARGS ((q, qbandp, adjust))
queue_t * q;
qband_t * qbandp;
ulong_t adjust;
#endif
{
ASSERT (qbandp != NULL);
QFROZEN_TRACE (q, "QBAND_INCREASE");
/*
* If the band we are dealing with is already flow-controlled, just
* get out of here.
*/
qbandp->qb_count += adjust;
if ((qbandp->qb_flag & QB_FULL) != 0)
return;
/*
* If we are over the high-water mark, then we must mark ourself and
* all the bands below us (including the normal messages) as full as
* well.
*/
if (qbandp->qb_count >= qbandp->qb_hiwat) {
do
qbandp->qb_flag |= QB_FULL;
while ((qbandp = QBAND_PREV (q, qbandp)) != NULL);
/*
* According to the way I read the STREAMS
* programmer's guide, band flow control
* should affect normal messages as well (ie,
* as if they are band 0).
*
* We deal with this as a special case.
*/
if (q->q_count >= q->q_hiwat)
q->q_flag |= QFULL;
}
}
/*
* This local function updates the flow-control information for a priority
* band when the band current level is decreasing.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QBAND_REDUCE) (queue_t * q, qband_t * qbandp, ulong_t adjust)
#else
__LOCAL__ void
QBAND_REDUCE __ARGS ((q, qbandp, adjust))
queue_t * q;
qband_t * qbandp;
ulong_t adjust;
#endif
{
ASSERT (qbandp != NULL);
ASSERT (qbandp->qb_count >= adjust);
QFROZEN_TRACE (q, "QBAND_REDUCE");
/*
* If this band wasn't flow-controlled anyway, then we don't need to
* do anything other than adjust the count.
*/
qbandp->qb_count -= adjust;
if ((qbandp->qb_flag & QB_FULL) == 0)
return;
/*
* If we are under the low-water mark, then we can release any pending
* writes to this band and our hold on lower bands if and only if the
* bands above us are not controlled.
*
* We can test the bands above us in one step since we require that a
* band which has become full also mark all lower bands as full. If
* our immediate upper neighbour is full then we cannot proceed.
*/
if (qbandp->qb_count <= qbandp->qb_lowat &&
(qbandp->qb_next == NULL ||
(qbandp->qb_next->qb_flag & QB_FULL) == 0)) {
/*
* We release lower bands as long as they are under their low-
* water marks. It seems pointless to resume bands that are
* too close to entering a controlled state anyway.
*/
do {
qbandp->qb_flag &= ~ QB_FULL;
if ((qbandp->qb_flag & QB_WANTW) != 0) {
/*
* Propagate a back-enable request to the
* queue.
*/
qbandp->qb_flag &= ~ QB_WANTW;
q->q_flag |= QBACK;
}
if ((qbandp = QBAND_PREV (q, qbandp)) == NULL) {
/*
* According to the way I read the STREAMS
* programmer's guide, band flow control
* should affect normal messages as well (ie,
* as if they are band 0).
*
* We deal with this as a special case.
*/
if (q->q_count <= q->q_lowat) {
q->q_flag &= ~ QFULL;
if ((q->q_flag & QWANTW) != 0) {
q->q_flag &= ~ QWANTW;
q->q_flag |= QBACK;
}
}
break;
}
} while (qbandp->qb_count <= qbandp->qb_lowat);
}
}
/*
* This local function factors out the computation of the total number of
* data bytes in a message from the routines that manipulate flow-control
* parameters. Since this is often the only loop in such routines, factoring
* this out might help inlining in some cases.
*/
#if __USE_PROTO__
__LOCAL__ ulong_t (MSG_SIZE) (mblk_t * mp)
#else
__LOCAL__ ulong_t
MSG_SIZE __ARGS ((mp))
mblk_t * mp;
#endif
{
ulong_t total = 0;
ASSERT (mp != NULL);
do
total += mp->b_wptr - mp->b_rptr;
while ((mp = mp->b_cont) != NULL);
return total;
}
/*
* This internal function wraps up the check for whether a newly queued
* message is of sufficient priority to cause the queue to be enabled.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ int (QUEUE_CHECK_SCHED) (queue_t * q, mblk_t * mp,
qband_t * qbandp)
#else
__LOCAL__ int
QUEUE_CHECK_SCHED __ARGS ((q, mp, qbandp))
queue_t * q;
mblk_t * mp;
qband_t * qbandp;
#endif
{
ulong_t msgsize;
QFROZEN_TRACE (q, "QUEUE_CHECK_SCHED");
if ((q->q_flag & QNOENB) == 0 &&
((q->q_flag & QWANTR) != 0 || q->q_lastband < mp->b_band)) {
/*
* The new message is part of a higher-priority band than the
* last message that was retrieved, so we need to enable the
* queue.
*/
q->q_lastband = mp->b_band;
(void) QUEUE_TRYSCHED (q);
}
/*
* As a convenience to the callers, all of whom are queueing the new
* message, update the flow control parameters.
*/
msgsize = MSG_SIZE (mp);
if (mp->b_band > 0) {
ASSERT (qbandp != NULL);
QBAND_INCREASE (q, qbandp, msgsize);
return 1;
} else {
/*
* Just deal with the base flow-control stuff.
*/
if ((q->q_count += msgsize) > q->q_hiwat)
q->q_flag |= QFULL;
return 0;
}
}
/*
* This internal function collects the details of dequeuing a message from
* a priority-band structure.
*
* The queue on which "mp" was queued must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QBAND_DEQUEUE) (qband_t * qbandp, mblk_t * mp)
#else
__LOCAL__ void
QBAND_DEQUEUE __ARGS ((qbandp, mp))
qband_t * qbandp;
mblk_t * mp;
#endif
{
ASSERT (qbandp != NULL);
ASSERT (mp != NULL);
ASSERT (mp->b_band > 0);
if (qbandp->qb_first == mp) {
if (qbandp->qb_last == mp) {
/*
* The band has become empty.
*/
ASSERT (mp->b_next == NULL ||
mp->b_next->b_band < mp->b_band);
qbandp->qb_last = qbandp->qb_first = NULL;
} else {
/*
* There is more data in the band.
*/
ASSERT (mp->b_next != NULL);
ASSERT (mp->b_next->b_band == mp->b_band);
qbandp->qb_first = mp->b_next;
}
} else if (qbandp->qb_last == mp) {
/*
* The band is non-empty, but we have to move the end up.
*/
ASSERT (mp->b_prev != NULL);
ASSERT (mp->b_prev->b_band == mp->b_band);
qbandp->qb_last = mp->b_prev;
}
}
/*
* This internal function tests to see whether a stream wanting to write to
* this queue should block to honour the flow-control scheme. The return
* value is 1 if the caller can write to this queue, 0 otherwise.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ int (QBAND_CANPUT) (queue_t * q, uchar_t pri)
#else
__LOCAL__ int
QBAND_CANPUT __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
qband_t * qbandp;
QFROZEN_TRACE (q, "QBAND_CANPUT");
if (pri > 0) {
qbandp = QUEUE_BAND (q, pri);
if (qbandp != NULL && (qbandp->qb_flag & QB_FULL) != 0) {
/*
* We must set the following flag to indicate that
* back-enabling is desired for this priority band.
*
* We'll set the global QWANTW flag as well, to make
* testing simpler for qprocson ().
*/
qbandp->qb_flag |= QB_WANTW;
q->q_flag |= QWANTW;
return 0;
}
} else
if ((q->q_flag & QFULL) != 0) {
/*
* We must set the following flag to indicate that the
* queue has a writer who has made a failed write
* attempt.
*/
q->q_flag |= QWANTW;
return 0;
}
return 1;
}
/*
* This internal function collects the non-priority-band flow-control actions
* from flushq () and getq ().
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QUEUE_REDUCE) (queue_t * q, ulong_t adjust)
#else
__LOCAL__ void
QUEUE_REDUCE __ARGS ((q, adjust))
queue_t * q;
ulong_t adjust;
#endif
{
QFROZEN_TRACE (q, "QUEUE_REDUCE");
ASSERT (q->q_count >= adjust);
/*
* If the queue was not previously full, then we don't need to do
* anything other than tweak the count.
*/
q->q_count -= adjust;
if ((q->q_flag & QFULL) != 0 && q->q_count <= q->q_lowat) {
q->q_flag &= ~ QFULL;
if ((q->q_flag & QWANTW) != 0) {
q->q_flag &= ~ QWANTW;
q->q_flag |= QBACK;
}
}
}
/*
* This function contains part of the M_SETOPTS processing; specifically, here
* we deal with changing the watermarks of a band. The caller should test the
* QBACK flag when unfreezing the stream.
*/
#if __USE_PROTO__
void (QBAND_SETOPT) (queue_t * q, struct stroptions * so)
#else
void
QBAND_SETOPT __ARGS ((q, so))
queue_t * q;
struct stroptions
* so;
#endif
{
qband_t * qbandp;
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "QBAND_SETOPTS");
if ((so->so_flags & (SO_LOWAT | SO_HIWAT)) == 0)
return;
if ((so->so_flags & SO_BAND) != 0 && so->so_band > 0) {
if ((qbandp = QUEUE_BAND (q, so->so_band)) == NULL &&
(qbandp = QBAND_ALLOC (q, so->so_band)) == NULL) {
/*
* We cannot allocate space for the band accounting
* structures. Give up.
*/
QUNFREEZE_TRACE (q, prev_pl);
return;
}
if ((so->so_flags & SO_LOWAT) != 0) {
qbandp->qb_lowat = so->so_lowat;
QBAND_REDUCE (q, qbandp, 0L);
}
if ((so->so_flags & SO_HIWAT) != 0) {
qbandp->qb_hiwat = so->so_hiwat;
QBAND_INCREASE (q, qbandp, 0L);
}
} else {
if ((so->so_flags & SO_LOWAT) != 0) {
q->q_lowat = so->so_lowat;
QUEUE_REDUCE (q, 0L);
}
if ((so->so_flags & SO_HIWAT) != 0) {
q->q_hiwat = so->so_hiwat;
if (q->q_count > q->q_hiwat)
q->q_flag |= QFULL;
}
}
/*
* Since we are in a path with QBAND_REDUCE (), check for QBACK before
* unfreezing the stream.
*/
{
unsigned long back;
if ((back = q->q_flag & QBACK) != 0)
q->q_flag &= ~ QBACK;
QUNFREEZE_TRACE (q, prev_pl);
if (back)
QUEUE_BACKENAB (q);
}
}
/*
* This internal function is used by bufcall () and esbbcall () to queue a
* STREAMS event cell on the queue for eventual processing once memory
* becomes available.
*/
#if __USE_PROTO__
__LOCAL__ toid_t (STORE_EVENT) (sevent_t * seventp, int pri)
#else
__LOCAL__ toid_t
STORE_EVENT __ARGS ((seventp, pri))
sevent_t * seventp;
int pri;
#endif
{
pl_t prev_pl;
selist_t * selistp;
sevent_t * sprev;
/*
* Let's lock the list that we are going to thread the new event on.
*/
selistp = & str_mem->sm_bcevents [MAP_PRI_LEVEL (pri)];
prev_pl = SELIST_LOCK (selistp);
#if _TOID_MEMBER
/*
* Before we perform the insertion of the event cell, we use the above
* lock to make our ID code generation multiprocessor-safe.
*/
selistp->sl_id = (((seventp->se_id = selistp->sl_id) +
TOID_INCREMENT) % TOID_MODULUS);
ASSERT (seventp->se_id != 0);
ASSERT (TOID_TO_PRI (seventp->se_id) == pri);
#endif
/*
* RESEARCH NOTE: There doesn't seem to be any compelling reason to
* choose a particular policy for managing event cells. Try getting
* some data of performance effects for low->high, high->low and FIFO
* queueing.
*/
#if _FIFO_BUFCALL
/*
* FIFO queueing is nice and simple.
*/
if ((sprev = selistp->sl_tail) == NULL) {
ASSERT (selistp->sl_head == NULL);
selistp->sl_head = selistp->sl_tail = seventp;
seventp->se_prev = seventp->se_next = NULL;
} else {
sprev->se_next = seventp;
seventp->se_prev = sprev;
seventp->se_next = NULL;
selistp->sl_tail = seventp;
}
#else
/*
* Now insert the event cell in the event list, keeping it sorted from
* low size to high size.
*/
{
sevent_t * sscan;
for (sscan = selistp->sl_head, sprev = NULL ; sscan != NULL ;
sscan = (sprev = sscan)->se_next) {
if (sscan->se_size > seventp->se_size)
break;
}
if (sprev == NULL)
selistp->sl_head = seventp;
else
sprev->se_next = seventp;
if (sscan != NULL)
sscan->se_prev = seventp;
seventp->se_prev = sprev;
seventp->se_next = sscan;
}
#endif
SELIST_UNLOCK (selistp, prev_pl);
#if _TOID_MEMBER
return seventp->se_id;
#endif
}
/*
* This local function factors out the common code in put () and
* QUEUE_PUTNEXT () [used to implement the putnext (), putnextctl () and
* similar functions].
*
* The queue must be frozen on entry to this function.
*/
#if __USE_PROTO__
__LOCAL__ void (QUEUE_PUT) (queue_t * q, mblk_t * mp, pl_t prev_pl)
#else
__LOCAL__ void
QUEUE_PUT __ARGS ((q, mp, prev_pl))
queue_t * q;
mblk_t * mp;
pl_t prev_pl;
#endif
{
QFROZEN_TRACE (q, "QUEUE_PUT");
q->q_active ++;
if (q->q_qinfo->qi_mstat != NULL)
q->q_qinfo->qi_mstat->ms_pcnt ++;
QUNFREEZE_TRACE (q, prev_pl);
(* q->q_qinfo->qi_putp) (q, mp);
(void) QFREEZE_TRACE (q, "QUEUE_PUT");
q->q_active --;
if ((q->q_flag & QPROCSOFF) != 0 && q->q_active == 0) {
/*
* Time to wake up the sleepers waiting for the put and
* service routines on a queue to exit. See qprocsoff () for
* a discussion of the locking protocol.
*/
(void) LOCK (str_mem->sm_proc_lock, plstr);
SV_BROADCAST (str_mem->sm_proc_sv, 0);
UNLOCK (str_mem->sm_proc_lock, plstr);
}
}
/*
* This local function factors out most of the interesting part of putnext ()
* into a common block that can be called from any of the related group of
* functions putnext (), putnextctl (), putnextctl1 (), and qreply ().
*/
#if __USE_PROTO__
__LOCAL__ void (QUEUE_PUTNEXT) (queue_t * q, mblk_t * mp)
#else
__LOCAL__ void
QUEUE_PUTNEXT __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
pl_t prev_pl;
/*
* We call QUEUE_NEXT () to find the next queue on the stream that has
* not been disabled via qprocsoff (). It may be that there is no
* such queue. This can happen on a uniprocessor or multiprocessor if
* a device's interrupt routine begins generating data before the
* queue open () entry point has issued qprocson (). Since this is
* really an error, we complain about it.
*/
prev_pl = QFREEZE_TRACE (q, "QUEUE_PUTNEXT");
q = QUEUE_NEXT (q);
if (q != NULL) {
/*
* We have found a queue we are allowed to put to.
*/
QUEUE_PUT (q, mp, prev_pl);
} else {
/*
* We have run off the end of the stream... we can
* either wait, put the message anyway, or discard
* the message. Either way, a warning is appropriate.
*/
cmn_err (CE_WARN, "putnext () ran off end of stream");
freemsg (mp);
}
QUNFREEZE_TRACE (q, prev_pl);
}
/*
* This internal function is similar in spirit to QUEUE_PUTNEXT (), above, but
* instead factors out the process of looking for a queue with a service
* procedure that can be enabled from the bcanputnext () and canputnext ()
* routines.
*
* Determines whether the caller can legitimately put downstream in the
* indicated priority band.
*
* The queue passed to this function cannot be frozen.
*/
#if __USE_PROTO__
__LOCAL__ int (QBAND_SRVNEXT) (queue_t * q, uchar_t pri)
#else
__LOCAL__ int
QBAND_SRVNEXT __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
int retval;
pl_t prev_pl;
/*
* Scan through the stream for a queue with a service procedure that
* we can enable.
*/
prev_pl = QFREEZE_TRACE (q, "QBAND_SRVNEXT");
for (;;) {
q = QUEUE_NEXT (q);
if (q == NULL) {
/*
* We have run off the end of the stream, so return
* true to the caller.
*/
retval = 1;
break;
} else if (q->q_qinfo->qi_srvp != NULL) {
retval = QBAND_CANPUT (q, pri);
break;
}
/*
* No service procedure, try next queue in line.
*/
}
QUNFREEZE_TRACE (q, prev_pl);
/*
* If there are no queues with service procedures, the caller is
* allowed to put by default; the special case above is an exception.
*/
return retval;
}
/*
* This function deals with allocating and initializing a message block from
* the message memory; unlike allocb (), an extra parameter can be used to
* cause this function to block if there is insufficient memory available to
* satisfy the request immediately.
*/
#if __USE_PROTO__
mblk_t * (MSGB_ALLOC) (size_t size, int pri, int flag)
#else
mblk_t *
MSGB_ALLOC __ARGS ((size, pri, flag))
size_t size;
int pri;
int flag;
#endif
{
mblk_t * mblkp;
ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP);
ASSERT (pri == BPRI_LO || pri == BPRI_MED || pri == BPRI_HI);
if ((mblkp = STRMEM_ALLOC (MSGB_SIZE (size), pri, flag)) != NULL) {
/*
* Note that we don't bother setting up the b_prev and
* b_next members of the message block.
*/
mblkp->b_cont = NULL;
mblkp->b_datap = MB_TO_DB (mblkp);
mblkp->b_rptr = DB_TO_DATA (mblkp->b_datap);
mblkp->b_wptr = mblkp->b_rptr;
/*
* We specially flag this message block as part of
* a triple. While there are other ways we could test
* this, for now we permit the possibility that a
* client might change the attachments of data blocks
* and message blocks perversely.
*/
mblkp->b_flag = MSGTRIPLE;
mblkp->b_band = 0;
mblkp->b_datap->db_base = mblkp->b_rptr;
mblkp->b_datap->db_lim = mblkp->b_rptr + size;
mblkp->b_datap->db_type = M_DATA;
mblkp->b_datap->db_ref = 1;
mblkp->b_datap->db_frtnp = NULL;
}
return mblkp;
}
/*
* This local helper function factors out some code in strlog () common to
* building the "struct log_ctl" headers for the log messages.
*/
#if __USE_PROTO__
__LOCAL__ mblk_t * (STRLOG_MAKE) (short mid, short sid, char level,
ushort_t flags, ulong_t seq, ushort_t whoami,
int * copies, int * failures, mblk_t * data)
#else
__LOCAL__ mblk_t *
STRLOG_MAKE __ARGS ((mid, sid, level, flags, seq, whoami, copies, failures, data))
short mid;
short sid;
char level;
ushort_t flags;
ulong_t seq;
ushort_t whoami;
int * copies;
int * failures;
mblk_t * data;
#endif
{
mblk_t * ctl;
struct log_ctl * lcp;
int alloc_pri;
int log_pri;
if ((flags & whoami) == 0)
return NULL;
/*
* Work out an allocation priority for the M_PROTO message block that
* will contain the "log_ctl" description structure, and also
* work out a priority for the "mwc_pri" member of the "log_ctl"
* structure.
*
* This code is pretty funky; the real rules might be much simpler,
* but this is what I get from the docs. The "log_pri" rules go from
* low to high priority, so this code should match the order defined
* for the
*/
if (whoami == SL_ERROR || (flags & SL_FATAL) != 0)
alloc_pri = BPRI_HI;
else if ((flags & SL_WARN) != 0)
alloc_pri = BPRI_MED;
else
alloc_pri = BPRI_LO;
/*
* if (whoami == SL_CONSOLE)
*/
log_pri = LOG_INFO;
if ((flags & SL_NOTE) != 0)
log_pri = LOG_NOTICE;
if (whoami == SL_TRACE)
log_pri = LOG_DEBUG;
if ((flags & SL_WARN) != 0)
log_pri = LOG_WARNING;
if (whoami == SL_ERROR)
log_pri = LOG_ERR;
if ((flags & SL_FATAL) != 0)
log_pri = LOG_CRIT;
/*
* Allocate the necessary structures and fill them in.
*/
if ((ctl = MSGB_ALLOC (sizeof (struct log_ctl), alloc_pri,
KM_NOSLEEP)) == NULL) {
(* failures) ++;
return NULL; /* return failure */
}
if (* copies == 0)
ctl->b_cont = data;
else if ((ctl->b_cont = dupb (data)) == NULL) {
freeb (ctl);
(* failures) ++;
return NULL;
}
(* copies) ++;
ctl->b_datap->db_type = M_PROTO;
lcp = (struct log_ctl *) ctl->b_rptr;
ctl->b_wptr = (uchar_t *) (lcp + 1);
lcp->mwc_mid = mid;
lcp->mwc_sid = sid;
lcp->level = level;
lcp->flags = flags;
lcp->mwc_seqno = seq;
lcp->mwc_pri = log_pri | LOG_KERN;
return ctl;
}
/*
* This function factors out common code from putbq () and putq () for finding
* the insertion point for a message. The original routines differed only in
* whether the search was for the end of a band or the front of a band. By
* passing in an appropriately adjusted band parameter, the same code can be
* made to do double duty.
*
* The queue passed to this function must be frozen.
*/
#if __USE_PROTO__
__LOCAL__ void (QUEUE_PLACE_MSG) (queue_t * q, mblk_t * mp, uchar_t band)
#else
__LOCAL__ void
QUEUE_PLACE_MSG __ARGS ((q, mp, band))
queue_t * q;
mblk_t * mp;
uchar_t band;
#endif
{
mblk_t * scan;
/*
* The first stage is to skip over all the high-priority messages, and \
* then to find the right band location.
*/
for (scan = q->q_first ; scan != NULL ; scan = scan->b_next)
if (! IS_PRI_MSG (scan))
break;
while (scan != NULL && scan->b_band > band)
scan = scan->b_next;
/*
* Now we know where the message goes, do it.
*/
if ((mp->b_next = scan) == NULL) {
if ((mp->b_prev = q->q_last) == NULL)
q->q_first = mp;
else
mp->b_prev->b_next = mp;
q->q_last = mp;
} else {
if ((mp->b_prev = scan->b_prev) == NULL)
q->q_first = mp;
else
mp->b_prev->b_next = mp;
scan->b_prev = mp;
}
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* adjmsg Trim bytes from a message
*
*-SYNOPSIS:
* #include <sys/streams.h>
*
* int adjmsg (mblk_t * mp, int len);
*
*-ARGUMENTS:
* mp Pointer to the message to be trimmed.
*
* len The number of bytes to be removed.
*
*-DESCRIPTION:
* adjmsg () removes bytes from a message. |"len"| (the absolute value of
* "len") specifies the how many bytes are to be removed. If "len" is
* greater than 0, bytes are removed from the head of the message. If
* "len" is less than 0, bytes are removed from the tail. adjmsg () fails
* if |"len"| is greater than the number of bytes in "mp". If "len" spans
* more than one message block in the message, the message blocks must be
* the same type, or else adjmsg () will fail.
*
*-RETURN VALUE:
* If the message can be trimmed successfully, 1 is returned. Otherwise,
* 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* If "len" is greater than the amount of data in a single message block,
* that message block is not freed. Rather, it is left linked in the
* message, and its read and write pointers are set equal to each other,
* indicating no data present in the block.
*
*-SEE ALSO:
* msgb
*/
#if __USE_PROTO__
int (adjmsg) (mblk_t * mp, int len)
#else
int
adjmsg __ARGS ((mp, len))
mblk_t * mp;
int len;
#endif
{
uchar_t msgtype = mp->b_datap->db_type;
int result = 1;
ASSERT (mp != NULL);
/*
* The DDI/DKI does not specify whether the message blocks are to be
* left unmodified in the case of failure. For now, we interpret the
* semantics of the routine such that a failure leaves all the
* requested data removed, with the return value supplying an
* indication that overflow occurred.
*/
if (len >= 0) {
/*
* Remove bytes forward from the head of the message.
*/
while (len != 0) {
mblk_t * next;
if (mp->b_wptr - mp->b_rptr >= len) {
/*
* We are only comsuming part of the message.
*/
mp->b_rptr += len;
return 1;
} else if ((next = mp->b_cont) == NULL) {
/*
* All the message blocks in the message have
* been totally consumed, so return failure.
*/
mp->b_rptr = mp->b_wptr;
return 0;
}
/*
* This message block has been consumed, so leave it
* empty.
*/
len -= mp->b_wptr - mp->b_rptr;
mp->b_wptr = mp->b_rptr = mp->b_datap->db_base;
mp = next;
if (mp->b_datap->db_type != msgtype)
return 0; /* wrong types */
}
} else {
mblk_t * span = mp;
int size = 0;
/*
* Since message blocks only have forward links, we have to
* employ some subterfuge to implement this efficiently. We
* first scan forward to discover the size and start of the
* last span of message blocks with the same type.
*/
for (;;) {
size += mp->b_wptr - mp->b_rptr;
if ((mp = mp->b_cont) == NULL)
break;
if (mp->b_datap->db_type != msgtype) {
/*
* The type has changed, so we reset our count
* and start pointers to begin a new span.
*/
size = 0;
msgtype = mp->b_datap->db_type;
span = mp;
}
}
if (size < len) {
/*
* The user has requested too many bytes be removed,
* so we return a failure indication and remove as
* many as we are able.
*/
result = 0;
len = size;
}
/*
* Now we have the last span, we can scan forward for the
* point where we must begin emptying messages.
*/
while (size > 0) {
size -= span->b_wptr - span->b_rptr;
if (size < len) {
/*
* The count of bytes remaining in the span
* is less than the threshold, so we start to
* remove data.
*/
span->b_wptr -= (len - size);
len = size;
}
span = span->b_next;
}
}
return result;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* allocb Allocate a message block
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* mblk_t * allocb (int size, uint_t pri);
*
*-ARGUMENTS:
* size The number of bytes in the message block.
*
* pri Priority of the request. This can take on one of three
* values: BPRI_LO, BPRI_MED, or BPRI_HI.
*
*-DESCRIPTION:
* allocb () tries to allocate a STREAMS message block. Buffer allocation
* fails only when the system is out of memory. If no buffer is
* available, the bufcall () function can help a module recover from an
* allocation failure.
*
* The "pri" argument is a hint to the allocator indicating how badly the
* message is needed. BPRI_LO should be used for normal data allocations,
* BPRI_MED should be used for other non-critical allocations. BPRI_HI
* should be used for allocations that absolutely must succeed, even
* though success is not guaranteed. Some implementations may choose to
* ignore this parameter.
*
*-RETURN VALUE:
* If successful, allocb () returns a pointer to the allocated message
* block of type M_DATA (defined in <sys/stream.h>). If a block cannot be
* allocated, a NULL pointer is returned.
*
*-LEVEL:
* Base or Interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* bufcall (), esballoc (), esbbcall (), freeb (), msgb
*/
#if __USE_PROTO__
mblk_t * (allocb) (int size, uint_t pri)
#else
mblk_t *
allocb __ARGS ((size, pri))
uint_t size;
uint_t pri;
#endif
{
ASSERT (size >= 0);
ASSERT (ATOMIC_FETCH_UCHAR (str_mem->sm_init) &&
str_mem->sm_msg_lock != NULL);
/*
* Since we allocate things in triples, "allocb (0)" could be legal if
* we wanted. There aren't any compelling reasons either way, but for
* simplicity I'll permit it.
*/
#if 0
if (size == 0)
return NULL;
#endif
return MSGB_ALLOC (size, pri, KM_NOSLEEP);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* bcanput Test for flow control in specified priority band.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* int bcanput (queue_t * q, uchar_t pri);
*
*-ARGUMENTS:
* q Pointer to the message queue.
*
* pri Message priority.
*
*-DESCRIPTION:
* bcanput () tests if there is room for a message in priority band "pri"
* of the queue pointed to by "q". The queue _must_ have a service
* procedure.
*
* If "pri" is 0, the bcanput () call is equivalent to a call to
* canput ().
*
* It is possible because of race conditions to test for room using
* bcanput () and get an indication that there is room for a message, and
* then have the queue fill up before subsequently enqueuing the message,
* causing a violation of flow control. This is not a problem, since the
* violation of flow control in this case is bounded.
*
*-RETURN VALUE:
* bcanput () returns 1 if a message of priority "pri" can be placed on
* the queue. 0 is returned if a message of priority "pri" cannot be
* enqueued because of flow control within the priority band.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The driver writer is responsible for both testing a queue with
* bcanput () and refraining from placing a message if bcanput () fails.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument may not reference "q_next" (for example, an argument
* of "q->q_next" is erroneous on a multiprocessor and is disallowed by
* the DDI/DKI). "bcanputnext (q)" is provided as a multiprocessor-safe
* equivalent to the common call "bcanput (q->q_next)", which is no
* longer allowed [see bcanputnext ()].
*
*-SEE ALSO:
* bcanputnext (), canput (), canputnext (), putbq (), putnext ()
*/
#if __USE_PROTO__
int (bcanput) (queue_t * q, uchar_t pri)
#else
int
bcanput __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
pl_t prev_pl;
int result = 1;
prev_pl = QFREEZE_TRACE (q, "bcanput");
result = QBAND_CANPUT (q, pri);
QUNFREEZE_TRACE (q, prev_pl);
return result;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* bcanputnext Test for flow control in specified priority band.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* int bcanputnext (queue_t * q, uchar_t pri);
*
*-ARGUMENTS:
* q Pointer to the message queue.
*
* pri Message priority.
*
*-DESCRIPTION:
* bcanputnext () searches through the stream (starting at "q->q_next")
* until it finds a queue containing a service routine, or until it
* reaches the end of the stream. If found, the queue containing the
* service routine is tested to see if a message in priority band "pri"
* can be enqueued. If the band is full, bcanputnext () marks the queue
* to automatically back-enable the caller's service routine when the
* amount of data in messages on the queue has reached its low water
* mark.
*
* If "pri" is 0, the bcanputnext () call is equivalent to a call to
* canputnext ().
*
* It is possible because of race conditions to test for room using
* bcanputnext () and get an indication that there is room for a message,
* and then have the queue fill up before subsequently enqueuing the
* message, causing a violation of flow control. This is not a problem,
* since the violation of flow control in this case is bounded.
*
*-RETURN VALUE:
* bcanputnext () returns 1 if a message of priority "pri" can be sent in
* the stream, or 0 if the stream is flow-controlled. If bcanputnext ()
* reaches the end of the stream without finding a queue with a service
* procedure, then it returns 1.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The driver writer is responsible for both testing a queue with
* bcanputnext () and refraining from placing a message if bcanputnext ()
* fails.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument may not reference "q_next" (for example, an argument
* of "q->q_next" is erroneous on a multiprocessor and is disallowed by
* the DDI/DKI). "bcanputnext (q)" is provided as a multiprocessor-safe
* equivalent to the common call "bcanput (q->q_next)", which is no
* longer allowed [see bcanputnext ()].
*
*-SEE ALSO:
* bcanput (), canput (), canputnext (), putbq (), putnext ()
*/
#if __USE_PROTO__
int (bcanputnext) (queue_t * q, uchar_t pri)
#else
int
bcanputnext __ARGS ((q, pri))
queue_t * q;
uchar_t pri;
#endif
{
QUEUE_TRACE (q, "bcanputnext");
return QBAND_SRVNEXT (q, pri);
}
/*
* Note: backq () is not present in the System V DDI/DKI For Intel Processors
* with Multiprocessing reference, and is listed in one of the appendices of
* that volume as having been removed in the R3.2 to R4 transition. However,
* the function is present in the regular generic System V DDI/DKI.
*/
#if 0
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* backq Get pointer to the queue behind the current queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* queue_t * backq (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the current queue.
*
*-DESCRIPTION:
* backq () returns a pointer to the queue preceding "q". If "q" is a
* read queue, backq () returns a pointer to the queue downstream from
* "q", unless it is the stream end. If "q" is a write queue, backq ()
* returns a pointer to the next queue upstream from "q", unless it is
* the stream head.
*
*-RETURN VALUE:
* If successful, backq () returns a pointer to the queue preceding the
* current queue. Otherwise, it returns NULL.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* If the case of a STREAMS-based fifo, this function takes no special
* action at the midpoint of the stream where the read and write sides
* interchange.
*/
#if __USE_PROTO__
queue_t * (backq) (queue_t * q)
#else
queue_t *
backq __ARGS ((q))
queue_t * q;
#endif
{
q = OTHERQ (q);
q = QUEUE_NEXT (q);
if (q == NULL)
return NULL;
return OTHERQ (q);
}
#endif
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* bufcall Call a function when a buffer becomes available.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* toid_t bufcall (uint_t size, int pri, void (* func) (), long arg);
*
*-ARGUMENTS:
* size Number of bytes in the buffer to be allocated (from
* the failed allocb () request).
*
* pri Priority of the allocb () allocation request (BPRI_LO,
* BPRI_MED, or BPRI_HI).
*
* func Fuction or driver routine to be called when a buffer
* becomes available.
*
* arg Argument to the function to be called when a buffer
* becomes available.
*
*-DESCRIPTION:
* bufcall () serves as a timeout call of indeterminate length. When a
* buffer allocation request fails, bufcall () can be used to schedule
* the routine "func" to be called with the argument "arg" when a buffer
* of at least "size" bytes becomes available.
*
* When "func" runs, all interrupts from STREAMS devices will be blocked
* on the processor on which it is running. "func" will have no user
* context and may not call any function that sleeps.
*
*-RETURN VALUE:
* If successful, bufcall () returns a non-zero value that identifies the
* scheduling request. This non-zero identifier may be passed to
* unbufcall () to cancel the request. If any failure occurs, bufcall ()
* returns 0.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* Even when "func" is called, allocb () can still fail if another
* module or driver had allocated the memory before "func" was able to
* call allocb ().
*
*-SEE ALSO:
* allocb (), esballoc (), esbbcall (), itimeout (), unbufcall ().
*/
#if __USE_PROTO__
toid_t (bufcall) (uint_t size, int pri, ...)
#else
toid_t
bufcall __ARGS ((size, pri))
uint_t size;
int pri;
#endif
{
se_funcptr_t func;
long arg;
va_list arglist;
sevent_t * seventp;
/*
* Before we do anything, we fetch the two other arguments. We use
* the variable-argument function convention to get around problems
* with implicit conversions introduced by having a prototype for
* this function visible.
*
* This is a generic problem with callbacks, since the shape of a
* function type includes the shapes of the function arguments, and
* because we want to pass some argument to the user-supplied function
* without knowing its type (and hence, we must not cause any implicit
* conversions on its value).
*/
va_start (arglist, pri);
func = va_arg (arglist, se_funcptr_t);
arg = va_arg (arglist, long);
va_end (arglist);
/*
* It may be that NULL is defined in such a way that it is not
* meaningful to compare it with a function pointer, so we just cast
* a zero to get the right kind of effect.
*
* We add some extra assertions below to check that "long" is of
* sufficient size to contain all likely parameter data items. For
* C, "likely" means the fundamental types, which has to include
* pointers to functions, pointers to void, and also pointers to
* structures since under C++ there is currently no guarantee to match
* the ISO C requirement that a pointer of type "void *" be able to
* hold the value of any pointer type without loss of information.
* [We don't consider member pointers here; they are pathological]
*/
ASSERT (sizeof (se_funcptr_t) <= sizeof (long) &&
sizeof (_VOID *) <= sizeof (long) &&
sizeof (selist_t *) <= sizeof (long));
ASSERT (func != (se_funcptr_t) 0);
ASSERT (pri == BPRI_LO || pri == BPRI_HI || pri == BPRI_LO);
/*
* Here we *could* lock and test the available space in the STREAMS
* message heap to see whether sufficient space to satisfy the
* request has become available between the failed allocb () and
* the bufcall () request.
*
* The question is whether the extra time that the heap is locked is
* worth overcoming a delay for a freemsg () to trigger the bufcall ()
* later, especially given the fact that we'll have to unlock the
* heap to call the user's callback (introducing the opportunity for
* the newly-discovered space to vanish).
*
* All in all, once things get to the bufcall () stage there seems
* little reason to keep pushing the system, so we don't bother.
*/
/*
* First off, let's get ourselves an event cell. This is mutually
* dependent with the ID generation policy; if ID codes are stored in
* the event cells, we are free to allocate and discard from a general
* memory pool; if ID codes are not stored, then they must reflect
* some persistent attribute of the cell, which usually implies that
* cells come from some nonshrinking managed pool.
*
* Of course, a generative ID code scheme requires extra locking in
* the absence of atomic FETCH_AND_INCREMENT () operations. In this
* implementation, we get around this by using the list-head locks
* to guard the counter operations. [This has interesting implications
* for the design of a generic list-manipulation facility.]
*/
#if _TOID_MEMBER
if ((seventp = (sevent_t *) kmem_alloc (sizeof (sevent_t),
KM_NOSLEEP)) == NULL)
return 0;
#else
#error Need an allocator for event cells that matches the ID policy
#endif
/*
* Fill in the event cell. Note that the size member includes the
* memory space required for the message block and data block!
*/
seventp->se_arg = arg;
seventp->se_func = func;
seventp->se_size = MSGB_SIZE (size);
return STORE_EVENT (seventp, pri);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* canput Test for room in a message queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int canput (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the message queue.
*
*-DESCRIPTION:
* canput () tests if there is room for a message in the queue pointed to
* by "q". The queue _must_ have a service procedure.
*
* It is possivle because of race conditions to test for room using
* canput () and get an indication that there is room for a message, and
* then have the queue fill up before subsequently enqueuing the message,
* causing a violation of flow control. This is not a problem, since the
* violation of flow control in this case is bounded.
*
*-RETURN VALUE:
* canput () returns 1 if a message can be placed on the queue. 0 is
* returned if a message cannot be enqueued because of flow control.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The driver writer is responsible for both testing a queue with
* canput () and refraining from placing a message if canput () fails.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument may not reference "q_next" (for example, an argument
* of "q->q_next" is erroneous on a multiprocessor and is disallowed by
* the DDI/DKI). "canputnext (q)" is provided as a multiprocessor-safe
* equivalent to the common call "canput (q->q_next)", which is no
* longer allowed [see canputnext ()].
*
*-SEE ALSO:
* bcanputnext (), bcanput (), canputnext (), putbq (), putnext ()
*/
#if __USE_PROTO__
int (canput) (queue_t * q)
#else
int
canput __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
int result = 1;
prev_pl = QFREEZE_TRACE (q, "canput");
if ((q->q_flag & QFULL) != 0) {
/*
* We must set the following flag to indicate that the
* queue has a writer who has made a failed write
* attempt.
*/
q->q_flag |= QWANTW;
result = 0;
}
QUNFREEZE_TRACE (q, prev_pl);
return result;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* canputnext Test for flow control in a stream.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int bcanputnext (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the message queue.
*
*-DESCRIPTION:
* canputnext () searches through the stream (starting at "q->q_next")
* until it finds a queue containing a service routine, or until it
* reaches the end of the stream. If found, the queue containing the
* service routine is tested to see if there is room for a message in the
* queue. If the band is full, canputnext () marks the queue to
* automatically back-enable the caller's service routine when the amount
* of data in messages on the queue has reached its low water mark.
*
* It is possible because of race conditions to test for room using
* canputnext () and get an indication that there is room for a message,
* and then have the queue fill up before subsequently enqueuing the
* message, causing a violation of flow control. This is not a problem,
* since the violation of flow control in this case is bounded.
*
*-RETURN VALUE:
* canputnext () returns 1 if a message can be sent in the stream, or 0
* if the stream is flow-controlled. If canputnext () reaches the end of
* the stream without finding a queue with a service procedure, then it
* returns 1.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The driver writer is responsible for both testing a queue with
* bcanputnext () and refraining from placing a message if bcanputnext ()
* fails.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument may not reference "q_next" (for example, an argument
* of "q->q_next" is erroneous on a multiprocessor and is disallowed by
* the DDI/DKI). "bcanputnext (q)" is provided as a multiprocessor-safe
* equivalent to the common call "bcanput (q->q_next)", which is no
* longer allowed [see bcanputnext ()].
*
*-SEE ALSO:
* bcanput (), bcanputnext (), canput (), putbq (), putnext ()
*/
#if __USE_PROTO__
int (canputnext) (queue_t * q)
#else
int
canputnext __ARGS ((q))
queue_t * q;
#endif
{
QUEUE_TRACE (q, "canputnext");
/*
* Rather than depend on canput () above, we'll use the band-capable
* function QBAND_SRVNEXT () and depend on its ability to handle
* band 0. The mechanics of iterating through a stream are much more
* complex now than in the old SVR3.2 days due to both freezestr ()
* and qprocsoff ().
*/
return QBAND_SRVNEXT (q, 0);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* copyb Copy a message block.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * copyb (mblk_t * bp);
*
*-ARGUMENTS:
* bp Pointer to the message block from which data are
* copied.
*
*-DESCRIPTION:
* copyb () allocates a new message block, and copies into it the data
* from the block pointed to by "bp". The new block will be at least as
* large as the block being copied. The "b_rptr" and "b_wptr" members of
* the message block pointed to by "bp" are used to determine how many
* bytes to copy.
*
*-RETURN VALUE:
* If successful, copyb () returns a pointer to the newly allocated
* message block containing the copied data. Otherwise, it returns a NULL
* pointer.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), copymsg (), msgb
*/
#define ALLOW_EMPTY_COPIES
#if __USE_PROTO__
mblk_t * (copyb) (mblk_t * bp)
#else
mblk_t *
copyb __ARGS ((bp))
mblk_t * bp;
#endif
{
mblk_t * newmsg;
size_t size;
ASSERT (bp != NULL && bp->b_datap != NULL);
/*
* What to do if there is no real data in the original message?
*
* Copy it anyway, I guess.
*/
size = bp->b_wptr - bp->b_rptr;
if (size == 0)
return MSGB_ALLOC (0, BPRI_LO, KM_NOSLEEP);
/*
* In this implementation we only allocate exactly as much space as
* we require (thus opening the zero-length issue). It is possible to
* interpret the wording of the manual-page as implying that the size
* of the new block is at least the size of the old block including
* unused space, but that doesn't seem reasonable. I read that
* requirement as merely pointing out that the allocator can give
* more memory to the new block that is strictly necessary.
*/
if ((newmsg = MSGB_ALLOC (size, BPRI_LO, KM_NOSLEEP)) != NULL) {
newmsg->b_datap->db_type = bp->b_datap->db_type;
memcpy (newmsg->b_rptr, bp->b_rptr, size);
newmsg->b_wptr += size;
}
return newmsg;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* copymsg Copy a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * copymsg (mblk_t * mp);
*
*-ARGUMENTS:
* mp Pointer to the message to be copied.
*
*-DESCRIPTION:
* copymsg () forms a new message by allocating message blocks, copies
* the conents of the message referred to by "mp" (using the copyb ()
* function) and returns a pointer to the new message.
*
*-RETURN VALUE:
* If successful, copymsg () returns a pointer to the new message.
* Otherwise, it returns a NULL pointer.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), copyb (), msgb
*/
#if __USE_PROTO__
mblk_t * (copymsg) (mblk_t * mp)
#else
mblk_t *
copymsg __ARGS ((mp))
mblk_t * mp;
#endif
{
mblk_t * oldscan;
mblk_t * msg;
mblk_t * newblk;
ASSERT (mp != NULL && mp->b_datap != NULL);
/*
* Rather than simply layering on top of copyb (), we can make this
* function be more friendly in situations where memory is scarce by
* trying the allocations before the copies, so that we don't spend
* time copying data that is going to be discarded.
*/
if ((newblk = msg = MSGB_ALLOC (mp->b_wptr - mp->b_rptr, BPRI_LO,
KM_NOSLEEP)) == NULL)
return NULL;
for (oldscan = mp->b_cont ; oldscan != NULL ;
newblk = newblk->b_cont, oldscan = oldscan->b_cont) {
if ((newblk->b_cont =
MSGB_ALLOC (oldscan->b_wptr - oldscan->b_rptr,
BPRI_LO, KM_NOSLEEP)) == NULL) {
freemsg (msg);
return NULL;
}
}
/*
* Now we have the memory, do the copy. Like copyb (), we have have to
* remember to dance around zero-length blocks in the original, which
* we will be preserve as copied zero-length blocks.
*/
oldscan = mp;
newblk = msg;
do {
size_t size;
ASSERT (oldscan != NULL);
if ((size = oldscan->b_wptr - oldscan->b_rptr) > 0)
memcpy (newblk->b_rptr, oldscan->b_rptr, size);
newblk->b_wptr += size;
oldscan = oldscan->b_cont;
} while ((newblk = newblk->b_cont) != NULL);
ASSERT (oldscan == NULL);
#if 0
if ((msg = newblk = copyb (mp)) != NULL)
while ((mp = mp->b_cont) != NULL)
if ((newblk = (newblk->b_cont = copyb (mp))) == NULL) {
/*
* If a given block in the message cannot be
* copied, undo all the work done so far.
*/
freemsg (msg);
return NULL;
}
#endif
return msg;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* datamsg Test whether a message is a data message.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
* #include <sys/ddi.h>
*
* int datamsg (uchar_t type);
*
*-ARGUMENTS:
* type The type of message to be tested. The "db_type" field
* of the "datab" structure contains the message type.
* This field may be accessed through the message block
* using "mp->b_datap->db_type".
*
*-DESCRIPTION:
* The datamsg () function tests the type of message to determine if it
* is a data message type (M_DATA, M_DELAY, M_PROTO or M_PCPROTO).
*
*-RETURN VALUE:
* datamsg () returns 1 if the message is a data message and 0 if the
* message is any other type.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), datab, msgb, messages
*/
#if __USE_PROTO__
int (datamsg) (uchar_t type)
#else
int
datamsg __ARGS ((type))
uchar_t type;
#endif
{
return datamsg (type); /* appeal to the macro version */
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* dupb Duplicate a message block.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * dupb (mblk_t * bp);
*
*-ARGUMENTS:
* bp Pointer to the message block to be duplicated.
*
*-DESCRIPTION:
* dupb () creates a new message block structure to reference the message
* block pointed to by "bp". Unlike copyb (), dupb () does not copy the
* information in the data block, but creates a new structure to point to
* it.
*
*-RETURN VALUE:
* If successful, dupb () returns a pointer to the new message block.
* Otherwise, it returns a NULL pointer.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* copyb (), dupmsg (), datab, msgb
*/
#if __USE_PROTO__
mblk_t * (dupb) (mblk_t * bp)
#else
mblk_t *
dupb __ARGS ((bp))
mblk_t * bp;
#endif
{
mblk_t * newblk;
ASSERT (bp != NULL);
if ((newblk = STRMEM_ALLOC (sizeof (mblk_t), BPRI_HI,
KM_NOSLEEP)) != NULL) {
/*
* Note that we do not initialize the "b_prev" and "b_next"
* members, and that the "b_cont" member is NULL rather than
* a copy of the original.
*/
newblk->b_cont = NULL;
newblk->b_datap = bp->b_datap;
newblk->b_rptr = bp->b_rptr;
newblk->b_wptr = bp->b_wptr;
++ newblk->b_datap->db_ref;
}
return newblk;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* dupmsg Duplicate a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * dupmsg (mblk_t * mp);
*
*-ARGUMENTS:
* mp Pointer to the message to be duplicated.
*
*-DESCRIPTION:
* dupmsg () forms a new message by duplicating the message blocks in the
* message pointed to by "mp" and linking them via their "b_cont"
* pointers.
*
*-RETURN VALUE:
* If successful, dupmsg () returns a pointer to the new message.
* Otherwise it returns a NULL pointer.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* copyb (), copymsg (), dupb (), datab, msgb
*/
#if __USE_PROTO__
mblk_t * (dupmsg) (mblk_t * mp)
#else
mblk_t *
dupmsg __ARGS ((mp))
mblk_t * mp;
#endif
{
mblk_t * msg;
mblk_t * newblk;
ASSERT (mp != NULL);
if ((msg = newblk = dupb (mp)) != NULL)
while ((mp = mp->b_cont) != NULL)
if ((newblk = (newblk->b_cont = dupb (mp))) == NULL) {
/*
* Discard the work we have done so far.
*/
freemsg (msg);
return NULL;
}
return msg;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* enableok Enable a queue to be serviced.
*
*-SYNOPSIS:
* #include <sys/stream.h>
* #include <sys/ddi.h>
*
* void enableok (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
*-DESCRIPTION:
* The enableok () function allows the service routine of the queue
* pointed to by "q" to be rescheduled for service. It cancels the effect
* of a previous use of the noenable () function on "q".
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* noenable (), qenable (), queue
*/
#if __USE_PROTO__
void (enableok) (queue_t * q)
#else
void
enableok __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "enableok");
q->q_flag &= ~ QNOENB;
QUNFREEZE_TRACE (q, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* esballoc Allocate a message block using an externally supplied
* buffer.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * esballoc (uchar_t * base, int size, int pri,
* frtn_t * fr_rtnp);
*
*-ARGUMENTS:
* base Address of driver-supplied data buffer.
*
* size Number of bytes in data buffer.
*
* pri Priority of allocation request (used to allocate the
* message and data blocks). Valid values are BPRI_LO,
* BPRI_MED, and BPRI_HI.
*
* fr_rtnp Pointer to the free-routine data structure.
*
*-DESCRIPTION:
* esballoc () creates a STREAMS message and attaches a driver-supplied
* data buffer in place of a STREAMS data buffer. It allocates a message
* and data block header only. The driver-supplied data buffer, pointed
* to by "base", is used as the data buffer for the message.
*
* When freeb () is called to free the message, on the last reference to
* the message the driver's free-routine specified by the "free_func"
* field in the "free_rtn" structure is called with one argument
* (specified by the "free_arg" field) to free the data buffer.
*
* Instead of requiring a specific number of arguments, the "free_arg"
* field is defined as type "char *". This way, the driver can pass a
* pointer to a structure if more than one argument is needed.
*
* When the "free_func" function runs, interrupts from all STREAMS
* devices will be blocked. It has no user context and may not call any
* routine that sleeps. The function may not access any dynamically
* allocated data structures that might no longer exist when it runs.
*
*-RETURN VALUE:
* On success, a pointer to the newly allocated message block is
* returned. On failure, a NULL pointer is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), freeb (), free_rtn
*/
#if __USE_PROTO__
mblk_t * (esballoc) (uchar_t * base, int size, int pri, frtn_t * fr_rtnp)
#else
mblk_t *
esballoc __ARGS ((base, size, pri, fr_rtnp))
uchar_t * base;
int size;
int pri;
frtn_t * fr_rtnp;
#endif
{
mblk_t * mblkp;
ASSERT (base != NULL);
ASSERT (size >= 0);
ASSERT (pri == BPRI_LO || pri == BPRI_HI || pri == BPRI_LO);
ASSERT (ATOMIC_FETCH_UCHAR (str_mem->sm_init) &&
str_mem->sm_msg_lock != NULL);
/*
* Is it permissible for a driver to specify a NULL fr_rtnp?
*
* I think that it seems entirely logical, but neither the DDI/DKI
* nor the STREAMS Programmer's Guide say anything about this.
*/
if ((mblkp = STRMEM_ALLOC (MSGB_SIZE (0), pri, KM_NOSLEEP)) != NULL) {
/*
* Note that we don't bother setting up the "b_prev" and
* "b_next" members of the message block.
*/
mblkp->b_cont = NULL;
mblkp->b_datap = MB_TO_DB (mblkp);
mblkp->b_rptr = base;
mblkp->b_wptr = base;
/*
* We specially flag this message block as part of
* a triple. While there are other ways we could test
* this, for now we permit the possibility that a
* client might change the attachments of data blocks
* and message blocks perversely.
*/
mblkp->b_flag = MSGTRIPLE;
mblkp->b_band = 0;
mblkp->b_datap->db_base = base;
mblkp->b_datap->db_lim = base + size;
mblkp->b_datap->db_type = M_DATA;
mblkp->b_datap->db_ref = 1;
mblkp->b_datap->db_frtnp = fr_rtnp;
}
return mblkp;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* esbbcall Call a function when an externally-supplied buffer
* can be allocated.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* toid_t esbbcall (int pri, int (* func) (), long arg);
*
*-ARGUMENTS:
* pri Priority of the esballoc () allocation request
* (BPRI_LO, BPRI_MED or BPRI_HI).
*
* func Function to be called when a buffer becomes available.
*
* arg Argument to the function to be called when a buffer
* becomes available.
*
*-DESCRIPTION:
* esbbcall (), like bufcall (), serves as a timeout call of
* indeterminate duration. If esballoc () is unable to allocate a message
* block header and a data block header to go with the externally
* supplied data buffer, esbbcall () can be used to schedule the routine
* "func", to be called with the argument "arg" when memory becomes
* available.
*
* When "func" runs, all interrupts from STREAMS devices will be blocked
* on the processor on which it is running. "func" will have no user
* context and may not call any function that sleeps.
*
*-RETURN VALUE:
* If successful, esbbcall () returns a non-zero value that identifies
* the scheduling request. This non-zero identifier may be passed to
* unbufcall () to cancel the request. If any failure occurs, esbbcall ()
* returns 0.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* Even when "func" is called, esballoc () can still fail if another
* module or driver had allocated the memory before "func" was able to
* call esballoc ().
*
*-SEE ALSO:
* allocb (), bufcall (), esballoc (), itimeout (), unbufcall ()
*/
#if __USE_PROTO__
toid_t (esbbcall) (int pri, ...)
#else
toid_t
esbbcall __ARGS ((pri))
int pri;
#endif
{
se_funcptr_t func;
long arg;
va_list arglist;
sevent_t * seventp;
/*
* Before we do anything, we fetch the two other arguments. We use
* the variable-argument function convention to get around problems
* with implicit conversions introduced by having a prototype for
* this function visible.
*
* This is a generic problem with callbacks, since the shape of a
* function type includes the shapes of the function arguments, and
* because we want to pass some argument to the user-supplied function
* without knowing its type (and hence, we must not cause any implicit
* conversions on its value).
*/
va_start (arglist, pri);
func = va_arg (arglist, se_funcptr_t);
arg = va_arg (arglist, long);
va_end (arglist);
/*
* It may be that NULL is defined in such a way that it is not
* meaningful to compare it with a function pointer, so we just cast
* a zero to get the right kind of effect.
*
* We add some extra assertions below to check that "long" is of
* sufficient size to contain all likely parameter data items. For
* C, "likely" means the fundamental types, which has to include
* pointers to functions, pointers to void, and also pointers to
* structures since under C++ there is currently no guarantee to match
* the ISO C requirement that a pointer of type "void *" be able to
* hold the value of any pointer type without loss of information.
* [We don't consider member pointers here; they are pathological]
*/
ASSERT (sizeof (se_funcptr_t) <= sizeof (long) &&
sizeof (_VOID *) <= sizeof (long) &&
sizeof (selist_t *) <= sizeof (long));
ASSERT (func != (se_funcptr_t) 0);
ASSERT (pri == BPRI_LO || pri == BPRI_HI || pri == BPRI_LO);
/*
* Here we *could* lock and test the available space in the STREAMS
* message heap to see whether sufficient space to satisfy the
* request has become available between the failed allocb () and
* the bufcall () request.
*
* The question is whether the extra time that the heap is locked is
* worth overcoming a delay for a freemsg () to trigger the bufcall ()
* later, especially given the fact that we'll have to unlock the
* heap to call the user's callback (introducing the opportunity for
* the newly-discovered space to vanish).
*
* All in all, once things get to the bufcall () stage there seems
* little reason to keep pushing the system, so we don't bother.
*/
/*
* First off, let's get ourselves an event cell. This is mutually
* dependent with the ID generation policy; if ID codes are stored in
* the event cells, we are free to allocate and discard from a general
* memory pool; if ID codes are not stored, then they must reflect
* some persistent attribute of the cell, which usually implies that
* cells come from some nonshrinking managed pool.
*
* Of course, a generative ID code scheme requires extra locking in
* the absence of atomic FETCH_AND_INCREMENT () operations. In this
* implementation, we get around this by using the list-head locks
* to guard the counter operations. [This has interesting implications
* for the design of a generic list-manipulation facility.]
*/
#if _TOID_MEMBER
if ((seventp = (sevent_t *) kmem_alloc (sizeof (sevent_t),
KM_NOSLEEP)) == NULL)
return 0;
#else
#error Need an allocator for event cells that matches the ID policy
#endif
seventp->se_arg = arg;
seventp->se_func = func;
seventp->se_size = MSGB_SIZE (0);
return STORE_EVENT (seventp, pri);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* flushband Flush messages in a specified priority band.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* void flushband (queue_t * q, uchar_t pri, int flag);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* pri Priority band of messages to be flushed.
*
* flag Determines messages to flush. Valid "flag" values are:
*
* FLUSHDATA Flush only data messages (types
* M_DATA, M_DELAY, M_PROTO, and
* M_PCPROTO).
*
* FLUSHALL Flush all messages.
*
*-DESCRIPTION:
* The flushband () function flushes messages associated with the
* priority band specified by "pri". If "pri" is 0, only normal and high
* priority messages are flushed. Otherwise, messages are flushed from
* the band "pri" according to the value of "flag".
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* put (), flushq (), queue
*/
#if __USE_PROTO__
void (flushband) (queue_t * q, uchar_t pri, int flag)
#else
void
flushband __ARGS ((q, pri, flag))
queue_t * q;
uchar_t pri;
int flag;
#endif
{
pl_t prev_pl;
qband_t * qbandp;
mblk_t * mp;
mblk_t * next;
ulong_t flushsize;
ASSERT (flag == FLUSHDATA || flag == FLUSHALL);
if (pri == 0) {
/*
* Since the priority-band flush algorithm is necessarily
* different from the regular one (due to flow control
* handling), we'll just forward this on to the regular
* version.
*/
flushq (q, flag);
return;
}
prev_pl = QFREEZE_TRACE (q, "flushband");
if ((qbandp = QUEUE_BAND (q, pri)) != NULL &&
(mp = qbandp->qb_first) != NULL) {
flushsize = 0;
do {
/*
* Read the "b_next" member now in case we unlink this message
* from the queue.
*/
next = mp->b_next;
ASSERT (mp->b_band == pri);
if (flag == FLUSHALL || datamsg (mp->b_datap->db_type)) {
if (mp->b_prev == NULL)
q->q_first = next;
else
mp->b_prev->b_next = next;
if (next == NULL)
q->q_last = mp->b_prev;
else
next->b_prev = mp->b_prev;
QBAND_DEQUEUE (qbandp, mp);
/*
* Free the message, accumulating the total
* size of the data referred to by the
* message.
*/
flushsize += MSG_SIZE (mp);
freemsg (mp);
}
} while ((mp = next) != qbandp->qb_last);
/*
* Here we update the flow control parameters for the band
* now that we have accumulated all the necessary changes.
*/
QBAND_REDUCE (q, qbandp, flushsize);
}
/*
* Since we are in a path with QBAND_REDUCE (), check for QBACK before
* unfreezing the stream.
*/
{
unsigned long back;
if ((back = q->q_flag & QBACK) != 0)
q->q_flag &= ~ QBACK;
QUNFREEZE_TRACE (q, prev_pl);
if (back)
QUEUE_BACKENAB (q);
}
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* flushq Flush messages on a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void flushq (queue_t * q, int flag);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* flag Determines messages to flush. Valid "flag" values are:
*
* FLUSHDATA Flush only data messages (types
* M_DATA, M_DELAY, M_PROTO, and
* M_PCPROTO).
*
* FLUSHALL Flush all messages.
*
*-DESCRIPTION:
* flushq () frees messages on a queue by calling freemsg () for each
* message. If the queue's count falls below the low water mark and
* someone wants to write to the queue, the nearest upstream or
* downstream (as approriate) service procedure is enabled.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* put (), flushband (), freemsg (), putq (), queue
*/
#if __USE_PROTO__
void (flushq) (queue_t * q, int flag)
#else
void
flushq __ARGS ((q, flag))
queue_t * q;
int flag;
#endif
{
pl_t prev_pl;
mblk_t * mp;
mblk_t * next;
ulong_t flushsize;
ASSERT (flag == FLUSHDATA || flag == FLUSHALL);
flushsize = 0;
prev_pl = QFREEZE_TRACE (q, "flushq");
/*
* High-priority messages are flushed as well as low-priority messages
* so we could presumably split this function into a two-part scan
* since the high-priority messages are at the front of the message
* list while the low-priority messages are at the end.
*
* However, this function is not called very often, and we expect
* band usage to be rare.
*/
for (mp = q->q_first ; mp != NULL ; mp = next) {
/*
* Read the "b_next" member now in case we unlink this message
* from the queue.
*/
next = mp->b_next;
if (mp->b_band == 0 &&
(flag == FLUSHALL || datamsg (mp->b_datap->db_type))) {
if (mp->b_prev == NULL)
q->q_first = next;
else
mp->b_prev->b_next = next;
if (next == NULL)
q->q_last = mp->b_prev;
else
next->b_prev = mp->b_prev;
/*
* If the message is high-priority, then we skip the
* accumulation of the size.
*/
if (IS_PRI_MSG (mp)) {
freemsg (mp);
continue;
}
/*
* Free the message, accumulating the total size of
* the data referred to by the message.
*/
flushsize += MSG_SIZE (mp);
freemsg (mp);
}
}
QUEUE_REDUCE (q, flushsize);
/*
* Since we are in a path with QUEUE_REDUCE (), check for QBACK before
* unfreezing the stream.
*/
{
unsigned long back;
if ((back = q->q_flag & QBACK) != 0)
q->q_flag &= ~ QBACK;
QUNFREEZE_TRACE (q, prev_pl);
if (back)
QUEUE_BACKENAB (q);
}
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* freeb Free a message block.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void freeb (mblk_t * bp);
*
*-ARGUMENTS:
* bp Pointer to the message block to be deallocated.
*
*-DESCRIPTION:
* freeb () deallocates a message block. If the reference count of the
* "db_ref" member of the "datab" structure is greater than 1, freeb ()
* decrements the count and returns. Otherwise, if "db_ref" equals 1, it
* deallocates the message block and the corresponding data block and the
* corresponding data block and buffer.
*
* If the data buffer to be freed was allocated with esballoc (), the
* driver is notified that the attached data buffer needs to be freed by
* calling the free-routine [see free_rtn] associated with the data
* buffer. Once this is accomplished, freeb () releases the STREAMS
* resources associated with the buffer.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), dupb (), esballoc (), datab, free_rtn, msgb
*/
#if __USE_PROTO__
void (freeb) (mblk_t * bp)
#else
void
freeb __ARGS ((bp))
mblk_t * bp;
#endif
{
size_t size;
pl_t prev_pl;
dblk_t * datap;
frtn_t * frtnp = NULL;
ASSERT (bp != NULL && bp->b_datap != NULL);
ASSERT (bp->b_datap->db_ref > 0);
datap = bp->b_datap;
/*
* It is expected that almost all of the calls to freeb () will result
* in some memory being returned to the free pool, so to save space
* and ensure correctness we lock the heap now.
*
* This has the advantage of serializing some of the tests below for
* dealing with which parts of a message triple are free. Since the
* message and data parts of a triple could be being freed
* simultaneously by different contexts, this will guarantee that the
* data will be freed by exactly one of the contexts.
*
* Another note about memory management: if the message and other
* heaps are not kept separate, then some extra work needs to be done
* to wake up processes sleeping for memory and for calling the
* scheduled buffer events. Essentially, the systems need to map to
* some common code that correctly prioritizes access and runs at an
* appropriate time (possibly when some processor is about to return
* to user mode from the kernel, or is totally idle).
*/
prev_pl = LOCK (str_mem->sm_msg_lock, str_msg_pl);
-- datap->db_ref;
if (datap->db_ref > 0) {
/*
* Ok, we don't need to free the data block and buffer, but
* we may still need to free the message block. This gets a
* little convoluted since we might be deallocating only part
* of a structure that was actually allocated as a whole by
* allocb () or esballoc ().
*/
if ((bp->b_flag & MSGTRIPLE) == 0) {
STRMEM_FREE (bp, sizeof (mblk_t));
goto free_done;
}
if ((datap = MB_TO_DB (bp))->db_ref > 0) {
/*
* The data block part of the triple is not free,
* so we set a flag (tested when the data block is
* freed).
*/
bp->b_flag |= MSGFREE;
goto free_done;
}
/*
* If the test above was false, it arranged for the message
* block to be associated with its triple partner so that
* we can proceed with the freeing process.
*/
} else if ((bp->b_flag & MSGTRIPLE) == 0) {
/*
* The message block isn't from the same triple as the data
* block. We free the message block and then see whether the
* message block from the triple with the data block is free.
*/
STRMEM_FREE (bp, sizeof (mblk_t));
bp = DB_TO_MB (datap);
if ((bp->b_flag & MSGFREE) == 0) {
/*
* The data block part of the triple is free but the
* message block isn't. The triple will be freed when
* the message block is freed.
*/
goto free_done;
}
}
/*
* Now we deal with freeing the data portion of the message. First off
* we check whether or not the data block holds a user-supplied buffer
* or not.
*/
if (datap->db_base != DB_TO_DATA (datap)) {
/*
* Calling the user-supplied free function now would be a
* really bad idea, since while all the conditions that are
* guaranteed to true while the function is called are true,
* we are still holding the lock on the heap. We record the
* information we need and do the job later.
*/
ASSERT (datap->db_frtnp != NULL);
frtnp = datap->db_frtnp;
size = 0;
} else {
ASSERT (datap->db_frtnp == NULL);
size = datap->db_lim - datap->db_base;
}
/*
* At this point we know we have a message block and a data block to
* free, plus "size" bytes of data buffer.
*/
STRMEM_FREE (bp, MSGB_SIZE (size));
free_done:
UNLOCK (str_mem->sm_msg_lock, plstr);
/*
* Here we call any deferred user data buffer free function
*/
if (frtnp != NULL)
(* frtnp->free_func) (frtnp->free_arg);
(void) splx (prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* freemsg Free a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void freemsg (mblk_t * mp);
*
*-ARGUMENTS:
* mp Pointer to the message to be freed.
*
*-DESCRIPTION:
* freemsg () frees all message blocks, data blocks, and data buffers
* associated with the message pointed to by "mp". freemsg () walks down
* the "b_cont" list [see msgb], calling freeb () for every message block
* in the message.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* freeb (), msgb
*/
#if __USE_PROTO__
void (freemsg) (mblk_t * mp)
#else
void
freemsg __ARGS ((mp))
mblk_t * mp;
#endif
{
mblk_t * next;
ASSERT (mp != NULL);
do {
next = mp->b_cont;
freeb (mp);
} while ((mp = next) != NULL);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* freezestr Freeze the state of a stream.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* pl_t freezestr (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to a message queue.
*
*-DESCRIPTION:
* freezestr () sets the interrupt priority to "plstr" (if the current
* level is lower than "plstr" and the implementation requires that
* interrupts be blocked while the stream is frozen) and freezes the
* state of the stream containing the queue specified by "q". Freezing
* the stream prevents any further entries into open, close, put or
* service procedures on the stream and prevents any messages from being
* taken on or taken off any queues in the stream (except by the caller
* of freezestr ()). Freezing the stream does not automatically stop all
* functions that are running within the stream; functions will continue
* to run until they attempt to perform some operation which changes the
* state of the stream, at while point they will be forced to wait for
* the stream to be unfrozen by a call to unfreezestr ().
*
* Drivers and modules must freeze the stream while they manipulate its
* queues directly. This includes searching the queues and for the
* duration of any calls to insq (), rmvq (), strqset (), and strqget ().
*
*-RETURN VALUE:
* freezestr () returns the previous interrupt priority level which is
* typically used in a subsequent call to unfreezestr ().
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Calling freezestr () to freeze a stream that is already frozen by the
* caller will result in deadlock.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* freezestr () should be used sparingly as it is rarely necessary to
* freeze a stream (most modules do not need to manipulate their queues
* directly) and freezing a stream can have a significant negative impact
* on performance.
*
*-SEE ALSO:
* unfreezestr ()
*/
#if __USE_PROTO__
pl_t (freezestr) (queue_t * q)
#else
pl_t
freezestr __ARGS ((q))
queue_t * q;
#endif
{
return QFREEZE_TRACE (q, "freezestr");
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* getq Get the next message from a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * getq (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue from which the message is to be
* retrieved.
*
*-DESCRIPTION:
* getq () is used by service routines to retrieve queue messages. It
* gets the next available message from the top of the queue pointed to
* by "q". getq () handles flow control, restarting I/O that was blocked
* as needed.
*
*-RETURN VALUE:
* If there is a message to retrieve, getq () returns a pointer to it. If
* no message is queued, getq () returns a NULL pointer.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* bcanput (), canput (), putbq (), putq (), qenable (), rmvq ()
*/
#if __USE_PROTO__
mblk_t * (getq) (queue_t * q)
#else
mblk_t *
getq __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
mblk_t * mp;
prev_pl = QFREEZE_TRACE (q, "getq");
if ((mp = q->q_first) != NULL) {
ulong_t msgsize;
/*
* There is a message. Dequeue it and adjust the flow-control
* parameters appropriately depending on whether the message
* is a priority message and also on the band of the message.
*/
if ((q->q_first = mp->b_next) == NULL) {
q->q_last = NULL;
QUEUE_DRAINED (q);
} else
mp->b_next->b_prev = NULL;
/*
* Record the message band for the putq () enabling mechanism.
*/
q->q_lastband = mp->b_band;
/*
* If the message is a priority-band message, we have to
* finish up the dequeueing operation by adjusting the
* "qb_first" and "qb_last" members of the "qband" structure.
*
* We do this below with the band flow-control management.
*/
ASSERT (mp->b_datap != NULL);
if (! IS_PRI_MSG (mp)) {
msgsize = MSG_SIZE (mp);
if (mp->b_band > 0) {
qband_t * qbandp = QUEUE_BAND (q, mp->b_band);
ASSERT (qbandp->qb_first == mp);
QBAND_DEQUEUE (qbandp, mp);
QBAND_REDUCE (q, qbandp, msgsize);
} else
QUEUE_REDUCE (q, msgsize);
}
} else {
/*
* The queue is empty; we set a flag to indicate to putq ()
* that it should enable the queue when a message is put.
*/
q->q_flag |= QWANTR;
}
/*
* Since we are in a path with QUEUE_REDUCE (), check for QBACK before
* unfreezing the stream.
*/
{
unsigned long back;
if ((back = q->q_flag & QBACK) != 0)
q->q_flag &= ~ QBACK;
QUNFREEZE_TRACE (q, prev_pl);
if (back)
QUEUE_BACKENAB (q);
}
return mp;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* insq Insert a message into a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int insq (queue_t * q, mblk_t * emp, mblk_t * nmp);
*
*-ARGUMENTS:
* q Pointer to the queue containing message "emp".
*
* emp Pointer to the existing message before which the new
* message is to be inserted.
*
* nmp Pointer to the new message to be inserted.
*
*-DESCRIPTION:
* insq () inserts a message into a queue. The message to be inserted,
* "nmp", is placed in the queue pointer to by "q", immediately before
* the message "emp". If "emp" is NULL, the new message is placed at the
* end of the queue. All flow control parameters are updated. The service
* procedure is scheduled to run unless disabled by a previous call to
* noenable ().
*
* Messages are ordered in the queue based on their priority. If an
* attempt is made to insert a message out of order in the queue, then
* "nmp" is not enqueued.
*
*-RETURN VALUE:
* If "nmp" was successfully enqueued, insq () returns 1. Otherwise,
* insq () returns 0.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller must have the stream frozen [see freezestr ()] when calling
* this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The insertion can fail if there is not enough memory to allocate the
* accounting data structures used with messages whose priority bands are
* greater than zero.
*
* If "emp" is non-NULL, it must point to a message in the queue pointed
* to by "q", or a system panic could result.
*
*-SEE ALSO:
* freezestr (), getq (), putbq (), putq (), rmvq (), unfreezestr ()
*/
#if __USE_PROTO__
int (insq) (queue_t * q, mblk_t * emp, mblk_t * nmp)
#else
int
insq __ARGS ((q, emp, nmp))
queue_t * q;
mblk_t * emp;
mblk_t * nmp;
#endif
{
qband_t * qbandp;
ASSERT (nmp != NULL);
QFROZEN_TRACE (q, "insq");
/*
* The code for high-priority messages has been factored out into a
* completely separate path since the legality tests are incompatible
* with the regular sequence, and in addition because such messages do
* not have their size accumulated in the flow control-parameters at
* all, at all. The execution path here is torturous enough without
* folding all the checks into a sequence with a common exit.
*/
if (IS_PRI_MSG (nmp)) {
/*
* Since putq () is defined as forcing b_band to 0 for high-
* priority messages, we do the same.
*/
nmp->b_band = 0;
if ((nmp->b_next = emp) == NULL) {
if ((nmp->b_prev = q->q_last) == NULL) {
q->q_first = nmp;
} else {
if (! IS_PRI_MSG (nmp->b_prev))
return 0;
nmp->b_prev->b_next = nmp;
}
q->q_last = nmp;
} else {
if ((nmp->b_prev = emp->b_prev) == NULL) {
q->q_first = nmp;
} else {
if (! IS_PRI_MSG (nmp->b_prev))
return 0;
nmp->b_prev->b_next = nmp;
}
emp->b_prev = nmp;
}
/*
* A high-priority message causes a queue to be scheduled,
* even if a noenable () has been done. If we cannot schedule
* the queue for some reason (it is disabled with qprocsoff ()
* or has no service routine) that is not our concern.
*/
(void) QUEUE_TRYSCHED (q);
return 1;
}
qbandp = NULL; /* paranoia */
if (nmp->b_band > 0 &&
(qbandp = QUEUE_BAND (q, nmp->b_band)) == NULL &&
(qbandp = QBAND_ALLOC (q, nmp->b_band)) == NULL) {
/*
* Return failure if we were unable to allocate a necessary
* extra band structure.
*/
return 0;
}
if ((nmp->b_next = emp) == NULL) {
if ((nmp->b_prev = q->q_last) == NULL) {
q->q_first = nmp;
} else {
if (nmp->b_prev->b_band < nmp->b_band)
return 0;
nmp->b_prev->b_next = nmp;
}
q->q_last = nmp;
} else {
/*
* The check against "emp" isn't sufficient, since this
* request could be attempting to insert a message in the
* middle of a sequence of messages with a lower band.
*/
if (emp->b_band > nmp->b_band || IS_PRI_MSG (emp))
return 0;
if ((nmp->b_prev = emp->b_prev) == NULL) {
q->q_first = nmp;
} else {
if (nmp->b_prev->b_band < nmp->b_band)
return 0;
emp->b_prev->b_next = nmp;
}
emp->b_prev = nmp;
}
/*
* Now update the flow control information and priority-band pointers
* after possibly scheduling the queue.
*/
if (QUEUE_CHECK_SCHED (q, nmp, qbandp)) {
/*
* Keep the band pointers updated.
*/
if (qbandp->qb_first == emp)
qbandp->qb_first = nmp;
if (qbandp->qb_last == NULL || (qbandp->qb_last->b_next == emp))
qbandp->qb_last = nmp;
}
return 1; /* success ! */
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* linkb Concatenate two message blocks.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void linkb (mblk_t * mp1, mblk_t * mp2);
*
*-ARGUMENTS:
* mp1 Pointer to the message block to which "mp2" is to be
* added.
*
* mp2 Pointer to the message to be added.
*
*-DESCRIPTION:
* linkb () appends the message "mp2" to the tail of message "mp1". The
* continuation pointer "b_cont" of the last message block in the first
* message is set to point to the second message.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* unlinkb (), msgb
*/
#if __USE_PROTO__
void (linkb) (mblk_t * mp1, mblk_t * mp2)
#else
void
linkb __ARGS ((mp1, mp2))
mblk_t * mp1;
mblk_t * mp2;
#endif
{
ASSERT (mp1 != NULL && mp2 != NULL);
while (mp1->b_cont != NULL)
mp1 = mp1->b_cont;
mp1->b_cont = mp2;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* msgdsize Return number of bytes of data in a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int msgdsize (mblk_t * mp);
*
*-ARGUMENTS:
* mp Pointer to the message to be evaluated.
*
*-DESCRIPTION:
* msgdsize () counts the number of bytes of data in the message pointed
* to by "mp". Only bytes included in message blocks of type "M_DATA" are
* included in the count.
*
*-RETURN VALUE:
* The number of bytes of data in the message.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* msgb
*/
#if __USE_PROTO__
int (msgdsize) (mblk_t * mp)
#else
int
msgdsize __ARGS ((mp))
mblk_t * mp;
#endif
{
int sum = 0;
ASSERT (mp != NULL);
do
if (mp->b_datap->db_type == M_DATA)
sum += mp->b_wptr - mp->b_rptr;
while ((mp = mp->b_cont) != NULL);
return sum;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* msgpullup Concatenate bytes in a message,
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * msgpullup (mblk_t * mp, int len);
*
*-ARGUMENTS:
* mp Pointer to the message whose blocks are to be
* concatenated.
*
* len Number of bytes to concatenate,
*
*-DESCRIPTION:
* msgpullup () concatenates and aligns the first "len" data bytes of the
* message pointed to by "mp", copying the data into a new message. The
* original message is unaltered. If "len" equals -1, all data are
* concatenated. If "len" bytes of the same message type cannot be found,
* msgpullup () fails and returns NULL.
*
*-RETURN VALUE:
* On success, a pointer to the new message is returned; on failure a
* NULL pointer is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), msgb
*/
#if __USE_PROTO__
mblk_t * (msgpullup) (mblk_t * mp, int len)
#else
mblk_t *
msgpullup __ARGS ((mp, len))
mblk_t * mp;
int len;
#endif
{
size_t size;
uchar_t msgtype;
mblk_t * scan;
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
/*
* Something that is now particularly well-specified about this new
* routine (intended to replace pullupmsg () from SVR3.2 STREAMS and
* the SVR4 DDI/DKI) is what happens to the message data in the
* original message *after* the "len" bytes to be pulled up?
*
* Basically, without a usage example (which I haven't been able to
* find in the new SVR4 MP STREAMS programmer's guide) I can't tell
* whether the remaining data in the message should be duplicated for
* the new message or not. In order to make this routine a closer
* functional replacement for pullupmsg (), I'm tempted to say "yes",
* but it could be that the new message is a single standalone message
* block. Hence, I'm putting in a switch to select between the two
* interpretations I consider most likely.
*
* This is particularly important for "msgpullup (mp, 0)", which I'd
* like to make an error.
*/
#define MSGPULLUP_DUPMSG
if (len == 0) {
#ifdef MSGPULLUP_DUPMSG
return dupmsg (mp);
#else
return MSG_ALLOC (0, BPRI_MED, KM_NOSLEEP);
#endif
}
/*
* Begin by calculating the amount of data of the same message type
* that is present in the message.
*/
msgtype = mp->b_datap->db_type;
size = 0;
scan = mp;
do
size += scan->b_wptr - scan->b_rptr;
while ((scan = scan->b_cont) != NULL &&
scan->b_datap->db_type == msgtype);
if (len == -1)
len = size;
else if (len > size)
return NULL;
/*
* Note that it may be reasonable to interpret the definition of this
* function as implying that data can be shared between the old and
* new message blocks if the old data block does not need to be
* altered (ie, if the previous data was aligned and contiguous up to
* the length "len").
*/
/* #define MSGPULLUP_SHARE */
#ifdef MSGPULLUP_SHARE
if ((mp->b_wptr - mp->b_rptr) >= len &&
((ulong_t) mp->b_rptr) & (sizeof (int) - 1)) == 0)
return dupmsg (mp);
#endif /* defined (MSGPULLUP_SHARE) */
if ((scan = MSGB_ALLOC (len, BPRI_MED, KM_NOSLEEP)) == NULL)
return NULL;
/*
* There are some message attributes other than data that need to be
* copied from the source to the new message.
*/
scan->b_band = mp->b_band;
scan->b_flag |= mp->b_flag & ~ MSGMASK_SYSTEM;
/*
* Now transfer the data from the old space to the new space.
*
* More unspecified behaviour deals with how zero-length blocks are
* to be treated. The following loop is coded specifically to skip
* over zero-length message blocks in the source that follow the
* "len" copied bytes of data.
*/
#define MSGPULLUP_DUPMSG
while (mp != NULL) {
size_t blklen = mp->b_wptr - mp->b_rptr;
if (blklen > 0)
memcpy (scan->b_wptr, mp->b_rptr, blklen);
scan->b_wptr += blklen;
len -= blklen;
if (blklen > len) {
/*
* Copy a partial block. Since this must also be the
* last message block in the source message whose
* data are being moved to the new message, here is a
* good place to decide on the dispensation of the
* remaining data.
*/
#ifdef MSGPULLUP_DUPMSG
if ((scan->b_cont = dupmsg (mp)) == NULL) {
/*
* The remaining data could not be duplicated,
* so we have to fail the call overall.
*/
freeb (scan);
return NULL;
}
#endif
break;
}
mp = mp->b_cont;
}
return scan;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* noenable Prevent a queue from being scheduled.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void noenable (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
*-DESCRIPTION:
* The noenable () function prevents the service routine of the queue
* pointed to by "q" from being scheduled for service by insq (),
* putbq (), or putq () when enqueuing a message that is not a high
* priority message. This restriction can be lifted with the enableok ()
* function.
*
* noenable () does not prevent the queue's service routine from being
* scheduled when a high priority message is enqueued, or by an explicit
* call to qenable ().
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* enableok (), insq (), putbq (), putq (), qenable (), queue
*/
#if __USE_PROTO__
void (noenable) (queue_t * q)
#else
void
noenable __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "noenable");
q->q_flag |= QNOENB;
QUNFREEZE_TRACE (q, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* OTHERQ Get pointer to queue's partner queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* queue_t * OTHERQ (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
*-DESCRIPTION:
* The OTHERQ () function returns a pointer to the other of the two
* queue structures that make up an instance of a STREAMS module or
* driver. If "q" points to the read queue the write queue will be
* returned, and vice versa.
*
*-RETURN VALUE:
* OTHERQ () returns a pointer to the queue's partner.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* RD (), WR ()
*/
#if __USE_PROTO__
queue_t * (OTHERQ) (queue_t * q)
#else
queue_t *
OTHERQ __ARGS ((q))
queue_t * q;
#endif
{
return OTHERQ (q);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* pcmsg Test whether a message is a priority control message.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
* #include <sys/ddi.h>
*
* int pcmsg (uchar_t type);
*
*-ARGUMENTS:
* type The type of message to be tested.
*
*-DESCRIPTION:
* The pcmsg () function tests the type of message to determine if it is
* a priority control message (also known as a high priority message).
* The "db_type" field of the "datab" structure contains the message
* type. This field may be accessed through the message block using
* "mp->b_datap->db_type".
*
*-RETURN VALUE:
* pcmsg () returns 1 if the message is a priority control message and
* 0 if the message is any other type.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* allocb (), datab, msgb, messages
*/
#if __USE_PROTO__
int (pcmsg) (uchar_t type)
#else
int
pcmsg __ARGS ((type))
uchar_t type;
#endif
{
return pcmsg (type);
}
/*
*-STATUS:
* Compatibility (pre-MP DDI/DKI)
*
*-NAME:
* pullupmsg Concatenate bytes in a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int pullupmsg (mblk_t * mp, int len);
*
*-ARGUMENTS:
* mp Pointer to the message whose blocks are to be
* concatenated.
*
* len Number of bytes to concatenate.
*
*-DESCRIPTION:
* pullupmsg () tries to combine multiple data blocks into a single
* block. pullupmsg () concatenates and aligns the first "len" data bytes
* of the message pointed to by "mp". If "len" equals -1, all data is
* concatenated. If "len" bytes of the same message type cannot be found,
* pullupmsg () fails and returns 0.
*
*-RETURN VALUE:
* On success, 1 is returned; on failure, 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* This function is provided for compatibility with versions of the
* DDI/DKI prior to the System V, Release 4 Multiprocessor edition. Calls
* to this function should be replaced by calls to the msgpullup ()
* function instead.
*
*-SEE ALSO:
* allocb (), msgpullup ()
*/
#if __USE_PROTO__
int (pullupmsg) (mblk_t * mp, int len)
#else
int
pullupmsg __ARGS ((mp, len))
mblk_t * mp;
int len;
#endif
{
mblk_t * newmsg;
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
/*
* If the first block of the old message is sufficiently large to
* encompass the pullup request, then we need do no work. It is not
* clear whether the new msgpullup () function allows this simple
* optimisation, so we test for this here to guarantee it.
*/
if (((mp->b_wptr - mp->b_rptr) > len ||
(len == -1 && mp->b_cont == NULL)) &&
((ulong_t) mp->b_rptr & (sizeof (int) - 1)) == 0) {
/*
* Do nothing and return success. Note that the alignment test
* above is specific to the i386 implementation.
*/
return 1;
}
/*
* Now use the new msgpullup () function to perform the harder task
* of concatenating the message, and then perform some subterfuge to
* ensure that the new message replaces the old message's storage.
*
* This requires some delicate manipulations to avoid disrupting
* the flags and whatnot that support the message-triple storage
* system.
*/
if ((newmsg = msgpullup (mp, len)) == NULL)
return 0;
{
dblk_t * temp = mp->b_datap;
mp->b_datap = newmsg->b_datap;
newmsg->b_datap = temp;
} {
mblk_t * temp = mp->b_cont;
mp->b_cont = newmsg->b_cont;
newmsg->b_cont = temp;
}
mp->b_rptr = newmsg->b_rptr;
mp->b_wptr = newmsg->b_wptr;
/*
* Since we exchanged the "b_datap" and "b_cont" members of the
* message blocks, freeing the "new" message pointer will free those
* elements of the original message that have been made redundant by
* the pull-up operation, leaving the original message block intact
* but pointing to the rearranged data.
*/
freemsg (newmsg);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* put Call a put procedure.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void put (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to a message queue.
*
* mp Pointer to the message block being passed.
*
*-DESCRIPTION:
* put () calls the "put" procedure for the queue specified by "q",
* passing it the arguments "q" and "mp". It is typically used by a
* driver or module to call its own "put" procedure so that the proper
* accounting is done in the stream.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* DDI/DKI conforming drivers and modules are no longer permitted to call
* "put" procedures directly, but must call through the appropriate
* STREAMS utility function - for example, put (), putnext (), putctl (),
* putnextctl (), or qreply (). "put (q, mp)" is provided as a DDI/DKI-
* conforming equivalent to a direct call to a "put" procedure, which is
* no longer allowed.
*
*-SEE ALSO:
* putctl (), putctl1 (), putnext (), putnextctl (), putnextctl1 (),
* qreply ()
*/
#if __USE_PROTO__
void (put) (queue_t * q, mblk_t * mp)
#else
void
put __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
pl_t prev_pl;
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
/*
* If a message is put to a queue that either has no put procedure at
* all, or has been disabled with qprocsoff (), then we discard the
* message and return without doing anything.
*/
prev_pl = QFREEZE_TRACE (q, "put");
if ((q->q_active == 0 && (q->q_flag & QPROCSOFF) != 0) ||
q->q_qinfo->qi_putp == NULL)
cmn_err (CE_WARN, "put () to disabled queue/NULL putp");
else
QUEUE_PUT (q, mp, prev_pl);
QUNFREEZE_TRACE (q, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putbq Place a message at the head of a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putbq (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* mp Pointer to the message.
*
*-DESCRIPTION:
* putbq () puts a message back at the head of the queue. If messages of
* a higher priority are present on the queue, then "bp" is placed at the
* head of its corresponding priority band.
*
* All flow control parameters are updated. The queue's service routine
* is scheduled if it has not been disabled by a previous call to
* noenable ().
*
* putbq () is usually called when bcanputnext () or canputnext ()
* determines that the message cannot be passed on to the next stream
* component.
*
*-RETURN VALUE:
* putbq () returns 1 on success and 0 on failure.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* putbq () can fail if there is not enough memory to allocate the
* accounting data structures used with messages whose priority bands are
* greater than zero.
*
* High priority messages should never be put back on a queue from within
* a service routine.
*
*-SEE ALSO:
* bcanputnext (), canputnext (), getq (), insq (), putq (), rmvq (),
* msgb, queue
*/
#if __USE_PROTO__
int (putbq) (queue_t * q, mblk_t * mp)
#else
int
putbq __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
pl_t prev_pl;
qband_t * qbandp;
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
ASSERT (! pcmsg (mp->b_datap->db_type));
prev_pl = QFREEZE_TRACE (q, "putbq");
/*
* The code for high-priority messages has been factored out into a
* completely separate path because such messages do not have their
* size accumulated in the flow control-parameters at all, at all, and
* because the location where such messages are to be queued is
* determined in one step without search.
*/
if (IS_PRI_MSG (mp)) {
/*
* Since putq () is defined as forcing b_band to 0 for high-
* priority messages, we do the same.
*/
if ((mp->b_next = q->q_first) == NULL)
q->q_last = mp;
else
mp->b_next->b_prev = mp;
mp->b_prev = NULL;
mp->b_band = 0;
q->q_first = mp;
/*
* A high-priority message causes a queue to be scheduled
* even if a noenable () has been performed. For putbq (),
* this is true whether or not the condition set by getq ()
* returning null is true or not, which is the reason for the
* proscription against calling this routine for a high-
* priority message within a service procedure.
*/
/*
*!!! We need help from the STREAMS service-routine scheduler
*!!! to build a testable assertion for detecting when we are
*!!! called within the context of a service routine.
*/
(void) QUEUE_TRYSCHED (q);
goto alldone;
}
qbandp = NULL; /* paranoia */
if (mp->b_band > 0 &&
(qbandp = QUEUE_BAND (q, mp->b_band)) == NULL &&
(qbandp = QBAND_ALLOC (q, mp->b_band)) == NULL)
return 0;
else if (QUEUE_CHECK_SCHED (q, mp, qbandp)) {
/*
* Deal with band pointer maintenance now.
*/
if ((mp->b_next = qbandp->qb_first) != NULL) {
/*
* We directly know where we want to put the message.
*/
if ((mp->b_prev = mp->b_next->b_prev) == NULL)
q->q_first = mp;
else
mp->b_prev->b_next = mp;
mp->b_next->b_prev = mp;
qbandp->qb_first = mp;
goto alldone;
}
/*
* This is the only message in this band. We will need to
* search for the correct position to queue this message.
*/
qbandp->qb_last = qbandp->qb_first = mp;
}
/*
* Now use a brute-force search for the position. We could use the
* band structures to speed this up, but we anticipate that there will
* be few band or high-priority messages, so that a brute-force search
* will be suitably short.
*/
QUEUE_PLACE_MSG (q, mp, mp->b_band);
alldone:
QUNFREEZE_TRACE (q, prev_pl);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putctl Send a control message to a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putctl (queue_t * q, int type);
*
*-ARGUMENTS:
* q Pointer to the queue to which the message is to be
* sent.
*
* type Message type (must be control).
*
*-DESCRIPTION:
* putctl () tests the "type" argument to make sure a data type has not
* been specified, and then attempts to allocate a message block.
* putctl () fails if "type" is M_DATA, M_PROTO, or M_PCPROTO, or if a
* message block cannot be allocated. If successful, putctl () calls the
* "put" routine of the queue pointed to by "q", passing it the allocated
* message.
*
*-RETURN VALUE:
* On success, 1 is returned. Otherwise, if "type" is a data type, or if
* a message block cannot be allocated, 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument to putctl () and putnextctl () may not reference
* "q_next" (eg, an argument of "q->q_next" is erroneous on a
* multiprocessor and is disallowed by the DDI/DKI).
* "putnextctl (q, type)" is provided as a multiprocessor-safe equivalent
* to the common call "putctl (q->q_next, type)" which is no longer
* allowed.
*
*-SEE ALSO:
* put (), putctl1 (), putnextctl (), putnextctl1 ()
*/
#if __USE_PROTO__
int (putctl) (queue_t * q, int type)
#else
int
putctl __ARGS ((q, type))
queue_t * q;
int type;
#endif
{
mblk_t * ctlmsg;
QUEUE_TRACE (q, "putctl");
/*
* We cannot use datamsg () to test the type because datamsg () is
* specified as testing for M_DATA in addition to M_DATA, M_PROTO, and
* M_PCPROTO.
*/
if ((type & (M_PRI - 1)) <= M_PROTO ||
(ctlmsg = MSGB_ALLOC (0, BPRI_HI, KM_NOSLEEP)) == NULL)
return 0;
ctlmsg->b_datap->db_type = type;
put (q, ctlmsg);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putctl1 Send a control message with a one-byte parameter to a
* queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putctl1 (queue_t * q, int type, int param);
*
*-ARGUMENTS:
* q Pointer to the queue to which the message is to be
* sent.
*
* type Message type (must be control).
*
* param One-byte parameter.
*
*-DESCRIPTION:
* putctl1 (), like putctl (), tests the "type" argument to make sure a
* data type has not been specified, and attempts to allocate a message
* block. The "param" parameter can be used, for example, to specify the
* signal number when an "M_PCSIG" message is being sent. putctl1 ()
* fails if "type" is M_DATA, M_PROTO, or M_PCPROTO, or if a message
* block cannot be allocated. If successful, putctl1 () calls the "put"
* routine of the queue pointed to by "q", passing it the allocated
* message.
*
*-RETURN VALUE:
* On success, 1 is returned. Otherwise, if "type" is a data type, or if
* a message block cannot be allocated, 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument to putctl1 () and putnextctl1 () may not reference
* "q_next" (eg, an argument of "q->q_next" is erroneous on a
* multiprocessor and is disallowed by the DDI/DKI).
* "putnextctl1 (q, type)" is provided as a multiprocessor-safe
* equivalent to the common call "putctl1 (q->q_next, type)" which is no
* longer allowed.
*
*-SEE ALSO:
* put (), putctl (), putnextctl (), putnextctl1 ()
*/
#if __USE_PROTO__
int (putctl1) (queue_t * q, int type, int param)
#else
int
putctl1 __ARGS ((q, type, param))
queue_t * q;
int type;
int param;
#endif
{
mblk_t * ctlmsg;
QUEUE_TRACE (q, "putctl1");
/*
* We cannot use datamsg () to test the type because datamsg () is
* specified as testing for M_DATA in addition to M_DATA, M_PROTO, and
* M_PCPROTO.
*/
if ((type & (M_PRI - 1)) <= M_PROTO ||
(ctlmsg = MSGB_ALLOC (1, BPRI_HI, KM_NOSLEEP)) == NULL)
return 0;
ctlmsg->b_datap->db_type = type;
* ctlmsg->b_wptr ++ = (unsigned char) param;
put (q, ctlmsg);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putnext Send a message to the next queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putnext (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to the queue from which the message "mp" will
* be sent.
*
* mp Pointer to the message to be passed.
*
*-DESCRIPTION:
* The putnext () function is used to pass a message to the "put" routine
* of the next queue ("q->q_next") in the stream.
*
*-RETURN VALUE:
* Ignored.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* putnextctl (), putnextctl1 ()
*/
#if __USE_PROTO__
int (putnext) (queue_t * q, mblk_t * mp)
#else
int
putnext __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
QUEUE_TRACE (q, "putnext");
QUEUE_PUTNEXT (q, mp);
return 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putnextctl Send a control message to a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putnextctl (queue_t * q, int type);
*
*-ARGUMENTS:
* q Pointer to the queue from which the message is to be
* sent.
*
* type Message type (must be control type).
*
*-DESCRIPTION:
* putnextctl () tests the "type" argument to make sure a data type has
* been specified, and then attempts to allocate a message block.
* putnextctl () fails if "type" is M_DATA, M_PROTO, or M_PCPROTO, or
* if a message block cannot be allocated. If successful, putnextctl ()
* calls the "put" procedure of the queue pointed to by "q->q_next",
* passing it the allocated message.
*
*-RETURN VALUE:
* Upon successful completion, putnextctl () returns 1. If "type" is a
* data type, or if a message block cannot be allocated, 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument to putctl () and putnextctl () may not reference
* "q_next" (eg, an argument of "q->q_next" is erroneous on a
* multiprocessor and is disallowed by the DDI/DKI).
* "putnextctl (q, type)" is provided as a multiprocessor-safe equivalent
* to the common call "putctl (q->q_next, type)" which is no longer
* allowed.
*
*-SEE ALSO:
* put (), putctl (), putctl1 (), putnextctl1 ()
*/
#if __USE_PROTO__
int (putnextctl) (queue_t * q, int type)
#else
int
putnextctl __ARGS ((q, type))
queue_t * q;
int type;
#endif
{
mblk_t * ctlmsg;
QUEUE_TRACE (q, "putnextctl");
/*
* We cannot use datamsg () to test the type because datamsg () is
* specified as testing for M_DATA in addition to M_DATA, M_PROTO, and
* M_PCPROTO.
*/
if ((type & (M_PRI - 1)) <= M_PROTO ||
(ctlmsg = MSGB_ALLOC (0, BPRI_HI, KM_NOSLEEP)) == NULL)
return 0;
ctlmsg->b_datap->db_type = type;
QUEUE_PUTNEXT (q, ctlmsg);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putnextctl1 Send a control message with a one-byte parameter to a
* queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putctl1 (queue_t * q, int type, int param);
*
*-ARGUMENTS:
* q Pointer to the queue from which the message is to be
* sent.
*
* type Message type (must be control).
*
* param One-byte parameter.
*
*-DESCRIPTION:
* putnextctl1 () tests the "type" argument to make sure a data type has
* not been specified, and attempts to allocate a message block.
* putnext ctl1 () fails if "type" is M_DATA, M_PROTO, or M_PCPROTO, or
* if a message block cannot be allocated. If successful, putctl1 ()
* calls the "put" routine of the queue pointed to by "q->q_next",
* passing it the allocated message with the one byte parameter specified
* by "param".
*
*-RETURN VALUE:
* Upon successful completion, putnextctl1 () returns 1. If "type" is a
& data type, or if a message block cannot be allocated, 0 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The "q" argument to putctl1 () and putnextctl1 () may not reference
* "q_next" (eg, an argument of "q->q_next" is erroneous on a
* multiprocessor and is disallowed by the DDI/DKI).
* "putnextctl1 (q, type)" is provided as a multiprocessor-safe
* equivalent to the common call "putctl1 (q->q_next, type)" which is no
* longer allowed.
*
*-SEE ALSO:
* put (), putctl (), putctl1 (), putnextctl ()
*/
#if __USE_PROTO__
int (putnextctl1) (queue_t * q, int type, int param)
#else
int
putnextctl1 __ARGS ((q, type, param))
queue_t * q;
int type;
int param;
#endif
{
mblk_t * ctlmsg;
QUEUE_TRACE (q, "putnextctl1");
/*
* We cannot use datamsg () to test the type because datamsg () is
* specified as testing for M_DATA in addition to M_DATA, M_PROTO, and
* M_PCPROTO.
*/
if ((type & (M_PRI - 1)) <= M_PROTO ||
(ctlmsg = MSGB_ALLOC (1, BPRI_HI, KM_NOSLEEP)) == NULL)
return 0;
ctlmsg->b_datap->db_type = type;
* ctlmsg->b_wptr ++ = (unsigned char) param;
QUEUE_PUTNEXT (q, ctlmsg);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* putq Put a message to a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int putq (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* mp Pointer to the message.
*
*-DESCRIPTION:
* putq () is used to put messages on a queue after the "put" routine has
* finished processing the message. The message is placed after any other
* messages of the same priority, and flow control parameters are
* updated. The queue's service routine is scheduled if it has not been
* disabled by a previous call to noenable ().
*
*-RETURN VALUE:
* putq () returns 1 on success and 0 on failure.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* putq () can fail if there is not enough memory to allocate the
* accounting data structures used with messages whose priority bands are
* greater than zero.
*
*-SEE ALSO:
* getq (), insq (), putbq (), rmvq (), msgb, queue
*/
#if __USE_PROTO__
int (putq) (queue_t * q, mblk_t * mp)
#else
int
putq __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
pl_t prev_pl;
qband_t * qbandp;
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
prev_pl = QFREEZE_TRACE (q, "putq");
if (IS_PRI_MSG (mp)) {
/*
* Since putq () is defined as forcing b_band to 0 for high-
* priority messages, we do the same. In addition, we also
* always schedule the queue. No flow-control parameters are
* updated for high-priority messages.
*/
mp->b_band = 0;
(void) QUEUE_TRYSCHED (q);
/*
* We need to flow into the code that finds the correct
* insertion point for high-priority and band messages.
*/
} else if (qbandp = NULL, mp->b_band > 0 &&
(qbandp = QUEUE_BAND (q, mp->b_band)) == NULL &&
(qbandp = QBAND_ALLOC (q, mp->b_band)) == NULL)
return 0;
else if (QUEUE_CHECK_SCHED (q, mp, qbandp)) {
/*
* Deal with band pointer maintenance now.
*/
if ((mp->b_next = qbandp->qb_first) != NULL) {
/*
* We directly know where we want to put the message.
*/
if ((mp->b_prev = mp->b_next->b_prev) == NULL)
q->q_first = mp;
else
mp->b_prev->b_next = mp;
mp->b_next->b_prev = mp;
qbandp->qb_first = mp;
goto alldone;
}
/*
* This is the only message in this band. We will need to
* search for the correct position to queue this message.
*/
qbandp->qb_last = qbandp->qb_first = mp;
} else {
/*
* Since we will always be inserting at the end of the queue,
* we directly know where we will be inserting the new
* message.
*/
mp->b_next = NULL;
if ((mp->b_prev = q->q_last) == NULL)
q->q_first = mp;
else
mp->b_prev->b_next = mp;
q->q_last = mp;
goto alldone;
}
/*
* Since we want to insert after other messages with the same band
* number, we adjust the band down by one. This introduces a special
* case for high-priority messages, which we want to queue before all
* normal messages. Since we define high-priority messages as having
* a band number of 0, subtracting one makes the band value wrap
* around, yielding the desired behaviour.
*/
QUEUE_PLACE_MSG (q, mp, mp->b_band - 1);
alldone:
QUNFREEZE_TRACE (q, prev_pl);
return 1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* qenable Schedule a queue's service routine to be run.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void qenable (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
*-DESCRIPTION:
* qenable () puts the queue pointed to by "q" on the linked list of
* STREAMS routines that are ready to be called by the STREAMS scheduler.
* qenable () works regardless of whether the service routine has been
* disabled by a previous call to noenable ().
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* enableok (), noenable (), queue
*/
#if __USE_PROTO__
void (qenable) (queue_t * q)
#else
void
qenable __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "qenable");
(void) QUEUE_TRYSCHED (q);
QUNFREEZE_TRACE (q, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* qprocsoff Disable put and service procedures.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void qprocsoff (queue_t * rq);
*
*-ARGUMENTS:
* rq Pointer to a read queue.
*
*-DESCRIPTION:
* qprocsoff () disables the "put" and "service" routines of the driver
* or module whose read queue is pointed to by "rq". When the routines
* are disabled in a module, messages flow around the module as if it
* were not present in the stream.
*
* qprocsoff () must be called by the "close" routine of a driver or
* module before deallocating any resources on which the driver/module's
* put and service procedures depend.
*
* qprocsoff () will remove the queue's service routines from the list of
* service routines to be run and waits until any concurrent "put" or
& "service" routines are finished.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base level only.
*
*-NOTES:
* May sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks and read/write locks may not be held across
* calls to this function.
*
* Driver-defined sleep locks may be held across calls to this function.
*
*-SEE ALSO:
* qprocson ()
*/
#if __USE_PROTO__
void (qprocsoff) (queue_t * rq)
#else
void
qprocsoff __ARGS ((rq))
queue_t * rq;
#endif
{
pl_t prev_pl;
int dosleep;
queue_t * wq;
try_rprocsoff:
prev_pl = QFREEZE_TRACE (rq, "qprocsoff");
rq->q_flag |= QPROCSOFF;
if ((dosleep = rq->q_active > 0) != 0)
(void) LOCK (str_mem->sm_proc_lock, plstr);
QUNFREEZE_TRACE (rq, prev_pl);
if (dosleep) {
SV_WAIT (str_mem->sm_proc_sv, prilo, str_mem->sm_proc_lock);
goto try_rprocsoff;
}
/*
* Now do the same for the write side.
*/
ASSERT ((rq->q_flag & QREADR) != 0);
wq = W (rq);
try_wprocsoff:
prev_pl = QFREEZE_TRACE (wq, "qprocsoff");
wq->q_flag |= QPROCSOFF;
if ((dosleep = wq->q_active > 0) != 0)
(void) LOCK (str_mem->sm_proc_lock, plstr);
QUNFREEZE_TRACE (wq, prev_pl);
if (dosleep) {
SV_WAIT (str_mem->sm_proc_sv, prilo, str_mem->sm_proc_lock);
goto try_wprocsoff;
}
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* qprocson Enable put and service routines.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void qprocson (queue_t * rq);
*
*-ARGUMENTS:
* rq Pointer to a read queue.
*
*-DESCRIPTION:
* qprocson () enables the "put" and "service" routines of the driver or
* module whose read queue is pointed to by "rq". Prior to the call to
* qprocson (), the put and service routines of a newly pushed module or
* driver are disabled. For the module, messages flow around it as if it
* were not present in the stream.
*
* qprocson () must be called by the first open of a module or driver
* after allocation and initialization of any resources on which the put
* and service routines depend.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base level only.
*
*-NOTES:
* May sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks and read/write locks may not be held across
* calls to this function.
*
* Driver-defined sleep locks may be held across calls to this function.
*
*-SEE ALSO:
* qprocsoff ()
*/
#if __USE_PROTO__
void (qprocson) (queue_t * rq)
#else
void
qprocson __ARGS ((rq))
queue_t * rq;
#endif
{
pl_t prev_pl;
queue_t * wq;
/*
* Actually, this implementation of qprocsoff () busy-waits rather
* than sleeping, but don't count on that.
*/
prev_pl = QFREEZE_TRACE (rq, "qprocson");
ASSERT ((rq->q_flag & QREADR) != 0);
wq = W (rq);
(void) QFREEZE_TRACE (wq, "qprocson");
rq->q_flag &= ~ QPROCSOFF;
wq->q_flag &= ~ QPROCSOFF;
ASSERT (rq->q_active == 0 && wq->q_active == 0);
/*
* If there are any messages on the queues, the service procedures
* should be run. If the QWANTW flag is set, we'll back-enable the
* queues.
*/
if (rq->q_first != NULL && (rq->q_flag & QNOENB) != 0)
(void) QUEUE_TRYSCHED (rq);
if (wq->q_first != NULL && (wq->q_flag & QNOENB) != 0)
(void) QUEUE_TRYSCHED (wq);
if ((rq->q_flag & QWANTW) != 0) {
rq->q_flag &= ~ QWANTW;
QUEUE_BACKENAB (rq);
}
if ((wq->q_flag & QWANTW) != 0) {
wq->q_flag &= ~ QWANTW;
QUEUE_BACKENAB (wq);
}
QUNFREEZE_TRACE (wq, plstr);
QUNFREEZE_TRACE (rq, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* qreply Send a message in the opposite direction on a stream.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void qreply (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to the queue from which the message is being
* sent.
*
* mp Pointer to the message to be sent in the opposite
* direction.
*
*-DESCRIPTION:
* qreply () sends a message in the opposite direction from that which
* "q" is pointing. It calls the OTHERQ () function to find "q"'s
* partner, and passes the message by calling the "put" routine of the
* next queue in the stream after "q"'s partner.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
*/
#if __USE_PROTO__
void (qreply) (queue_t * q, mblk_t * mp)
#else
void
qreply __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
/*
* The DDI/DKI says the caller cannot have the stream frozen, but we
* don't actually need to do anything that would freeze the stream.
* The proscription arises because we need to freeze the other queue
* temporarily, and for one context to freeze both sides of a stream
* may induce deadlock.
*/
#ifndef NDEBUG
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "qreply");
QUNFREEZE_TRACE (q, prev_pl);
#endif
ASSERT (mp != NULL);
ASSERT (mp->b_datap != NULL);
q = OTHERQ (q);
QUEUE_PUTNEXT (q, mp);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* qsize Find the number of messages on a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int qsize (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue to be evaluated.
*
*-DESCRIPTION:
* qsize () evaluates the queue pointed to by "q" and returns the number
* of messages it contains.
*
*-RETURN VALUE:
* If there are no messages on the queue, "qsize" returns 0. Otherwise,
* it returns the number of messages on the queue.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* msgb, queue
*/
#if __USE_PROTO__
int (qsize) (queue_t * q)
#else
int
qsize __ARGS ((q))
queue_t * q;
#endif
{
pl_t prev_pl;
mblk_t * scan;
int count = 0;
prev_pl = QFREEZE_TRACE (q, "qsize");
for (scan = q->q_first ; scan != NULL ; scan = scan->b_next)
count ++;
QUNFREEZE_TRACE (q, prev_pl);
return count;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* RD Get a pointer to the read queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* queue_t * RD (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue whose read queue is to be
* returned.
*
*-DESCRIPTION:
* The RD () function accepts a queue pointer as an argument and returns
* a pointer to the read queue of the same module or driver.
*
*-RETURN VALUE:
* The pointer to the read queue.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* OTHERQ (), WR ()
*/
#if __USE_PROTO__
queue_t * (RD) (queue_t * q)
#else
queue_t *
RD __ARGS ((q))
queue_t * q;
#endif
{
QUEUE_TRACE (q, "RD");
return RD (q);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* rmvb Remove a message block from a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * rmvb (mblk_t * mp, mblk_t * bp);
*
*-ARGUMENTS:
* mp Message from which a message block is to be removed.
*
* bp Message block to be removed.
*
*-DESCRIPTION:
* rmvb () removes a message block ("bp") from a message ("mp"), and
* returns a pointer to the altered message. The message block is not
* freed, merely removed from the message. It is the caller's
* responsibility to free the message block.
*
*-RETURN VALUE:
* If successful, a pointer to the message (minus the removed block) is
* returned. If "bp" was the only block in the message before rmvb ()
* was called, NULL is returned. If the designated message block ("bp")
* was not in the message, -1 is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*/
#if __USE_PROTO__
mblk_t * (rmvb) (mblk_t * mp, mblk_t * bp)
#else
mblk_t *
rmvb __ARGS ((mp, bp))
mblk_t * mp;
mblk_t * bp;
#endif
{
mblk_t * scan;
ASSERT (mp != NULL);
ASSERT (bp != NULL);
if (mp == bp)
return mp->b_cont;
for (scan = mp ; scan != NULL ; scan = scan->b_cont)
if (scan->b_cont == bp) {
scan->b_cont = bp->b_cont;
return mp;
}
return (mblk_t *) -1;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* rmvq Remove a message from a queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void rmvq (queue_t * q, mblk_t * mp);
*
*-ARGUMENTS:
* q Pointer to the queue containing the message to be
* removed.
*
* mp Pointer to the message to be removed.
*
*-DESCRIPTION:
* rmvq () removes a message from a queue. A message can be removed from
* anywhere in a queue. To prevent modules and drivers from having to
* deal with the internals of message linkage on a queue, either rmvq ()
* or getq () should be used to remove a message from a queue.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller must have the stream frozen [see freezestr ()] when calling
* this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* "mp" must point to an existing message in the queue pointed to by "q",
* or a system panic will occur.
*
*-SEE ALSO:
* freezestr (), getq (), insq (), unfreezestr ().
*/
#if __USE_PROTO__
void (rmvq) (queue_t * q, mblk_t * mp)
#else
void
rmvq __ARGS ((q, mp))
queue_t * q;
mblk_t * mp;
#endif
{
ASSERT (mp != NULL);
QFROZEN_TRACE (q, "rmvq");
/*
* First, simply dequeue the message based on mp's "b_next" and
* "b_prev" members.
*/
if (mp->b_next == NULL) {
ASSERT (q->q_last == mp);
if ((q->q_last = mp->b_prev) == NULL)
QUEUE_DRAINED (q);
} else
mp->b_next->b_prev = mp->b_prev;
if (mp->b_prev == NULL) {
ASSERT (q->q_first == mp);
q->q_first = mp->b_next;
/*
* Since we are dequeueing a message from the front of the
* queue, record the message band.
*/
q->q_lastband = mp->b_band;
} else
mp->b_prev->b_next = mp->b_next;
/*
* Now we adjust the flow-control parameters and band information.
*/
if (! IS_PRI_MSG (mp)) {
ulong_t msgsize = MSG_SIZE (mp);
if (mp->b_band > 0) {
qband_t * qbandp = QUEUE_BAND (q, mp->b_band);
ASSERT (qbandp->qb_first == mp);
QBAND_DEQUEUE (qbandp, mp);
QBAND_REDUCE (q, qbandp, msgsize);
} else
QUEUE_REDUCE (q, msgsize);
}
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* SAMESTR Test if next queue is same type.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int SAMESTR (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
*-DESCRIPTION:
* The SAMESTR () function is used to see if the next queue in a stream
* (if it exists) is the same type as the current queue (that is, both
* are read queues or both are write queues). This can be used to
* determine the point in a STREAMS-based pipe where a read queue is
* linked to a write queue.
*
*-RETURN VALUE:
* SAMESTR () returns 1 if the next queue is the same type as the current
* queue. It returns 0 if the next queue does not exist or if it is not
* the same type.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller cannot have the stream frozen [see freezestr ()] when
* calling this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The argument "q" may not reference "q_next" (for example, an argument
* of "q->q_next" is erroneous on a multiprocessor and is disallowed by
* the DDI/DKI).
*
*-SEE ALSO:
* OTHERQ ()
*/
#if __USE_PROTO__
int (SAMESTR) (queue_t * q)
#else
int
SAMESTR __ARGS ((q))
queue_t * q;
#endif
{
int retval;
pl_t prev_pl;
prev_pl = QFREEZE_TRACE (q, "SAMESTR");
retval = q->q_next == NULL ? 0 :
(q->q_next->q_flag & QREADR) == (q->q_flag & QREADR);
QUNFREEZE_TRACE (q, prev_pl);
return retval;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* strlog Submit messages to the log driver.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
* #include <sys/strlog.h>
* #include <sys/log.h>
*
* int strlog (short mid, short sid, char level, uchar_t flags,
* char * fmt, ...);
*
*-ARGUMENTS:
* mid Identification number of the module or driver
* submitting the message.
*
* sid Identification number for a particular minor device.
*
* flags Bitmask of flags indicating message purpose. Valid
* flags are:
* SL_ERROR Message is for error logger.
* SL_TRACE Message is for tracing.
* SL_CONSOLE Message is for console logger.
* SL_NOTIFY If SL_ERROR is also set, mail copy of
* message to system administrator.
* SL_FATAL Modifier indicating error is fatal.
* SL_WARN Modifier indicating error is a
* warning.
* SL_NOTE Modifier indicating error is a notice.
*
* fmt printf () style format string. %s, %e, %g and %G
* formats are not allowed.
*
* ... Zero or more arguments to printf () (maximum of
* NLOGARGS, currently three).
*
*-DESCRIPTION:
* strlog () submits formatted messages to the "log" driver. The messages
* can be retrieved with the getmsg () system call. The "flags" argument
* specifies the type of the message and where it is to be sent.
*
*-RETURN VALUE:
* strlog () returns 0 if the message is not seen by all the readers, 1
* otherwise.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* log (7) in the "Programmer's Guide: STREAMS"
*/
#if __USE_PROTO__
int (strlog) (short mid, short sid, char level, ushort_t flags, char * fmt,
...)
#else
int
strlog __ARGS ((mid, sid, level, flags, fmt))
short mid;
short sid;
char level;
ushort_t flags;
char * fmt;
#endif
{
mblk_t * data;
ulong_t err_seq, trc_seq, con_seq;
mblk_t * errmsg, * trcmsg, * conmsg;
int failures;
int copies;
int pri;
# define SL_ALL (SL_ERROR | SL_TRACE | SL_CONSOLE | SL_NOTIFY |\
SL_FATAL | SL_WARN | SL_NOTE)
ASSERT ((flags & SL_ALL) != 0);
ASSERT ((flags & ~ SL_ALL) == 0);
ASSERT (fmt != NULL);
/*
* The first thing we do is assign a sequence number to this log item
* so that we can correctly detect when log items have been discarded
* due to lack of available resources. In addition, there are several
* sequence-number spaces available, depending on the destination of
* the log message. We acquire numbers from all the spaces now, and
* write them into the actual log messages later.
*/
{
pl_t prev_pl;
int temp;
prev_pl = LOCK (str_mem->sm_seq_lock, plstr);
err_seq = trc_seq = con_seq = 0; /* paranoia */
if ((flags & SL_ERROR) != 0)
err_seq = str_mem->sm_err_seq ++;
if ((flags & SL_TRACE) != 0)
trc_seq = str_mem->sm_trc_seq ++;
if ((flags & SL_CONSOLE) != 0)
con_seq = str_mem->sm_con_seq ++;
temp = str_mem->sm_log_rq == NULL;
UNLOCK (str_mem->sm_seq_lock, prev_pl);
/*
* If the log driver isn't installed yet, don't bother with
* the rest of this routine.
*/
if (temp)
return 0;
}
/*
* While the documentation for this function doesn't spell it out,
* the log (7) driver traffics in error, trace and console messages,
* with the others being variations on that theme.
*
* Somehow the "flags" have to be mapped into a priority/facility code
* according to some rules that are alluded to in the log (7) manual
* pages.
*/
{
size_t size;
va_list args;
uchar_t * dest;
int temp;
if ((flags & (SL_ERROR | SL_FATAL)) != 0)
pri = BPRI_HI;
else if ((flags & SL_WARN) != 0)
pri = BPRI_MED;
else
pri = BPRI_LO;
/*
* The data portion of a STREAMS logger message contains the
* unexpanded text of the "fmt" string plus NLOGARGS worth of
* data containing any extra arguments packed after the string
* aligned to the next word address.
*
* We'll also need at least one control message section, which
* we can copy as necessary later on.
*/
/*
* ALIGNMENT-DEPENDENT CODE.
*/
size = ((strlen (fmt) + 4) & ~ (sizeof (int) - 1)) +
NLOGARGS * sizeof (ulong_t);
if ((data = MSGB_ALLOC (size, pri, KM_NOSLEEP)) == NULL) {
/*
* Return a failure indication. This is not a major
* problem, as the gaps in the sequence space tell a
* story...
*/
return 0;
}
/*
* Copy the format string and the argument data to the log
* buffer.
*/
dest = data->b_rptr;
while ((* dest ++ = * (uchar_t *) fmt ++) != 0)
;
dest += (sizeof (int) - 1) - (((unsigned long) dest - 1) &
(sizeof (int) - 1));
va_start (args, fmt);
for (temp = 0 ; temp < NLOGARGS ; temp ++) {
* (ulong_t *) dest = va_arg (args, ulong_t);
dest += sizeof (ulong_t);
}
va_end (args);
data->b_wptr += size;
ASSERT (data->b_wptr == dest);
}
/*
* Now send the log request to the read side of the log driver. This
* is how we distinguish kernel log requests from user log requests,
* since the user requests will arrive on the write side as usual.
*
* Since the various destinations for log messages expect to see
* messages with sequence numbers tailored to them, we create
* additional copies of the log message as needed. For this to work,
* we work out the copies first, and then send them.
*
* This would be a lot easier if C had nested functions as standard.
* Sadly, only GNU does this now.
*/
failures = 0;
copies = 0;
errmsg = STRLOG_MAKE (mid, sid, level, flags, err_seq, SL_ERROR,
& copies, & failures, data);
trcmsg = STRLOG_MAKE (mid, sid, level, flags, trc_seq, SL_TRACE,
& copies, & failures, data);
conmsg = STRLOG_MAKE (mid, sid, level, flags, con_seq, SL_CONSOLE,
& copies, & failures, data);
if (errmsg != NULL)
put (str_mem->sm_log_rq, errmsg);
if (trcmsg != NULL)
put (str_mem->sm_log_rq, trcmsg);
if (conmsg != NULL)
put (str_mem->sm_log_rq, conmsg);
if (copies == 0)
freemsg (data);
return failures == 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* strqget Get information about a queue or band of the queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int strqget (queue_t * q, qfields_t what, uchar_t pri, long * valp);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* what The field of the queeu about which to return
* information. Valid values are:
*
* QHIWAT High water mark of the specified priority
* band.
*
* QLOWAT Low water mark of the specified priority band.
*
* QMAXPSZ Maximum packet size of the specified priority
* band.
*
* QMINPSZ Minimum packet size of the specified priority
* band.
*
* QCOUNT Number of bytes of data in messages in the
* specified priority band.
*
* QFIRST Pointer to the first message in the specified
* priority band.
*
* QLAST Pointer to the last message in the specified
* priority band.
*
* QFLAG Flags for the specified priority band.
*
* pri Priority band of the queue about which to obtain
* information.
*
* valp Pointer to the memory locatation where the value is
* to be stored.
*
*-DESCRIPTION:
* strqget () gives drivers and modules a way to get information about
* a queue or a particular priority band of a queue without directly
* accessing STREAMS data structures.
*
*-RETURN VALUE:
* On success, 0 is returns. An error number is returned on failure. The
* actual value of the requested field is returned through the reference
* parameter "valp".
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller must have the stream frozen [see freezestr ()] when calling
* this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* freezestr (), strqset (), unfreezestr (), queue
*/
#if __USE_PROTO__
int (strqget) (queue_t * q, qfields_t what, uchar_t pri, long * valp)
#else
int
strqget __ARGS ((q, what, pri, valp))
queue_t * q;
qfields_t what;
uchar_t pri;
long * valp;
#endif
{
QFROZEN_TRACE (q, "strqget");
if (pri > 0)
switch (what) {
case QHIWAT:
* valp = q->q_hiwat;
break;
case QLOWAT:
* valp = q->q_lowat;
break;
case QMAXPSZ:
* valp = q->q_maxpsz;
break;
case QMINPSZ:
* valp = q->q_minpsz;
break;
case QCOUNT:
* valp = q->q_count;
break;
case QFIRST:
* valp = (long) q->q_first;
break;
case QLAST:
* valp = (long) q->q_last;
break;
case QFLAG:
* valp = q->q_flag;
break;
default:
return EINVAL;
}
else {
qband_t * qbandp;
if ((qbandp = QUEUE_BAND (q, pri)) == NULL &&
(qbandp = QBAND_ALLOC (q, pri)) == NULL)
return ENOMEM;
switch (what) {
case QHIWAT:
* valp = qbandp->qb_hiwat;
break;
case QLOWAT:
* valp = qbandp->qb_lowat;
break;
case QCOUNT:
* valp = qbandp->qb_count;
break;
case QFIRST:
* valp = (long) qbandp->qb_first;
break;
case QLAST:
* valp = (long) qbandp->qb_last;
break;
case QFLAG:
* valp = qbandp->qb_flag;
break;
default:
return EINVAL;
}
}
return 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* strqset Change information about a queue or band of the queue.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* int strqset (queue_t * q, qfields_t what, uchar_t pri, long val);
*
*-ARGUMENTS:
* q Pointer to the queue.
*
* what The field of the queue to change. Value values are:
*
* QHIWAT High water mark of the specified priority
* band.
*
* QLOWAT Low water mark of the specified priority band.
*
* QMAXPSZ Maximum packet size of the specified priority
* band.
*
* QMINPSZ Minimum packet size of the specified priority
* band.
*
* pri Priority band of the queue to be changed.
*
* val New value for the field to be changed.
*
*-DESCRIPTION:
* strqset () gives drivers and modules a way to change information about
* a queue or a particular priority band of a queue without directly
* accessing STREAMS data structures.
*
*-RETURN VALUE:
* On success, 0 is returned. An error number is returned on failure.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller must have the stream frozen [see freezestr ()] when calling
* this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
*/
#if __USE_PROTO__
int (strqset) (queue_t * q, qfields_t what, uchar_t pri, long val)
#else
int
strqset __ARGS ((q, what, pri, val))
queue_t * q;
qfields_t what;
uchar_t pri;
long val;
#endif
{
QFROZEN_TRACE (q, "strqset");
if (pri > 0)
switch (what) {
case QHIWAT:
q->q_hiwat = val;
break;
case QLOWAT:
q->q_lowat = val;
break;
case QMAXPSZ:
q->q_maxpsz = val;
break;
case QMINPSZ:
q->q_minpsz = val;
break;
case QCOUNT:
case QFIRST:
case QLAST:
case QFLAG:
return EPERM;
default:
return EINVAL;
}
else {
qband_t * qbandp;
if ((qbandp = QUEUE_BAND (q, pri)) == NULL &&
(qbandp = QBAND_ALLOC (q, pri)) == NULL)
return ENOMEM;
switch (what) {
case QHIWAT:
qbandp->qb_hiwat = val;
break;
case QLOWAT:
qbandp->qb_lowat = val;
break;
case QCOUNT:
case QFIRST:
case QLAST:
case QFLAG:
return EPERM;
default:
return EINVAL;
}
}
return 0;
}
/*
*-STATUS:
* Compatibility (pre-MP DDI/DKI)
*
*-NAME:
* testb Check for an available buffer.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* int testb (int size, int pri);
*
*-ARGUMENTS:
* size Size of the requested buffer.
*
* pri Priority of the allocb () request.
*
*-DESCRIPTION:
* testb () checks to see if an allocb () call is likely to succeed if
* a buffer of "size" bytes at priority "pri" is requested. Even if
* testb () returns successfully, the call to allocb () can fail.
*
*-RETURN VALUE:
* Returns 1 if a buffer of the requested size is available, and 0 if
* one is not.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* This function is provided purely as a porting convenience for
* developers working with drivers developed under earlier releases of
* the System V DDI/DKI or under STREAMS from System V, Release 3. Calls
* to this function should be replaced with calls to functions that do
* the real work.
*
*-SEE ALSO:
* allocb (), bufcall ().
*/
#if __USE_PROTO__
int (testb) (int size, int pri)
#else
int
testb __ARGS ((size, pri))
int size;
int pri;
#endif
{
pl_t prev_pl;
int return_val;
/*
* This function is one of the most braindead peices of STREAMS. It
* may not have been initially, but by the SVR4 DDI/DKI, it was
* *totally* useless. The example code given in that DDI/DKI issue is
* so bad it's unbelievable, and they admit it too...
*/
ASSERT (size > 0);
ASSERT (pri == BPRI_LO || pri == BPRI_HI || pri == BPRI_LO);
pri = MAP_PRI_LEVEL (pri);
size = MSGB_SIZE (pri);
/*
* Lock the basic lock protecting access to the memory pool
* and attempt to acquire the memory we desire.
*/
prev_pl = LOCK (str_mem->sm_msg_lock, str_msg_pl);
/*
* Before allocating any memory, we check to see that it makes sense
* to give out that memory to the given priority level.
*/
return_val = str_mem->sm_used + size < str_mem->sm_max [pri] &&
st_maxavail (str_mem->sm_msg_heap) >= size;
UNLOCK (str_mem->sm_msg_lock, prev_pl);
return return_val;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* unbufcall Cancel a pending bufcall () request.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* void unbufcall (toid_t id);
*
*-ARGUMENTS:
* id Identifier returned from bufcall () or esbbcall ().
*
*-DESCRIPTION:
* unbufcall () cancels a pending bufcall () or esbbcall () request. The
* argument "id" is a non-zero identifier for the request to be
* cancelled. "id" is returned from the bufcall () or esbbcall ()
* function used to issue the request.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* bufcall (), esbbcall ()
*/
#if __USE_PROTO__
void (unbufcall) (toid_t id)
#else
void
unbufcall __ARGS ((id))
toid_t id;
#endif
{
pl_t prev_pl;
selist_t * selistp;
sevent_t * sscan;
sevent_t * sprev;
ASSERT (id != 0);
/*
* Let's lock the list that we are going to search for the new event
* on.
*/
selistp = & str_mem->sm_bcevents [MAP_PRI_LEVEL (TOID_TO_PRI (id))];
prev_pl = SELIST_LOCK (selistp);
for (sscan = selistp->sl_head, sprev = NULL ; sscan != NULL ;
sscan = (sprev = sscan)->se_next) {
/*
* If we find it, dequeue it.
*/
if (sscan->se_id == id) {
if (sprev == NULL)
selistp->sl_head = sscan->se_next;
else
sprev->se_next = sscan->se_next;
#if _FIFO_BUFCALL
if (selistp->sl_tail == sscan) {
ASSERT (sscan->se_next == NULL);
selistp->sl_tail = sprev;
} else
ASSERT (sscan->se_next != NULL);
#endif
break;
}
}
SELIST_UNLOCK (selistp, prev_pl);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* unfreezestr Unfreeze the state of a stream.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/stream.h>
*
* void unfreezestr (queue_t * q, pl_t prev_pl);
*
*-ARGUMENTS:
* q Pointer to a message queue.
*
* pl The interrupt priority level to be set (if the
* implementation requires that interrupts be blocked in
* order to prevent deadlock) after unfreezing the
* stream. See LOCK_ALLOC () for a list of valid values
* for "pl". "pl" should be the value that was returned
* from the corresponding call to freezestr () unless the
* caller has a specific need to set some other interrupt
* priority level. Although portable drivers must always
* specify an appropriate "pl" argument, implementations
* which do not require that the interrupt priority be
* raised while the stream is frozen may choose to ignore
* this argument.
*
*-DESCRIPTION:
* unfreezestr () unfreezes the state of the stream containing the queue
* specified by "q", and sets the interrupt priority level to the value
* specified by "pl". Unfreezing the state of the stream allows
* continuation of all activities that were forced to wait while the
* stream was frozen.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* The caller must have the stream frozen [see freezestr ()] when calling
* this function.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* freezestr ()
*/
#if __USE_PROTO__
void (unfreezestr) (queue_t * q, pl_t pl)
#else
void
unfreezestr __ARGS ((q, pl))
queue_t * q;
pl_t pl;
#endif
{
unsigned long back;
QFROZEN_TRACE (q, "unfreezestr");
/*
* Since the caller might have used rmvq () to cause a condition where
* queues behind the current one need back-enabling, test for this.
*/
if ((back = q->q_flag & QBACK) != 0)
q->q_flag &= ~ QBACK;
QUNFREEZE_TRACE (q, pl);
if (back)
QUEUE_BACKENAB (q);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* unlinkb Remove a message block from the head of a message.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* mblk_t * unlinkb (mblk_t * mp);
*
*-ARGUMENTS:
* mp Pointer to the message.
*
*-DESCRIPTION:
* unlinkb () removes the first message block from the message pointed to
* by "mp". The removed message block is not freed. It is the caller's
* responsibility to free it.
*
*-RETURN VALUE:
* unlinkb () returns a pointer to the remainder of the message after the
* first message block has been removed. If there is only one message
* block in the message, NULL is returned.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* linkb ()
*/
#if __USE_PROTO__
mblk_t * (unlinkb) (mblk_t * mp)
#else
mblk_t *
unlinkb __ARGS ((mp))
mblk_t * mp;
#endif
{
mblk_t * retval;
ASSERT (mp != NULL);
retval = mp->b_cont;
mp->b_cont = NULL;
return retval;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* WR Get a pointer to the write queue.
*
*-SYNOPSIS:
* #include <sys/stream.h>
*
* queue_t * WR (queue_t * q);
*
*-ARGUMENTS:
* q Pointer to the queue whose write queue is to be
* returned.
*
*-DESCRIPTION:
* The WR () function accepts a queue pointer as an argument and returns
* a pointer to the write queue of the same module.
*
*-RETURN VALUE:
* To pointer to the write queue.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
*-SEE ALSO:
* OTHERQ (), RD ().
*/
#if __USE_PROTO__
queue_t * (WR) (queue_t * q)
#else
queue_t *
WR __ARGS ((q))
queue_t * q;
#endif
{
QUEUE_TRACE (q, "WR");
return WR (q);
}