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