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