Merge branch 'openssh'
[dragonfly.git] / tools / tools / netrate / pktgen / pktgen.c
1 /*
2  * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/tools/tools/netrate/pktgen/pktgen.c,v 1.4 2008/04/02 14:18:55 sephe Exp $
35  */
36
37 #define _IP_VHL
38
39 #include <sys/param.h>
40 #include <sys/conf.h>
41 #include <sys/device.h>
42 #include <sys/in_cksum.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/proc.h>
47 #include <sys/priv.h>
48 #include <sys/queue.h>
49 #include <sys/socket.h>
50 #include <sys/systm.h>
51 #include <sys/serialize.h>
52
53 #include <net/if.h>
54 #include <net/if_dl.h>
55 #include <net/if_var.h>
56 #include <net/ifq_var.h>
57 #include <net/ethernet.h>
58 #include <net/netmsg2.h>
59 #include <net/netisr2.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_pcb.h>
63 #include <netinet/ip.h>
64 #include <netinet/udp_var.h>
65
66 #include "pktgen.h"
67
68 #define CDEV_NAME       "pktg"
69
70 #define PKTGEN_BUFSZ    2048
71
72 #ifndef PKTGEN_DEVCNT
73 #define PKTGEN_DEVCNT   4
74 #endif
75
76 struct pktgen;
77
78 struct netmsg_pktgen {
79         struct netmsg_base      np_base;
80         struct pktgen           *np_pktg;
81         struct ifaltq_subque    *np_ifsq;
82 };
83
84 struct pktgen_buf {
85         struct netmsg_base      pb_nmsg;        /* MUST BE THE FIRST */
86         void                    *pb_buf;
87         volatile int            pb_done;
88         int                     pb_inuse;
89         struct ifnet            *pb_ifp;
90         struct ifaltq_subque    *pb_ifsq;
91         int                     pb_len;
92         int                     pb_cpuid;
93         struct pktgen           *pb_pktg;
94         LIST_ENTRY(pktgen_buf)  pb_link;
95 };
96
97 struct pktgen_pcpu {
98         struct callout          pktg_stop;
99         LIST_HEAD(, pktgen_buf) pktg_buflist;
100 };
101
102 struct pktgen {
103         uint32_t                pktg_flags;     /* PKTG_F_ */
104         int                     pktg_refcnt;
105
106         int                     pktg_duration;
107
108         int                     pktg_datalen;
109         struct ifnet            *pktg_ifp;
110
111         int                     pktg_pktenq;
112
113         struct sockaddr_in      pktg_src;
114         int                     pktg_ndst;
115         struct sockaddr_in      *pktg_dst;
116         uint8_t                 pktg_dst_lladdr[ETHER_ADDR_LEN];
117
118         struct pktgen_pcpu      pktg_pcpu[MAXCPU];
119 };
120
121 #define PKTG_F_CONFIG           0x1
122 #define PKTG_F_RUNNING          0x4
123 #define PKTG_F_SWITCH_SRCDST    0x8
124
125 static int              pktgen_modevent(module_t, int, void *);
126
127 static void             pktgen_buf_free(void *);
128 static void             pktgen_buf_ref(void *);
129 static void             pktgen_buf_send(netmsg_t);
130
131 static int              pktgen_config(struct pktgen *,
132                             const struct pktgen_conf *);
133 static int              pktgen_start(struct pktgen *, int);
134 static void             pktgen_free(struct pktgen *);
135 static void             pktgen_ref(struct pktgen *);
136 static void             pktgen_pcpu_stop_cb(void *);
137 static void             pktgen_mbuf(struct pktgen_buf *, struct mbuf *);
138 static void             pktgen_start_ifsq(struct pktgen *,
139                             struct ifaltq_subque *);
140 static void             pktgen_start_ifsq_handler(netmsg_t);
141
142 static d_open_t         pktgen_open;
143 static d_close_t        pktgen_close;
144 static d_ioctl_t        pktgen_ioctl;
145
146 static struct dev_ops   pktgen_ops = {
147         { CDEV_NAME, 0, D_MPSAFE },
148         .d_open =       pktgen_open,
149         .d_close =      pktgen_close,
150         .d_ioctl =      pktgen_ioctl,
151 };
152
153 static volatile int             pktgen_refcnt;
154 static struct lwkt_token pktgen_tok = LWKT_TOKEN_INITIALIZER(pktgen_token);
155
156 MALLOC_DECLARE(M_PKTGEN);
157 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator");
158
159 DEV_MODULE(pktgen, pktgen_modevent, NULL);
160
161 static int
162 pktgen_modevent(module_t mod, int type, void *data)
163 {
164         int error = 0, i;
165
166         switch (type) {
167         case MOD_LOAD:
168                 for (i = 0; i < PKTGEN_DEVCNT; ++i) {
169                         make_dev(&pktgen_ops, 0, UID_ROOT, GID_WHEEL, 0600,
170                             CDEV_NAME"%d", i);
171                 }
172                 break;
173
174         case MOD_UNLOAD:
175                 if (pktgen_refcnt > 0)
176                         return EBUSY;
177                 dev_ops_remove_all(&pktgen_ops);
178                 break;
179
180         default:
181                 error = EOPNOTSUPP;
182                 break;
183         }
184         return error;
185 }
186
187 static int
188 pktgen_open(struct dev_open_args *ap)
189 {
190         cdev_t dev = ap->a_head.a_dev;
191         struct pktgen *pktg;
192         int error, i;
193
194         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
195         if (error)
196                 return error;
197
198         lwkt_gettoken(&pktgen_tok);
199
200         if (dev->si_drv1 != NULL) {
201                 lwkt_reltoken(&pktgen_tok);
202                 return EBUSY;
203         }
204
205         pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK);
206         for (i = 0; i < ncpus; ++i) {
207                 struct pktgen_pcpu *p = &pktg->pktg_pcpu[i];
208
209                 callout_init_mp(&p->pktg_stop);
210                 LIST_INIT(&p->pktg_buflist);
211         }
212
213         dev->si_drv1 = pktg;
214         pktg->pktg_refcnt = 1;
215
216         atomic_add_int(&pktgen_refcnt, 1);
217
218         lwkt_reltoken(&pktgen_tok);
219         return 0;
220 }
221
222 static int
223 pktgen_close(struct dev_close_args *ap)
224 {
225         cdev_t dev = ap->a_head.a_dev;
226         struct pktgen *pktg = dev->si_drv1;
227
228         lwkt_gettoken(&pktgen_tok);
229         dev->si_drv1 = NULL;
230         lwkt_reltoken(&pktgen_tok);
231
232         pktgen_free(pktg);
233
234         return 0;
235 }
236
237 static int
238 pktgen_ioctl(struct dev_ioctl_args *ap __unused)
239 {
240         cdev_t dev = ap->a_head.a_dev;
241         caddr_t data = ap->a_data;
242         struct pktgen *pktg = dev->si_drv1;
243         int error;
244
245         lwkt_gettoken(&pktgen_tok);
246
247         switch (ap->a_cmd) {
248         case PKTGENSTART:
249                 error = pktgen_start(pktg, 0);
250                 break;
251
252         case PKTGENMQSTART:
253                 error = pktgen_start(pktg, 1);
254                 break;
255
256         case PKTGENSCONF:
257                 error = pktgen_config(pktg, (const struct pktgen_conf *)data);
258                 break;
259
260         default:
261                 error = EOPNOTSUPP;
262                 break;
263         }
264
265         lwkt_reltoken(&pktgen_tok);
266         return error;
267 }
268
269 static int
270 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf)
271 {
272         const struct sockaddr_in *sin;
273         struct sockaddr_in *dst = NULL;
274         const struct sockaddr *sa;
275         struct ifnet *ifp;
276         size_t dst_size;
277         int i, error, pktenq;
278
279         if (pktg->pktg_flags & (PKTG_F_RUNNING | PKTG_F_CONFIG))
280                 return EBUSY;
281
282         if (conf->pc_datalen <= 0 ||
283             conf->pc_datalen > ETHERMTU - sizeof(struct udpiphdr))
284                 return EINVAL;
285         if (conf->pc_duration <= 0)
286                 return EINVAL;
287
288         sin = &conf->pc_src;
289         if (sin->sin_family != AF_INET)
290                 return EPROTONOSUPPORT;
291         if (sin->sin_port == 0)
292                 return EINVAL;
293
294         if (conf->pc_ndst <= 0)
295                 return EINVAL;
296         dst_size = conf->pc_ndst * sizeof(struct sockaddr_in);
297
298         dst = kmalloc(dst_size, M_PKTGEN, M_WAITOK | M_NULLOK);
299         if (dst == NULL)
300                 return ENOMEM;
301
302         error = copyin(conf->pc_dst, dst, dst_size);
303         if (error)
304                 goto failed;
305
306         for (i = 0; i < conf->pc_ndst; ++i) {
307                 sin = &dst[i];
308                 if (sin->sin_family != AF_INET) {
309                         error = EPROTONOSUPPORT;
310                         goto failed;
311                 }
312                 if (sin->sin_port == 0) {
313                         error = EINVAL;
314                         goto failed;
315                 }
316         }
317
318         ifp = ifunit(conf->pc_ifname);
319         if (ifp == NULL) {
320                 error = ENXIO;
321                 goto failed;
322         }
323
324         pktenq = conf->pc_pktenq;
325         if (pktenq < 0 || pktenq > ifp->if_snd.altq_maxlen) {
326                 error = ENOBUFS;
327                 goto failed;
328         } else if (pktenq == 0) {
329                 pktenq = (ifp->if_snd.altq_maxlen * 3) / 4;
330         }
331
332         sa = &conf->pc_dst_lladdr;
333         if (sa->sa_family != AF_LINK) {
334                 error = EPROTONOSUPPORT;
335                 goto failed;
336         }
337         if (sa->sa_len != ETHER_ADDR_LEN) {
338                 error = EPROTONOSUPPORT;
339                 goto failed;
340         }
341         if (ETHER_IS_MULTICAST(sa->sa_data) ||
342             bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
343                 error = EADDRNOTAVAIL;
344                 goto failed;
345         }
346
347         /*
348          * Accept the config
349          */
350         pktg->pktg_flags |= PKTG_F_CONFIG;
351
352         if (conf->pc_flags & PKTGEN_FLAG_SWITCH_SRCDST)
353                 pktg->pktg_flags |= PKTG_F_SWITCH_SRCDST;
354         pktg->pktg_duration = conf->pc_duration;
355         pktg->pktg_datalen = conf->pc_datalen;
356         pktg->pktg_pktenq = pktenq;
357         pktg->pktg_ifp = ifp;
358         pktg->pktg_src = conf->pc_src;
359         pktg->pktg_ndst = conf->pc_ndst;
360         KKASSERT(pktg->pktg_dst == NULL);
361         pktg->pktg_dst = dst;
362         bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN);
363
364         return 0;
365
366 failed:
367         if (dst != NULL)
368                 kfree(dst, M_PKTGEN);
369         return error;
370 }
371
372 static void
373 pktgen_start_ifsq(struct pktgen *pktg, struct ifaltq_subque *ifsq)
374 {
375         struct netmsg_pktgen *np;
376
377         np = kmalloc(sizeof(*np), M_LWKTMSG, M_WAITOK);
378         netmsg_init(&np->np_base, NULL, &netisr_afree_rport, 0,
379             pktgen_start_ifsq_handler);
380         np->np_pktg = pktg;
381         np->np_ifsq = ifsq;
382
383         lwkt_sendmsg(netisr_cpuport(ifsq_get_cpuid(ifsq)), &np->np_base.lmsg);
384 }
385
386 static int
387 pktgen_start(struct pktgen *pktg, int mq)
388 {
389         struct ifaltq *ifq;
390
391         if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0)
392                 return EINVAL;
393         if (pktg->pktg_flags & PKTG_F_RUNNING)
394                 return EBUSY;
395         pktg->pktg_flags |= PKTG_F_RUNNING;
396
397         ifq = &pktg->pktg_ifp->if_snd;
398         if (!mq) {
399                 pktgen_ref(pktg);
400                 pktgen_start_ifsq(pktg, ifq_get_subq_default(ifq));
401         } else {
402                 int i;
403
404                 for (i = 0; i < ifq->altq_subq_cnt; ++i)
405                         pktgen_ref(pktg);
406                 for (i = 0; i < ifq->altq_subq_cnt; ++i)
407                         pktgen_start_ifsq(pktg, ifq_get_subq(ifq, i));
408         }
409         return 0;
410 }
411
412 static void
413 pktgen_start_ifsq_handler(netmsg_t nmsg)
414 {
415         struct netmsg_pktgen *np = (struct netmsg_pktgen *)nmsg;
416         struct pktgen *pktg = np->np_pktg;
417         struct ifaltq_subque *ifsq = np->np_ifsq;
418
419         struct mbuf *m, *head = NULL, **next;
420         struct ifnet *ifp;
421         struct pktgen_pcpu *p;
422         int cpuid, i, alloc_cnt, keep_cnt;
423
424         u_short ulen, psum;
425         int len, ip_len;
426
427         /* Reply ASAP */
428         lwkt_replymsg(&np->np_base.lmsg, 0);
429
430         ifp = pktg->pktg_ifp;
431
432         cpuid = ifsq_get_cpuid(ifsq);
433         KKASSERT(cpuid == mycpuid);
434
435         p = &pktg->pktg_pcpu[cpuid];
436
437         keep_cnt = pktg->pktg_pktenq;
438         alloc_cnt = keep_cnt * 2;
439
440         /*
441          * Prefault enough mbuf into mbuf objcache
442          */
443         next = &head;
444         for (i = 0; i < alloc_cnt; ++i) {
445                 MGETHDR(m, MB_WAIT, MT_DATA);
446                 *next = m;
447                 next = &m->m_nextpkt;
448         }
449
450         for (i = 0; i < alloc_cnt - keep_cnt; ++i) {
451                 m = head;
452                 head = m->m_nextpkt;
453                 m->m_nextpkt = NULL;
454                 m_freem(m);
455         }
456         KKASSERT(head != NULL);
457
458         /*
459          * Setup the packets' data
460          */
461         ip_len = pktg->pktg_datalen + sizeof(struct udpiphdr);
462         len = ip_len + ETHER_HDR_LEN;
463
464         psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) +
465             IPPROTO_UDP);
466         ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr));
467
468         m = head;
469         i = 0;
470         while (m != NULL) {
471                 struct mbuf *nextm;
472                 const struct sockaddr_in *dst;
473                 struct pktgen_buf *pb;
474                 struct ip *ip;
475                 struct udpiphdr *ui;
476                 struct ether_header *eh;
477
478                 pktgen_ref(pktg);
479
480                 pb = kmalloc(sizeof(*pb), M_PKTGEN, M_WAITOK | M_ZERO);
481                 pb->pb_ifp = ifp;
482                 pb->pb_ifsq = ifsq;
483                 pb->pb_inuse = 1;
484                 pb->pb_buf = kmalloc(PKTGEN_BUFSZ, M_PKTGEN, M_WAITOK);
485                 pb->pb_len = len;
486                 pb->pb_cpuid = cpuid;
487                 pb->pb_pktg = pktg;
488                 netmsg_init(&pb->pb_nmsg, NULL, &netisr_adone_rport, 0,
489                     pktgen_buf_send);
490                 LIST_INSERT_HEAD(&p->pktg_buflist, pb, pb_link);
491
492                 dst = &pktg->pktg_dst[i % pktg->pktg_ndst];
493                 ++i;
494
495                 m->m_ext.ext_arg = pb;
496                 m->m_ext.ext_buf = pb->pb_buf;
497                 m->m_ext.ext_free = pktgen_buf_free;
498                 m->m_ext.ext_ref = pktgen_buf_ref;
499                 m->m_ext.ext_size = PKTGEN_BUFSZ;
500
501                 m->m_data = m->m_ext.ext_buf;
502                 m->m_flags |= M_EXT;
503                 m->m_len = m->m_pkthdr.len = len;
504
505                 m->m_data += ETHER_HDR_LEN;
506                 m->m_len -= ETHER_HDR_LEN;
507                 m->m_pkthdr.len -= ETHER_HDR_LEN;
508
509                 ui = mtod(m, struct udpiphdr *);
510                 ui->ui_pr = IPPROTO_UDP;
511                 if (pktg->pktg_flags & PKTG_F_SWITCH_SRCDST) {
512                         ui->ui_src.s_addr = dst->sin_addr.s_addr;
513                         ui->ui_dst.s_addr = pktg->pktg_src.sin_addr.s_addr;
514                         ui->ui_sport = dst->sin_port;
515                         ui->ui_dport = pktg->pktg_src.sin_port;
516                 } else {
517                         ui->ui_src.s_addr = pktg->pktg_src.sin_addr.s_addr;
518                         ui->ui_dst.s_addr = dst->sin_addr.s_addr;
519                         ui->ui_sport = pktg->pktg_src.sin_port;
520                         ui->ui_dport = dst->sin_port;
521                 }
522                 ui->ui_ulen = ulen;
523                 ui->ui_sum = in_pseudo(ui->ui_src.s_addr, ui->ui_dst.s_addr,
524                     psum);
525                 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
526
527                 ip = (struct ip *)ui;
528                 ip->ip_len = ip_len;
529                 ip->ip_ttl = 64;        /* XXX */
530                 ip->ip_tos = 0;         /* XXX */
531                 ip->ip_vhl = IP_VHL_BORING;
532                 ip->ip_off = 0;
533                 ip->ip_sum = 0;
534                 ip->ip_id = ip_newid();
535
536                 in_delayed_cksum(m);
537
538                 ip->ip_len = htons(ip->ip_len);
539                 ip->ip_sum = in_cksum_hdr(ip);
540
541                 m->m_data -= ETHER_HDR_LEN;
542                 m->m_len += ETHER_HDR_LEN;
543                 m->m_pkthdr.len += ETHER_HDR_LEN;
544
545                 eh = mtod(m, struct ether_header *);
546                 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN);
547                 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
548                 eh->ether_type = htons(ETHERTYPE_IP);
549
550                 nextm = m->m_nextpkt;
551                 m->m_nextpkt = NULL;
552
553                 ifq_dispatch(ifp, m, NULL);
554
555                 m = nextm;
556         }
557
558         callout_reset(&p->pktg_stop, pktg->pktg_duration * hz,
559             pktgen_pcpu_stop_cb, p);
560
561         pktgen_free(pktg);
562 }
563
564 static void
565 pktgen_mbuf(struct pktgen_buf *pb, struct mbuf *m)
566 {
567         m->m_ext.ext_arg = pb;
568         m->m_ext.ext_buf = pb->pb_buf;
569         m->m_ext.ext_free = pktgen_buf_free;
570         m->m_ext.ext_ref = pktgen_buf_ref;
571         m->m_ext.ext_size = PKTGEN_BUFSZ;
572
573         m->m_data = m->m_ext.ext_buf;
574         m->m_flags |= M_EXT;
575         m->m_len = m->m_pkthdr.len = pb->pb_len;
576 }
577
578 static void
579 pktgen_buf_send(netmsg_t msg)
580 {
581         struct pktgen_buf *pb = (struct pktgen_buf *)msg;
582         struct mbuf *m;
583
584         KKASSERT(&curthread->td_msgport == netisr_cpuport(pb->pb_cpuid));
585
586         crit_enter();
587         lwkt_replymsg(&pb->pb_nmsg.lmsg, 0);
588         crit_exit();
589
590         MGETHDR(m, MB_WAIT, MT_DATA);
591         pktgen_mbuf(pb, m);
592         ifq_dispatch(pb->pb_ifp, m, NULL);
593 }
594
595 static void
596 pktgen_buf_free(void *arg)
597 {
598         struct pktgen_buf *pb = arg;
599         struct mbuf *m;
600
601         KKASSERT(pb->pb_inuse > 0);
602         if (pb->pb_done) {
603                 if (atomic_fetchadd_int(&pb->pb_inuse, -1) == 1) {
604                         struct pktgen *pktg;
605
606                         pktg = pb->pb_pktg;
607                         crit_enter();
608                         LIST_REMOVE(pb, pb_link);
609                         crit_exit();
610                         kfree(pb->pb_buf, M_PKTGEN);
611                         kfree(pb, M_PKTGEN);
612
613                         pktgen_free(pktg);
614                 }
615                 return;
616         }
617
618         if (&curthread->td_msgport != netisr_cpuport(pb->pb_cpuid)) {
619                 KKASSERT(pb->pb_cpuid == mycpuid);
620                 crit_enter();
621                 KKASSERT(pb->pb_nmsg.lmsg.ms_flags & MSGF_DONE);
622                 lwkt_sendmsg(netisr_cpuport(pb->pb_cpuid), &pb->pb_nmsg.lmsg);
623                 crit_exit();
624                 return;
625         }
626
627         MGETHDR(m, MB_WAIT, MT_DATA);
628         pktgen_mbuf(pb, m);
629         ifsq_enqueue(pb->pb_ifsq, m, NULL);
630 }
631
632 static void
633 pktgen_buf_ref(void *arg)
634 {
635         struct pktgen_buf *pb = arg;
636
637         panic("%s should never be called\n", __func__);
638
639         KKASSERT(pb->pb_inuse > 0);
640         atomic_add_int(&pb->pb_inuse, 1);
641 }
642
643 static void
644 pktgen_free(struct pktgen *pktg)
645 {
646         KKASSERT(pktg->pktg_refcnt > 0);
647         if (atomic_fetchadd_int(&pktg->pktg_refcnt, -1) == 1) {
648                 int i;
649
650                 if (pktg->pktg_dst != NULL)
651                         kfree(pktg->pktg_dst, M_PKTGEN);
652
653                 for (i = 0; i < ncpus; ++i)
654                         KKASSERT(LIST_EMPTY(&pktg->pktg_pcpu[i].pktg_buflist));
655                 kfree(pktg, M_PKTGEN);
656         }
657
658         KKASSERT(pktgen_refcnt > 0);
659         atomic_subtract_int(&pktgen_refcnt, 1);
660 }
661
662 static void
663 pktgen_pcpu_stop_cb(void *arg)
664 {
665         struct pktgen_pcpu *p = arg;
666         struct pktgen_buf *pb;
667
668         crit_enter();
669         LIST_FOREACH(pb, &p->pktg_buflist, pb_link)
670                 pb->pb_done = 1;
671         crit_exit();
672 }
673
674 static void
675 pktgen_ref(struct pktgen *pktg)
676 {
677         atomic_add_int(&pktg->pktg_refcnt, 1);
678         atomic_add_int(&pktgen_refcnt, 1);
679 }