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