]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netatm/atm_subr.c
Clean up some loose ends in the network code, including the X.25 and ISO
[FreeBSD/FreeBSD.git] / sys / netatm / atm_subr.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD$
27  *
28  */
29
30 /*
31  * Core ATM Services
32  * -----------------
33  *
34  * Miscellaneous ATM subroutines
35  *
36  */
37
38 #include <netatm/kern_include.h>
39 #include <net/intrq.h>
40
41 #ifndef lint
42 __RCSID("@(#) $FreeBSD$");
43 #endif
44
45
46 /*
47  * Global variables
48  */
49 struct atm_pif          *atm_interface_head = NULL;
50 struct atm_ncm          *atm_netconv_head = NULL;
51 Atm_endpoint            *atm_endpoints[ENDPT_MAX+1] = {NULL};
52 struct sp_info          *atm_pool_head = NULL;
53 struct stackq_entry     *atm_stackq_head = NULL, *atm_stackq_tail;
54 #ifdef sgi
55 int                     atm_intr_index;
56 #endif
57 struct atm_sock_stat    atm_sock_stat = { { 0 } };
58 int                     atm_init = 0;
59 int                     atm_debug = 0;
60 int                     atm_dev_print = 0;
61 int                     atm_print_data = 0;
62 int                     atm_version = ATM_VERSION;
63 struct timeval          atm_debugtime = {0, 0};
64 const int               atmintrq_present = 1;
65
66 struct sp_info  atm_attributes_pool = {
67         "atm attributes pool",          /* si_name */
68         sizeof(Atm_attributes),         /* si_blksiz */
69         10,                             /* si_blkcnt */
70         100                             /* si_maxallow */
71 };
72
73
74 /*
75  * Local functions
76  */
77 static void     atm_compact __P((struct atm_time *));
78 static KTimeout_ret     atm_timexp __P((void *));
79
80 /*
81  * Local variables
82  */
83 static struct atm_time  *atm_timeq = NULL;
84 static struct atm_time  atm_compactimer = {0, 0};
85
86 static struct sp_info   atm_stackq_pool = {
87         "Service stack queue pool",     /* si_name */
88         sizeof(struct stackq_entry),    /* si_blksiz */
89         10,                             /* si_blkcnt */
90         10                              /* si_maxallow */
91 };
92
93
94 /*
95  * Initialize ATM kernel
96  * 
97  * Performs any initialization required before things really get underway.
98  * Called from ATM domain initialization or from first registration function 
99  * which gets called.
100  *
101  * Arguments:
102  *      none
103  *
104  * Returns:
105  *      none
106  *
107  */
108 void
109 atm_initialize()
110 {
111         /*
112          * Never called from interrupts, so no locking needed
113          */
114         if (atm_init)
115                 return;
116         atm_init = 1;
117
118 #ifndef __FreeBSD__
119         /*
120          * Add ATM protocol family
121          */
122         (void) protocol_family(&atmdomain, NULL, NULL);
123 #endif
124
125         atm_intrq.ifq_maxlen = ATM_INTRQ_MAX;
126 #ifdef sgi
127         atm_intr_index = register_isr(atm_intr);
128 #endif
129 #ifdef __FreeBSD__
130         register_netisr(NETISR_ATM, atm_intr);
131 #endif
132
133         /*
134          * Initialize subsystems
135          */
136         atm_aal5_init();
137
138         /*
139          * Prime the timer
140          */
141         (void) timeout(atm_timexp, (void *)0, hz/ATM_HZ);
142
143         /*
144          * Start the compaction timer
145          */
146         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
147 }
148
149
150 /*
151  * Allocate a Control Block
152  * 
153  * Gets a new control block allocated from the specified storage pool, 
154  * acquiring memory for new pool chunks if required.  The returned control
155  * block's contents will be cleared.
156  *
157  * Arguments:
158  *      sip     pointer to sp_info for storage pool
159  *
160  * Returns:
161  *      addr    pointer to allocated control block
162  *      0       allocation failed
163  *
164  */
165 void *
166 atm_allocate(sip)
167         struct sp_info  *sip;
168 {
169         void            *bp;
170         struct sp_chunk *scp;
171         struct sp_link  *slp;
172         int             s = splnet();
173
174         /*
175          * Count calls
176          */
177         sip->si_allocs++;
178
179         /*
180          * Are there any free in the pool?
181          */
182         if (sip->si_free) {
183
184                 /*
185                  * Find first chunk with a free block
186                  */
187                 for (scp = sip->si_poolh; scp; scp = scp->sc_next) {
188                         if (scp->sc_freeh != NULL)
189                                 break;
190                 }
191
192         } else {
193
194                 /*
195                  * No free blocks - have to allocate a new
196                  * chunk (but put a limit to this)
197                  */
198                 struct sp_link  *slp_next;
199                 int     i;
200
201                 /*
202                  * First time for this pool??
203                  */
204                 if (sip->si_chunksiz == 0) {
205                         size_t  n;
206
207                         /*
208                          * Initialize pool information
209                          */
210                         n = sizeof(struct sp_chunk) +
211                                 sip->si_blkcnt * 
212                                 (sip->si_blksiz + sizeof(struct sp_link));
213                         sip->si_chunksiz = roundup(n, SPOOL_ROUNDUP);
214
215                         /*
216                          * Place pool on kernel chain
217                          */
218                         LINK2TAIL(sip, struct sp_info, atm_pool_head, si_next);
219                 }
220
221                 if (sip->si_chunks >= sip->si_maxallow) {
222                         sip->si_fails++;
223                         (void) splx(s);
224                         return (NULL);
225                 }
226
227                 scp = (struct sp_chunk *)
228                         KM_ALLOC(sip->si_chunksiz, M_DEVBUF, M_NOWAIT);
229                 if (scp == NULL) {
230                         sip->si_fails++;
231                         (void) splx(s);
232                         return (NULL);
233                 }
234                 scp->sc_next = NULL;
235                 scp->sc_info = sip;
236                 scp->sc_magic = SPOOL_MAGIC;
237                 scp->sc_used = 0;
238
239                 /*
240                  * Divy up chunk into free blocks
241                  */
242                 slp = (struct sp_link *)(scp + 1);
243                 scp->sc_freeh = slp;
244
245                 for (i = sip->si_blkcnt; i > 1; i--) { 
246                         slp_next = (struct sp_link *)((caddr_t)(slp + 1) + 
247                                         sip->si_blksiz);
248                         slp->sl_u.slu_next = slp_next;
249                         slp = slp_next;
250                 }
251                 slp->sl_u.slu_next = NULL;
252                 scp->sc_freet = slp;
253
254                 /*
255                  * Add new chunk to end of pool
256                  */
257                 if (sip->si_poolh)
258                         sip->si_poolt->sc_next = scp;
259                 else
260                         sip->si_poolh = scp;
261                 sip->si_poolt = scp;
262                 
263                 sip->si_chunks++;
264                 sip->si_total += sip->si_blkcnt;
265                 sip->si_free += sip->si_blkcnt;
266                 if (sip->si_chunks > sip->si_maxused)
267                         sip->si_maxused = sip->si_chunks;
268         }
269
270         /*
271          * Allocate the first free block in chunk
272          */
273         slp = scp->sc_freeh;
274         scp->sc_freeh = slp->sl_u.slu_next;
275         scp->sc_used++;
276         sip->si_free--;
277         bp = (slp + 1);
278
279         /*
280          * Save link back to pool chunk
281          */
282         slp->sl_u.slu_chunk = scp;
283
284         /*
285          * Clear out block
286          */
287         KM_ZERO(bp, sip->si_blksiz);
288
289         (void) splx(s);
290         return (bp);
291 }
292
293
294 /*
295  * Free a Control Block
296  * 
297  * Returns a previously allocated control block back to the owners 
298  * storage pool.  
299  *
300  * Arguments:
301  *      bp      pointer to block to be freed
302  *
303  * Returns:
304  *      none
305  *
306  */
307 void
308 atm_free(bp)
309         void            *bp;
310 {
311         struct sp_info  *sip;
312         struct sp_chunk *scp;
313         struct sp_link  *slp;
314         int             s = splnet();
315
316         /*
317          * Get containing chunk and pool info
318          */
319         slp = (struct sp_link *)bp;
320         slp--;
321         scp = slp->sl_u.slu_chunk;
322         if (scp->sc_magic != SPOOL_MAGIC)
323                 panic("atm_free: chunk magic missing");
324         sip = scp->sc_info;
325
326         /*
327          * Add block to free chain
328          */
329         if (scp->sc_freeh) {
330                 scp->sc_freet->sl_u.slu_next = slp;
331                 scp->sc_freet = slp;
332         } else
333                 scp->sc_freeh = scp->sc_freet = slp;
334         slp->sl_u.slu_next = NULL;
335         sip->si_free++;
336         scp->sc_used--;
337
338         (void) splx(s);
339         return;
340 }
341
342
343 /*
344  * Storage Pool Compaction
345  * 
346  * Called periodically in order to perform compaction of the
347  * storage pools.  Each pool will be checked to see if any chunks 
348  * can be freed, taking some care to avoid freeing too many chunks
349  * in order to avoid memory thrashing.
350  *
351  * Called at splnet.
352  *
353  * Arguments:
354  *      tip     pointer to timer control block (atm_compactimer)
355  *
356  * Returns:
357  *      none
358  *
359  */
360 static void
361 atm_compact(tip)
362         struct atm_time *tip;
363 {
364         struct sp_info  *sip;
365         struct sp_chunk *scp;
366         int             i;
367         struct sp_chunk *scp_prev;
368
369         /*
370          * Check out all storage pools
371          */
372         for (sip = atm_pool_head; sip; sip = sip->si_next) {
373
374                 /*
375                  * Always keep a minimum number of chunks around
376                  */
377                 if (sip->si_chunks <= SPOOL_MIN_CHUNK)
378                         continue;
379
380                 /*
381                  * Maximum chunks to free at one time will leave
382                  * pool with at least 50% utilization, but never
383                  * go below minimum chunk count.
384                  */
385                 i = ((sip->si_free * 2) - sip->si_total) / sip->si_blkcnt;
386                 i = MIN(i, sip->si_chunks - SPOOL_MIN_CHUNK);
387
388                 /*
389                  * Look for chunks to free
390                  */
391                 scp_prev = NULL;
392                 for (scp = sip->si_poolh; scp && i > 0; ) {
393
394                         if (scp->sc_used == 0) {
395
396                                 /*
397                                  * Found a chunk to free, so do it
398                                  */
399                                 if (scp_prev) {
400                                         scp_prev->sc_next = scp->sc_next;
401                                         if (sip->si_poolt == scp)
402                                                 sip->si_poolt = scp_prev;
403                                 } else
404                                         sip->si_poolh = scp->sc_next;
405
406                                 KM_FREE((caddr_t)scp, sip->si_chunksiz,
407                                         M_DEVBUF);
408
409                                 /*
410                                  * Update pool controls
411                                  */
412                                 sip->si_chunks--;
413                                 sip->si_total -= sip->si_blkcnt;
414                                 sip->si_free -= sip->si_blkcnt;
415                                 i--;
416                                 if (scp_prev)
417                                         scp = scp_prev->sc_next;
418                                 else
419                                         scp = sip->si_poolh;
420                         } else {
421                                 scp_prev = scp;
422                                 scp = scp->sc_next;
423                         }
424                 }
425         }
426
427         /*
428          * Restart the compaction timer
429          */
430         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
431
432         return;
433 }
434
435
436 /*
437  * Release a Storage Pool
438  * 
439  * Frees all dynamic storage acquired for a storage pool.
440  * This function is normally called just prior to a module's unloading.
441  *
442  * Arguments:
443  *      sip     pointer to sp_info for storage pool
444  *
445  * Returns:
446  *      none
447  *
448  */
449 void
450 atm_release_pool(sip)
451         struct sp_info  *sip;
452 {
453         struct sp_chunk *scp, *scp_next;
454         int             s = splnet();
455
456         /*
457          * Free each chunk in pool
458          */
459         for (scp = sip->si_poolh; scp; scp = scp_next) {
460
461                 /*
462                  * Check for memory leaks
463                  */
464                 if (scp->sc_used)
465                         panic("atm_release_pool: unfreed blocks");
466
467                 scp_next = scp->sc_next;
468
469                 KM_FREE((caddr_t)scp, sip->si_chunksiz, M_DEVBUF);
470         }
471
472         /*
473          * Update pool controls
474          */
475         sip->si_poolh = NULL;
476         sip->si_chunks = 0;
477         sip->si_total = 0;
478         sip->si_free = 0;
479
480         /*
481          * Unlink pool from active chain
482          */
483         sip->si_chunksiz = 0;
484         UNLINK(sip, struct sp_info, atm_pool_head, si_next);
485
486         (void) splx(s);
487         return;
488 }
489
490
491 /*
492  * Handle timer tick expiration
493  * 
494  * Decrement tick count in first block on timer queue.  If there
495  * are blocks with expired timers, call their timeout function.
496  * This function is called ATM_HZ times per second.
497  *
498  * Arguments:
499  *      arg     argument passed on timeout() call
500  *
501  * Returns:
502  *      none
503  *
504  */
505 static KTimeout_ret
506 atm_timexp(arg)
507         void    *arg;
508 {
509         struct atm_time *tip;
510         int             s = splimp();
511
512
513         /*
514          * Decrement tick count
515          */
516         if (((tip = atm_timeq) == NULL) || (--tip->ti_ticks > 0)) {
517                 goto restart;
518         }
519
520         /*
521          * Stack queue should have been drained
522          */
523 #ifdef DIAGNOSTIC
524         if (atm_stackq_head != NULL)
525                 panic("atm_timexp: stack queue not empty");
526 #endif
527
528         /*
529          * Dispatch expired timers
530          */
531         while (((tip = atm_timeq) != NULL) && (tip->ti_ticks == 0)) {
532                 void    (*func)__P((struct atm_time *));
533
534                 /*
535                  * Remove expired block from queue
536                  */
537                 atm_timeq = tip->ti_next;
538                 tip->ti_flag &= ~TIF_QUEUED;
539
540                 /*
541                  * Call timeout handler (with network interrupts locked out)
542                  */
543                 func = tip->ti_func;
544                 (void) splx(s);
545                 s = splnet();
546                 (*func)(tip);
547                 (void) splx(s);
548                 s = splimp();
549
550                 /*
551                  * Drain any deferred calls
552                  */
553                 STACK_DRAIN();
554         }
555
556 restart:
557         /*
558          * Restart the timer
559          */
560         (void) splx(s);
561         (void) timeout(atm_timexp, (void *)0, hz/ATM_HZ);
562
563         return;
564 }
565
566
567 /*
568  * Schedule a control block timeout
569  * 
570  * Place the supplied timer control block on the timer queue.  The
571  * function (func) will be called in 't' timer ticks with the
572  * control block address as its only argument.  There are ATM_HZ
573  * timer ticks per second.  The ticks value stored in each block is
574  * a delta of the number of ticks from the previous block in the queue.
575  * Thus, for each tick interval, only the first block in the queue 
576  * needs to have its tick value decremented.
577  *
578  * Arguments:
579  *      tip     pointer to timer control block
580  *      t       number of timer ticks until expiration
581  *      func    pointer to function to call at expiration 
582  *
583  * Returns:
584  *      none
585  *
586  */
587 void
588 atm_timeout(tip, t, func)
589         struct atm_time *tip;
590         int             t;
591         void            (*func)__P((struct atm_time *));
592 {
593         struct atm_time *tip1, *tip2;
594         int             s;
595
596
597         /*
598          * Check for double queueing error
599          */
600         if (tip->ti_flag & TIF_QUEUED)
601                 panic("atm_timeout: double queueing");
602
603         /*
604          * Make sure we delay at least a little bit
605          */
606         if (t <= 0)
607                 t = 1;
608
609         /*
610          * Find out where we belong on the queue
611          */
612         s = splimp();
613         for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2->ti_ticks <= t); 
614                                             tip1 = tip2, tip2 = tip1->ti_next) {
615                 t -= tip2->ti_ticks;
616         }
617
618         /*
619          * Place ourselves on queue and update timer deltas
620          */
621         if (tip1 == NULL)
622                 atm_timeq = tip;
623         else
624                 tip1->ti_next = tip;
625         tip->ti_next = tip2;
626
627         if (tip2)
628                 tip2->ti_ticks -= t;
629         
630         /*
631          * Setup timer block
632          */
633         tip->ti_flag |= TIF_QUEUED;
634         tip->ti_ticks = t;
635         tip->ti_func = func;
636
637         (void) splx(s);
638         return;
639 }
640
641
642 /*
643  * Cancel a timeout
644  * 
645  * Remove the supplied timer control block from the timer queue.
646  *
647  * Arguments:
648  *      tip     pointer to timer control block
649  *
650  * Returns:
651  *      0       control block successfully dequeued
652  *      1       control block not on timer queue
653  *
654  */
655 int
656 atm_untimeout(tip)
657         struct atm_time *tip;
658 {
659         struct atm_time *tip1, *tip2;
660         int             s;
661
662         /*
663          * Is control block queued?
664          */
665         if ((tip->ti_flag & TIF_QUEUED) == 0)
666                 return(1);
667
668         /*
669          * Find control block on the queue
670          */
671         s = splimp();
672         for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2 != tip); 
673                                             tip1 = tip2, tip2 = tip1->ti_next) {
674         }
675
676         if (tip2 == NULL) {
677                 (void) splx(s);
678                 return (1);
679         }
680
681         /*
682          * Remove block from queue and update timer deltas
683          */
684         tip2 = tip->ti_next;
685         if (tip1 == NULL)
686                 atm_timeq = tip2;
687         else
688                 tip1->ti_next = tip2;
689
690         if (tip2)
691                 tip2->ti_ticks += tip->ti_ticks;
692         
693         /*
694          * Reset timer block
695          */
696         tip->ti_flag &= ~TIF_QUEUED;
697
698         (void) splx(s);
699         return (0);
700 }
701
702
703 /*
704  * Queue a Stack Call 
705  * 
706  * Queues a stack call which must be deferred to the global stack queue.
707  * The call parameters are stored in entries which are allocated from the
708  * stack queue storage pool.
709  *
710  * Arguments:
711  *      cmd     stack command
712  *      func    destination function
713  *      token   destination layer's token
714  *      cvp     pointer to  connection vcc
715  *      arg1    command argument
716  *      arg2    command argument
717  *
718  * Returns:
719  *      0       call queued
720  *      errno   call not queued - reason indicated
721  *
722  */
723 int
724 atm_stack_enq(cmd, func, token, cvp, arg1, arg2)
725         int             cmd;
726         void            (*func)__P((int, void *, int, int));
727         void            *token;
728         Atm_connvc      *cvp;
729         int             arg1;
730         int             arg2;
731 {
732         struct stackq_entry     *sqp;
733         int             s = splnet();
734
735         /*
736          * Get a new queue entry for this call
737          */
738         sqp = (struct stackq_entry *)atm_allocate(&atm_stackq_pool);
739         if (sqp == NULL) {
740                 (void) splx(s);
741                 return (ENOMEM);
742         }
743
744         /*
745          * Fill in new entry
746          */
747         sqp->sq_next = NULL;
748         sqp->sq_cmd = cmd;
749         sqp->sq_func = func;
750         sqp->sq_token = token;
751         sqp->sq_arg1 = arg1;
752         sqp->sq_arg2 = arg2;
753         sqp->sq_connvc = cvp;
754
755         /*
756          * Put new entry at end of queue
757          */
758         if (atm_stackq_head == NULL)
759                 atm_stackq_head = sqp;
760         else
761                 atm_stackq_tail->sq_next = sqp;
762         atm_stackq_tail = sqp;
763
764         (void) splx(s);
765         return (0);
766 }
767
768
769 /*
770  * Drain the Stack Queue
771  * 
772  * Dequeues and processes entries from the global stack queue.  
773  *
774  * Arguments:
775  *      none
776  *
777  * Returns:
778  *      none
779  *
780  */
781 void
782 atm_stack_drain()
783 {
784         struct stackq_entry     *sqp, *qprev, *qnext;
785         int             s = splnet();
786         int             cnt;
787
788         /*
789          * Loop thru entire queue until queue is empty
790          *      (but panic rather loop forever)
791          */
792         do {
793                 cnt = 0;
794                 qprev = NULL;
795                 for (sqp = atm_stackq_head; sqp; ) {
796
797                         /*
798                          * Got an eligible entry, do STACK_CALL stuff
799                          */
800                         if (sqp->sq_cmd & STKCMD_UP) {
801                                 if (sqp->sq_connvc->cvc_downcnt) {
802
803                                         /*
804                                          * Cant process now, skip it
805                                          */
806                                         qprev = sqp;
807                                         sqp = sqp->sq_next;
808                                         continue;
809                                 }
810
811                                 /*
812                                  * OK, dispatch the call
813                                  */
814                                 sqp->sq_connvc->cvc_upcnt++;
815                                 (*sqp->sq_func)(sqp->sq_cmd, 
816                                                 sqp->sq_token,
817                                                 sqp->sq_arg1,
818                                                 sqp->sq_arg2);
819                                 sqp->sq_connvc->cvc_upcnt--;
820                         } else {
821                                 if (sqp->sq_connvc->cvc_upcnt) {
822
823                                         /*
824                                          * Cant process now, skip it
825                                          */
826                                         qprev = sqp;
827                                         sqp = sqp->sq_next;
828                                         continue;
829                                 }
830
831                                 /*
832                                  * OK, dispatch the call
833                                  */
834                                 sqp->sq_connvc->cvc_downcnt++;
835                                 (*sqp->sq_func)(sqp->sq_cmd, 
836                                                 sqp->sq_token,
837                                                 sqp->sq_arg1,
838                                                 sqp->sq_arg2);
839                                 sqp->sq_connvc->cvc_downcnt--;
840                         }
841
842                         /*
843                          * Dequeue processed entry and free it
844                          */
845                         cnt++;
846                         qnext = sqp->sq_next;
847                         if (qprev)
848                                 qprev->sq_next = qnext;
849                         else
850                                 atm_stackq_head = qnext;
851                         if (qnext == NULL)
852                                 atm_stackq_tail = qprev;
853                         atm_free((caddr_t)sqp);
854                         sqp = qnext;
855                 }
856         } while (cnt > 0);
857
858         /*
859          * Make sure entire queue was drained
860          */
861         if (atm_stackq_head != NULL)
862                 panic("atm_stack_drain: Queue not emptied");
863
864         (void) splx(s);
865 }
866
867
868 /*
869  * Process Interrupt Queue
870  * 
871  * Processes entries on the ATM interrupt queue.  This queue is used by
872  * device interface drivers in order to schedule events from the driver's 
873  * lower (interrupt) half to the driver's stack services.
874  *
875  * The interrupt routines must store the stack processing function to call
876  * and a token (typically a driver/stack control block) at the front of the
877  * queued buffer.  We assume that the function pointer and token values are 
878  * both contained (and properly aligned) in the first buffer of the chain.
879  *
880  * Arguments:
881  *      none
882  *
883  * Returns:
884  *      none
885  *
886  */
887 void
888 atm_intr()
889 {
890         KBuffer         *m;
891         caddr_t         cp;
892         atm_intr_func_t func;
893         void            *token;
894         int             s;
895
896         for (; ; ) {
897                 /*
898                  * Get next buffer from queue
899                  */
900                 s = splimp();
901                 IF_DEQUEUE(&atm_intrq, m);
902                 (void) splx(s);
903                 if (m == NULL)
904                         break;
905
906                 /*
907                  * Get function to call and token value
908                  */
909                 KB_DATASTART(m, cp, caddr_t);
910                 func = *(atm_intr_func_t *)cp;
911                 cp += sizeof(func);
912                 token = *(void **)cp;
913                 KB_HEADADJ(m, -(sizeof(func) + sizeof(token)));
914                 if (KB_LEN(m) == 0) {
915                         KBuffer         *m1;
916                         KB_UNLINKHEAD(m, m1);
917                         m = m1;
918                 }
919
920                 /*
921                  * Call processing function
922                  */
923                 (*func)(token, m);
924
925                 /*
926                  * Drain any deferred calls
927                  */
928                 STACK_DRAIN();
929         }
930 }
931
932
933 /*
934  * Print a pdu buffer chain
935  * 
936  * Arguments:
937  *      m       pointer to pdu buffer chain
938  *      msg     pointer to message header string
939  *
940  * Returns:
941  *      none
942  *
943  */
944 void
945 atm_pdu_print(m, msg)
946         KBuffer         *m;
947         char            *msg;
948 {
949         caddr_t         cp;
950         int             i;
951         char            c = ' ';
952
953         printf("%s:", msg);
954         while (m) { 
955                 KB_DATASTART(m, cp, caddr_t);
956                 printf("%cbfr=%p data=%p len=%d: ",
957                         c, m, cp, KB_LEN(m));
958                 c = '\t';
959                 if (atm_print_data) {
960                         for (i = 0; i < KB_LEN(m); i++) {
961                                 printf("%2x ", (u_char)*cp++);
962                         }
963                         printf("<end_bfr>\n");
964                 } else {
965                         printf("\n");
966                 }
967                 m = KB_NEXT(m);
968         }
969 }
970