|
|
coherent
#define _DDI_DKI 1
#define _SYSV4 1
/*
* This file contains various miscellaneous STREAMS library functions that
* have been moved into a file on their own. Mostly, this stuff deals with
* scheduling STREAMS service procedures, running STREAMS service procedures,
* invoking bufcall ()/esbbcall () events, and similar stuff.
*/
#include <common/ccompat.h>
#include <kernel/strmlib.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/stream.h>
/*
* Allocate and initialize a schedule.
*
* Callable only from base level. This function may sleep.
*/
#if __USE_PROTO__
ssched_t * (QSCHED_ALLOC) (void)
#else
ssched_t *
QSCHED_ALLOC __ARGS (())
#endif
{
ssched_t * sched;
if ((sched = (ssched_t *) kmem_alloc (sizeof (* sched), KM_SLEEP))
!= NULL) {
if (SCHLOCK_INIT (sched, KM_SLEEP) == 0) {
kmem_free (sched, sizeof (* sched));
return NULL;
}
sched->ss_head = sched->ss_tail = NULL;
}
return sched;
}
/*
* Destroy a schedule.
*
* The schedule most not have any STREAMS queues threaded on it.
*/
#if __USE_PROTO__
void (QSCHED_FREE) (ssched_t * sched)
#else
void
QSCHED_FREE __ARGS ((sched))
ssched_t * sched;
#endif
{
ASSERT (sched != NULL);
ASSERT (sched->ss_head == NULL && sched->ss_tail == NULL);
SCHLOCK_DESTROY (sched);
kmem_free (sched, sizeof (* sched));
}
/*
* Remove a queue from a schedule. This function is available for modules and
* drivers to call. Note that STREAMS itself will only call this function at
* queue destruction time.
*/
#if __USE_PROTO__
void (QSCHED_UNSCHEDULE) (queue_t * q, ssched_t * sched)
#else
void
QSCHED_UNSCHEDULE __ARGS ((q, sched))
queue_t * q;
ssched_t * sched;
#endif
{
pl_t lock_pl;
queue_t * scan;
queue_t * prev;
ASSERT (sched != NULL);
QUEUE_TRACE (q, "QUEUE_UNSCHEDULE");
lock_pl = SCHLOCK_LOCK (sched, "QUEUE_UNSCHEDULE");
for (scan = sched->ss_head, prev = NULL ; scan != NULL ;
scan = (prev = scan)->q_link) {
if (scan == q) {
if (prev == NULL)
sched->ss_head = q->q_next;
else
prev->q_link = q->q_next;
if (sched->ss_tail == q)
sched->ss_tail = prev;
break;
}
}
SCHLOCK_UNLOCK (str_mem->sm_sched, lock_pl);
}
/*
* Dequeue and return the first stream in the schedule.
*/
#if __USE_PROTO__
queue_t * (QSCHED_GETFIRST) (ssched_t * sched)
#else
queue_t *
QSCHED_GETFIRST __ARGS ((sched))
ssched_t * sched;
#endif
{
pl_t prev_pl;
queue_t * q;
ASSERT (sched != NULL);
prev_pl = SCHLOCK_LOCK (sched, "QSCHED_GETFIRST");
if ((q = sched->ss_head) != NULL) {
if ((sched->ss_head = q->q_link) == NULL)
sched->ss_tail = NULL;
}
SCHLOCK_UNLOCK (sched, prev_pl);
return q;
}
/*
* Add a queue to a schedule. This function is available for use by modules
* and drivers. It is callable from base or interrupt level and does not
* sleep.
*
* The return value is 0 for success, and -1 if it is unable to schedule the
* queue.
*/
#if __USE_PROTO__
int (QSCHED_SCHEDULE) (queue_t * q, ssched_t * sched)
#else
int
QSCHED_SCHEDULE __ARGS ((q, sched))
queue_t * q;
ssched_t * sched;
#endif
{
pl_t lock_pl;
QUEUE_TRACE (q, "QSCHED_SCHEDULE");
/*
* Link this queue into the tail of the scheduling
* list after locking the scheduling list.
*/
lock_pl = SCHLOCK_LOCK (sched, "QSCHED_SCHEDULE");
q->q_link = NULL;
if (sched->ss_tail == NULL)
sched->ss_head = q;
else
sched->ss_tail->q_link = q;
sched->ss_tail = q;
SCHLOCK_UNLOCK (str_mem->sm_sched, lock_pl);
/*
* Under this implementation, it is not really possible for a request
* to fail, but because we are using STREAMS internal data here, we
* allow for a portable implementation that may fail.
*/
return 0;
}
/*
* Code for checking which bufcall events to call, in a function by itself to
* keep things clear and maintainable.
*
* We take a snapshot of the available memory, then loop over the bufcall ()/
* esbbcall () lists and give away memory until our notion of what the memory
* pool size ought to be drops to a level where there are cells we think we
* should hold off enabling... if we make that decision, we mark things so
* that next time through here we look again, then we exit.
*/
#if __USE_PROTO__
void (RUN_BUFCALLS) (void)
#else
void
RUN_BUFCALLS __ARGS (())
#endif
{
size_t mem_level;
pl_t prev_pl;
sevent_t * runlist;
sevent_t * seventp;
int i;
/*
* Take a snapshot of the amount of STREAMS memory that is in use, so
* we can parcel it out to the bufcall routines. We clear the bufcall
* defer flag now so that if STREAMS memory is made available after
* our snapshot we will be run again.
*/
ATOMIC_STORE_UCHAR (ddi_global_data ()->dg_run_bufcalls, 0);
mem_level = str_mem->sm_used;
runlist = NULL;
for (i = N_PRI_LEVELS ; i -- > 0 ; ) {
selist_t * elistp;
unsigned long max = str_mem->sm_max [i];
elistp = & str_mem->sm_bcevents [i];
prev_pl = SELIST_LOCK (elistp);
while ((seventp = elistp->sl_head) != NULL) {
/*
* If the amount of memory requested in this cell is
* enough to push the memory level over the top for
* this allocation priority, then give up.
*/
if ((mem_level += seventp->se_size) > max)
goto give_up;
/*
* Dequeue the event from the event list and add it to
* our work list.
*/
elistp->sl_head = seventp->se_next;
seventp->se_next = runlist;
runlist = seventp;
}
#if _FIFO_BUFCALL
/*
* If we have drained all the events in this cell...
*/
elistp->sl_tail = NULL;
#endif
give_up:
SELIST_UNLOCK (elistp, prev_pl);
if (seventp != NULL)
break;
}
/*
* Now use 'i' as a boolean to remember whether we have any
* outstanding event cells we though it better not to run.
*/
i = seventp != NULL;
/*
* Now walk over our little work list...
*/
while (runlist != NULL) {
seventp = runlist;
runlist = seventp->se_next;
prev_pl = splstr ();
(* seventp->se_func) (seventp->se_arg);
(void) splx (prev_pl);
kmem_free (seventp, sizeof (* seventp));
}
/*
* If we found anything worth running, try again after we have given
* the bufcall functions a chance to claim the memory that was free
* first time through. We queue another defer request so that we give
* a chance to other deferred routines.
*/
if (i)
SCHEDULE_BUFCALLS ();
}
/*
* Streams scheduling routine, invoked before return to user level. Runs any
* service procedures that have been enabled, calls bufcall ()/esbbcall ()
* events if memory is sitting around, and also wakes up processes sleeping
* in kmem_alloc ()/kmem_zalloc () for memory to become available.
*/
#if __USE_PROTO__
void (RUN_STREAMS) (void)
#else
void
RUN_STREAMS __ARGS (())
#endif
{
queue_t * q;
/*
* Before we start running through the queues that we need to service,
* turn off the "deferred" flag for this routine to avoid race
* conditions.
*/
ATOMIC_STORE_UCHAR (ddi_global_data ()->dg_run_strsched, 0);
while ((q = QSCHED_GETFIRST (str_mem->sm_sched)) != NULL) {
pl_t prev_pl;
/*
* Before we run the service procedure, we must ensure
* that we are not going to re-enter the service
* routine. After that, we can turn off the 'enabled'
* flag.
*/
prev_pl = QFREEZE_TRACE (q, "STREAMS_SCHEDULER");
if ((q->q_flag & QSRVACTIVE) != 0) {
/*
* Put the queue back and look for something
* else to do.
*/
QUNFREEZE_TRACE (q, prev_pl);
QSCHED_SCHEDULE (q, str_mem->sm_sched);
continue;
}
q->q_flag = (q->q_flag & ~ QENAB) | QSRVACTIVE;
if ((q->q_flag & QPROCSOFF) != 0) {
/*
* The service procedure of this queue has
* been disabled, so we leave things alone.
*/
goto srvdone;
}
q->q_active ++;
QUNFREEZE_TRACE (q, prev_pl);
(* q->q_qinfo->qi_srvp) (q);
prev_pl = QFREEZE_TRACE (q, "STREAMS_SCHEDULER");
q->q_flag &= ~ QSRVACTIVE;
if (-- q->q_active == 0 && (q->q_flag & QPROCSOFF) != 0) {
/*
* Wake up the qprocoffs () procedure waiting
* for us to go exit.
*/
(void) LOCK (str_mem->sm_proc_lock, plstr);
SV_BROADCAST (str_mem->sm_proc_sv, 0);
UNLOCK (str_mem->sm_proc_lock, plstr);
}
srvdone:
QUNFREEZE_TRACE (q, prev_pl);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.