diff options
Diffstat (limited to 'sys/netatm/atm_subr.c')
-rw-r--r-- | sys/netatm/atm_subr.c | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/sys/netatm/atm_subr.c b/sys/netatm/atm_subr.c new file mode 100644 index 000000000000..40fc433d3d5b --- /dev/null +++ b/sys/netatm/atm_subr.c @@ -0,0 +1,970 @@ +/* + * + * =================================== + * 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); + } +} + |