freebsd-dev/sys/netatm/atm_subr.c
Poul-Henning Kamp 1820df7a2d Add new files for HARP3
Host ATM Research Platform (HARP), Network Computing Services, Inc.
This software was developed with the support of the Defense Advanced
Research Projects Agency (DARPA).
1998-09-15 08:23:17 +00:00

971 lines
18 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $Id: atm_subr.c,v 1.10 1998/05/18 19:06:02 mks Exp $
*
*/
/*
* Core ATM Services
* -----------------
*
* Miscellaneous ATM subroutines
*
*/
#ifndef lint
static char *RCSid = "@(#) $Id: atm_subr.c,v 1.10 1998/05/18 19:06:02 mks Exp $";
#endif
#include <netatm/kern_include.h>
/*
* Global variables
*/
struct atm_pif *atm_interface_head = NULL;
struct atm_ncm *atm_netconv_head = NULL;
Atm_endpoint *atm_endpoints[ENDPT_MAX+1] = {NULL};
struct sp_info *atm_pool_head = NULL;
struct stackq_entry *atm_stackq_head = NULL, *atm_stackq_tail;
struct ifqueue atm_intrq;
#ifdef sgi
int atm_intr_index;
#endif
struct atm_sock_stat atm_sock_stat = {0};
int atm_init = 0;
int atm_debug = 0;
int atm_dev_print = 0;
int atm_print_data = 0;
int atm_version = ATM_VERSION;
struct timeval atm_debugtime = {0, 0};
struct sp_info atm_attributes_pool = {
"atm attributes pool", /* si_name */
sizeof(Atm_attributes), /* si_blksiz */
10, /* si_blkcnt */
100 /* si_maxallow */
};
/*
* Local functions
*/
static void atm_compact __P((struct atm_time *));
static KTimeout_ret atm_timexp __P((void *));
/*
* Local variables
*/
static struct atm_time *atm_timeq = NULL;
static struct atm_time atm_compactimer = {0, 0};
static struct sp_info atm_stackq_pool = {
"Service stack queue pool", /* si_name */
sizeof(struct stackq_entry), /* si_blksiz */
10, /* si_blkcnt */
10 /* si_maxallow */
};
/*
* Initialize ATM kernel
*
* Performs any initialization required before things really get underway.
* Called from ATM domain initialization or from first registration function
* which gets called.
*
* Arguments:
* none
*
* Returns:
* none
*
*/
void
atm_initialize()
{
/*
* Never called from interrupts, so no locking needed
*/
if (atm_init)
return;
atm_init = 1;
#ifndef __FreeBSD__
/*
* Add ATM protocol family
*/
(void) protocol_family(&atmdomain, NULL, NULL);
#endif
atm_intrq.ifq_maxlen = ATM_INTRQ_MAX;
#ifdef sgi
atm_intr_index = register_isr(atm_intr);
#endif
/*
* Initialize subsystems
*/
atm_aal5_init();
/*
* Prime the timer
*/
(void) timeout(atm_timexp, (void *)0, hz/ATM_HZ);
/*
* Start the compaction timer
*/
atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
}
/*
* Allocate a Control Block
*
* Gets a new control block allocated from the specified storage pool,
* acquiring memory for new pool chunks if required. The returned control
* block's contents will be cleared.
*
* Arguments:
* sip pointer to sp_info for storage pool
*
* Returns:
* addr pointer to allocated control block
* 0 allocation failed
*
*/
void *
atm_allocate(sip)
struct sp_info *sip;
{
void *bp;
struct sp_chunk *scp;
struct sp_link *slp;
int s = splnet();
/*
* Count calls
*/
sip->si_allocs++;
/*
* Are there any free in the pool?
*/
if (sip->si_free) {
/*
* Find first chunk with a free block
*/
for (scp = sip->si_poolh; scp; scp = scp->sc_next) {
if (scp->sc_freeh != NULL)
break;
}
} else {
/*
* No free blocks - have to allocate a new
* chunk (but put a limit to this)
*/
struct sp_link *slp_next;
int i;
/*
* First time for this pool??
*/
if (sip->si_chunksiz == 0) {
size_t n;
/*
* Initialize pool information
*/
n = sizeof(struct sp_chunk) +
sip->si_blkcnt *
(sip->si_blksiz + sizeof(struct sp_link));
sip->si_chunksiz = roundup(n, SPOOL_ROUNDUP);
/*
* Place pool on kernel chain
*/
LINK2TAIL(sip, struct sp_info, atm_pool_head, si_next);
}
if (sip->si_chunks >= sip->si_maxallow) {
sip->si_fails++;
(void) splx(s);
return (NULL);
}
scp = (struct sp_chunk *)
KM_ALLOC(sip->si_chunksiz, M_DEVBUF, M_NOWAIT);
if (scp == NULL) {
sip->si_fails++;
(void) splx(s);
return (NULL);
}
scp->sc_next = NULL;
scp->sc_info = sip;
scp->sc_magic = SPOOL_MAGIC;
scp->sc_used = 0;
/*
* Divy up chunk into free blocks
*/
slp = (struct sp_link *)(scp + 1);
scp->sc_freeh = slp;
for (i = sip->si_blkcnt; i > 1; i--) {
slp_next = (struct sp_link *)((caddr_t)(slp + 1) +
sip->si_blksiz);
slp->sl_u.slu_next = slp_next;
slp = slp_next;
}
slp->sl_u.slu_next = NULL;
scp->sc_freet = slp;
/*
* Add new chunk to end of pool
*/
if (sip->si_poolh)
sip->si_poolt->sc_next = scp;
else
sip->si_poolh = scp;
sip->si_poolt = scp;
sip->si_chunks++;
sip->si_total += sip->si_blkcnt;
sip->si_free += sip->si_blkcnt;
if (sip->si_chunks > sip->si_maxused)
sip->si_maxused = sip->si_chunks;
}
/*
* Allocate the first free block in chunk
*/
slp = scp->sc_freeh;
scp->sc_freeh = slp->sl_u.slu_next;
scp->sc_used++;
sip->si_free--;
bp = (slp + 1);
/*
* Save link back to pool chunk
*/
slp->sl_u.slu_chunk = scp;
/*
* Clear out block
*/
KM_ZERO(bp, sip->si_blksiz);
(void) splx(s);
return (bp);
}
/*
* Free a Control Block
*
* Returns a previously allocated control block back to the owners
* storage pool.
*
* Arguments:
* bp pointer to block to be freed
*
* Returns:
* none
*
*/
void
atm_free(bp)
void *bp;
{
struct sp_info *sip;
struct sp_chunk *scp;
struct sp_link *slp;
int s = splnet();
/*
* Get containing chunk and pool info
*/
slp = (struct sp_link *)bp;
slp--;
scp = slp->sl_u.slu_chunk;
if (scp->sc_magic != SPOOL_MAGIC)
panic("atm_free: chunk magic missing");
sip = scp->sc_info;
/*
* Add block to free chain
*/
if (scp->sc_freeh) {
scp->sc_freet->sl_u.slu_next = slp;
scp->sc_freet = slp;
} else
scp->sc_freeh = scp->sc_freet = slp;
slp->sl_u.slu_next = NULL;
sip->si_free++;
scp->sc_used--;
(void) splx(s);
return;
}
/*
* Storage Pool Compaction
*
* Called periodically in order to perform compaction of the
* storage pools. Each pool will be checked to see if any chunks
* can be freed, taking some care to avoid freeing too many chunks
* in order to avoid memory thrashing.
*
* Called at splnet.
*
* Arguments:
* tip pointer to timer control block (atm_compactimer)
*
* Returns:
* none
*
*/
static void
atm_compact(tip)
struct atm_time *tip;
{
struct sp_info *sip;
struct sp_chunk *scp;
int i;
struct sp_chunk *scp_prev;
/*
* Check out all storage pools
*/
for (sip = atm_pool_head; sip; sip = sip->si_next) {
/*
* Always keep a minimum number of chunks around
*/
if (sip->si_chunks <= SPOOL_MIN_CHUNK)
continue;
/*
* Maximum chunks to free at one time will leave
* pool with at least 50% utilization, but never
* go below minimum chunk count.
*/
i = ((sip->si_free * 2) - sip->si_total) / sip->si_blkcnt;
i = MIN(i, sip->si_chunks - SPOOL_MIN_CHUNK);
/*
* Look for chunks to free
*/
scp_prev = NULL;
for (scp = sip->si_poolh; scp && i > 0; ) {
if (scp->sc_used == 0) {
/*
* Found a chunk to free, so do it
*/
if (scp_prev) {
scp_prev->sc_next = scp->sc_next;
if (sip->si_poolt == scp)
sip->si_poolt = scp_prev;
} else
sip->si_poolh = scp->sc_next;
KM_FREE((caddr_t)scp, sip->si_chunksiz,
M_DEVBUF);
/*
* Update pool controls
*/
sip->si_chunks--;
sip->si_total -= sip->si_blkcnt;
sip->si_free -= sip->si_blkcnt;
i--;
if (scp_prev)
scp = scp_prev->sc_next;
else
scp = sip->si_poolh;
} else {
scp_prev = scp;
scp = scp->sc_next;
}
}
}
/*
* Restart the compaction timer
*/
atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
return;
}
/*
* Release a Storage Pool
*
* Frees all dynamic storage acquired for a storage pool.
* This function is normally called just prior to a module's unloading.
*
* Arguments:
* sip pointer to sp_info for storage pool
*
* Returns:
* none
*
*/
void
atm_release_pool(sip)
struct sp_info *sip;
{
struct sp_chunk *scp, *scp_next;
int s = splnet();
/*
* Free each chunk in pool
*/
for (scp = sip->si_poolh; scp; scp = scp_next) {
/*
* Check for memory leaks
*/
if (scp->sc_used)
panic("atm_release_pool: unfreed blocks");
scp_next = scp->sc_next;
KM_FREE((caddr_t)scp, sip->si_chunksiz, M_DEVBUF);
}
/*
* Update pool controls
*/
sip->si_poolh = NULL;
sip->si_chunks = 0;
sip->si_total = 0;
sip->si_free = 0;
/*
* Unlink pool from active chain
*/
sip->si_chunksiz = 0;
UNLINK(sip, struct sp_info, atm_pool_head, si_next);
(void) splx(s);
return;
}
/*
* Handle timer tick expiration
*
* Decrement tick count in first block on timer queue. If there
* are blocks with expired timers, call their timeout function.
* This function is called ATM_HZ times per second.
*
* Arguments:
* arg argument passed on timeout() call
*
* Returns:
* none
*
*/
static KTimeout_ret
atm_timexp(arg)
void *arg;
{
struct atm_time *tip;
int s = splimp();
/*
* Decrement tick count
*/
if (((tip = atm_timeq) == NULL) || (--tip->ti_ticks > 0)) {
goto restart;
}
/*
* Stack queue should have been drained
*/
#ifdef DIAGNOSTIC
if (atm_stackq_head != NULL)
panic("atm_timexp: stack queue not empty");
#endif
/*
* Dispatch expired timers
*/
while (((tip = atm_timeq) != NULL) && (tip->ti_ticks == 0)) {
void (*func)__P((struct atm_time *));
/*
* Remove expired block from queue
*/
atm_timeq = tip->ti_next;
tip->ti_flag &= ~TIF_QUEUED;
/*
* Call timeout handler (with network interrupts locked out)
*/
func = tip->ti_func;
(void) splx(s);
s = splnet();
(*func)(tip);
(void) splx(s);
s = splimp();
/*
* Drain any deferred calls
*/
STACK_DRAIN();
}
restart:
/*
* Restart the timer
*/
(void) splx(s);
(void) timeout(atm_timexp, (void *)0, hz/ATM_HZ);
return;
}
/*
* Schedule a control block timeout
*
* Place the supplied timer control block on the timer queue. The
* function (func) will be called in 't' timer ticks with the
* control block address as its only argument. There are ATM_HZ
* timer ticks per second. The ticks value stored in each block is
* a delta of the number of ticks from the previous block in the queue.
* Thus, for each tick interval, only the first block in the queue
* needs to have its tick value decremented.
*
* Arguments:
* tip pointer to timer control block
* t number of timer ticks until expiration
* func pointer to function to call at expiration
*
* Returns:
* none
*
*/
void
atm_timeout(tip, t, func)
struct atm_time *tip;
int t;
void (*func)__P((struct atm_time *));
{
struct atm_time *tip1, *tip2;
int s;
/*
* Check for double queueing error
*/
if (tip->ti_flag & TIF_QUEUED)
panic("atm_timeout: double queueing");
/*
* Make sure we delay at least a little bit
*/
if (t <= 0)
t = 1;
/*
* Find out where we belong on the queue
*/
s = splimp();
for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2->ti_ticks <= t);
tip1 = tip2, tip2 = tip1->ti_next) {
t -= tip2->ti_ticks;
}
/*
* Place ourselves on queue and update timer deltas
*/
if (tip1 == NULL)
atm_timeq = tip;
else
tip1->ti_next = tip;
tip->ti_next = tip2;
if (tip2)
tip2->ti_ticks -= t;
/*
* Setup timer block
*/
tip->ti_flag |= TIF_QUEUED;
tip->ti_ticks = t;
tip->ti_func = func;
(void) splx(s);
return;
}
/*
* Cancel a timeout
*
* Remove the supplied timer control block from the timer queue.
*
* Arguments:
* tip pointer to timer control block
*
* Returns:
* 0 control block successfully dequeued
* 1 control block not on timer queue
*
*/
int
atm_untimeout(tip)
struct atm_time *tip;
{
struct atm_time *tip1, *tip2;
int s;
/*
* Is control block queued?
*/
if ((tip->ti_flag & TIF_QUEUED) == 0)
return(1);
/*
* Find control block on the queue
*/
s = splimp();
for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2 != tip);
tip1 = tip2, tip2 = tip1->ti_next) {
}
if (tip2 == NULL) {
(void) splx(s);
return (1);
}
/*
* Remove block from queue and update timer deltas
*/
tip2 = tip->ti_next;
if (tip1 == NULL)
atm_timeq = tip2;
else
tip1->ti_next = tip2;
if (tip2)
tip2->ti_ticks += tip->ti_ticks;
/*
* Reset timer block
*/
tip->ti_flag &= ~TIF_QUEUED;
(void) splx(s);
return (0);
}
/*
* Queue a Stack Call
*
* Queues a stack call which must be deferred to the global stack queue.
* The call parameters are stored in entries which are allocated from the
* stack queue storage pool.
*
* Arguments:
* cmd stack command
* func destination function
* token destination layer's token
* cvp pointer to connection vcc
* arg1 command argument
* arg2 command argument
*
* Returns:
* 0 call queued
* errno call not queued - reason indicated
*
*/
int
atm_stack_enq(cmd, func, token, cvp, arg1, arg2)
int cmd;
void (*func)__P((int, void *, int, int));
void *token;
Atm_connvc *cvp;
int arg1;
int arg2;
{
struct stackq_entry *sqp;
int s = splnet();
/*
* Get a new queue entry for this call
*/
sqp = (struct stackq_entry *)atm_allocate(&atm_stackq_pool);
if (sqp == NULL) {
(void) splx(s);
return (ENOMEM);
}
/*
* Fill in new entry
*/
sqp->sq_next = NULL;
sqp->sq_cmd = cmd;
sqp->sq_func = func;
sqp->sq_token = token;
sqp->sq_arg1 = arg1;
sqp->sq_arg2 = arg2;
sqp->sq_connvc = cvp;
/*
* Put new entry at end of queue
*/
if (atm_stackq_head == NULL)
atm_stackq_head = sqp;
else
atm_stackq_tail->sq_next = sqp;
atm_stackq_tail = sqp;
(void) splx(s);
return (0);
}
/*
* Drain the Stack Queue
*
* Dequeues and processes entries from the global stack queue.
*
* Arguments:
* none
*
* Returns:
* none
*
*/
void
atm_stack_drain()
{
struct stackq_entry *sqp, *qprev, *qnext;
int s = splnet();
int cnt;
/*
* Loop thru entire queue until queue is empty
* (but panic rather loop forever)
*/
do {
cnt = 0;
qprev = NULL;
for (sqp = atm_stackq_head; sqp; ) {
/*
* Got an eligible entry, do STACK_CALL stuff
*/
if (sqp->sq_cmd & STKCMD_UP) {
if (sqp->sq_connvc->cvc_downcnt) {
/*
* Cant process now, skip it
*/
qprev = sqp;
sqp = sqp->sq_next;
continue;
}
/*
* OK, dispatch the call
*/
sqp->sq_connvc->cvc_upcnt++;
(*sqp->sq_func)(sqp->sq_cmd,
sqp->sq_token,
sqp->sq_arg1,
sqp->sq_arg2);
sqp->sq_connvc->cvc_upcnt--;
} else {
if (sqp->sq_connvc->cvc_upcnt) {
/*
* Cant process now, skip it
*/
qprev = sqp;
sqp = sqp->sq_next;
continue;
}
/*
* OK, dispatch the call
*/
sqp->sq_connvc->cvc_downcnt++;
(*sqp->sq_func)(sqp->sq_cmd,
sqp->sq_token,
sqp->sq_arg1,
sqp->sq_arg2);
sqp->sq_connvc->cvc_downcnt--;
}
/*
* Dequeue processed entry and free it
*/
cnt++;
qnext = sqp->sq_next;
if (qprev)
qprev->sq_next = qnext;
else
atm_stackq_head = qnext;
if (qnext == NULL)
atm_stackq_tail = qprev;
atm_free((caddr_t)sqp);
sqp = qnext;
}
} while (cnt > 0);
/*
* Make sure entire queue was drained
*/
if (atm_stackq_head != NULL)
panic("atm_stack_drain: Queue not emptied");
(void) splx(s);
}
/*
* Process Interrupt Queue
*
* Processes entries on the ATM interrupt queue. This queue is used by
* device interface drivers in order to schedule events from the driver's
* lower (interrupt) half to the driver's stack services.
*
* The interrupt routines must store the stack processing function to call
* and a token (typically a driver/stack control block) at the front of the
* queued buffer. We assume that the function pointer and token values are
* both contained (and properly aligned) in the first buffer of the chain.
*
* Arguments:
* none
*
* Returns:
* none
*
*/
void
atm_intr()
{
KBuffer *m;
caddr_t cp;
atm_intr_func_t func;
void *token;
int s;
for (; ; ) {
/*
* Get next buffer from queue
*/
s = splimp();
IF_DEQUEUE(&atm_intrq, m);
(void) splx(s);
if (m == NULL)
break;
/*
* Get function to call and token value
*/
KB_DATASTART(m, cp, caddr_t);
func = *(atm_intr_func_t *)cp;
cp += sizeof(func);
token = *(void **)cp;
KB_HEADADJ(m, -(sizeof(func) + sizeof(token)));
if (KB_LEN(m) == 0) {
KBuffer *m1;
KB_UNLINKHEAD(m, m1);
m = m1;
}
/*
* Call processing function
*/
(*func)(token, m);
/*
* Drain any deferred calls
*/
STACK_DRAIN();
}
}
#ifdef __FreeBSD__
NETISR_SET(NETISR_ATM, atm_intr);
#endif
/*
* Print a pdu buffer chain
*
* Arguments:
* m pointer to pdu buffer chain
* msg pointer to message header string
*
* Returns:
* none
*
*/
void
atm_pdu_print(m, msg)
KBuffer *m;
char *msg;
{
caddr_t cp;
int i;
char c = ' ';
printf("%s:", msg);
while (m) {
KB_DATASTART(m, cp, caddr_t);
printf("%cbfr=0x%x data=0x%x len=%d: ",
c, (int)m, (int)cp, KB_LEN(m));
c = '\t';
if (atm_print_data) {
for (i = 0; i < KB_LEN(m); i++) {
printf("%2x ", (u_char)*cp++);
}
printf("<end_bfr>\n");
} else {
printf("\n");
}
m = KB_NEXT(m);
}
}