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