Merge branch 'vendor/TNFTP'
[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
60 #include <netinet/in.h>
61 #include <netinet/ip.h>
62 #include <netinet/udp_var.h>
63
64 #include "pktgen.h"
65
66 #define CDEV_NAME       "pktg"
67
68 #define PKTGEN_BUFSZ    2048
69
70 #ifndef PKTGEN_DEVCNT
71 #define PKTGEN_DEVCNT   4
72 #endif
73
74 struct pktgen;
75
76 struct netmsg_pktgen {
77         struct netmsg_base      np_base;
78         struct pktgen           *np_pktg;
79         struct ifaltq_subque    *np_ifsq;
80 };
81
82 struct pktgen_buf {
83         struct netmsg_base      pb_nmsg;        /* MUST BE THE FIRST */
84         void                    *pb_buf;
85         volatile int            pb_done;
86         int                     pb_inuse;
87         struct ifnet            *pb_ifp;
88         struct ifaltq_subque    *pb_ifsq;
89         int                     pb_len;
90         int                     pb_cpuid;
91         struct pktgen           *pb_pktg;
92         LIST_ENTRY(pktgen_buf)  pb_link;
93 };
94
95 struct pktgen_pcpu {
96         struct callout          pktg_stop;
97         LIST_HEAD(, pktgen_buf) pktg_buflist;
98 };
99
100 struct pktgen {
101         uint32_t                pktg_flags;     /* PKTG_F_ */
102         int                     pktg_refcnt;
103
104         int                     pktg_duration;
105
106         int                     pktg_datalen;
107         struct ifnet            *pktg_ifp;
108
109         int                     pktg_pktenq;
110
111         struct sockaddr_in      pktg_src;
112         int                     pktg_ndst;
113         struct sockaddr_in      *pktg_dst;
114         uint8_t                 pktg_dst_lladdr[ETHER_ADDR_LEN];
115
116         struct pktgen_pcpu      pktg_pcpu[MAXCPU];
117 };
118
119 #define PKTG_F_CONFIG   0x1
120 #define PKTG_F_RUNNING  0x4
121
122 static int              pktgen_modevent(module_t, int, void *);
123
124 static void             pktgen_buf_free(void *);
125 static void             pktgen_buf_ref(void *);
126 static void             pktgen_buf_send(netmsg_t);
127
128 static int              pktgen_config(struct pktgen *,
129                             const struct pktgen_conf *);
130 static int              pktgen_start(struct pktgen *, int);
131 static void             pktgen_free(struct pktgen *);
132 static void             pktgen_ref(struct pktgen *);
133 static void             pktgen_pcpu_stop_cb(void *);
134 static void             pktgen_mbuf(struct pktgen_buf *, struct mbuf *);
135 static void             pktgen_start_ifsq(struct pktgen *,
136                             struct ifaltq_subque *);
137 static void             pktgen_start_ifsq_handler(netmsg_t);
138
139 static d_open_t         pktgen_open;
140 static d_close_t        pktgen_close;
141 static d_ioctl_t        pktgen_ioctl;
142
143 static struct dev_ops   pktgen_ops = {
144         { CDEV_NAME, 0, D_MPSAFE },
145         .d_open =       pktgen_open,
146         .d_close =      pktgen_close,
147         .d_ioctl =      pktgen_ioctl,
148 };
149
150 static volatile int             pktgen_refcnt;
151 static struct lwkt_token pktgen_tok = LWKT_TOKEN_INITIALIZER(pktgen_token);
152
153 MALLOC_DECLARE(M_PKTGEN);
154 MALLOC_DEFINE(M_PKTGEN, CDEV_NAME, "Packet generator");
155
156 DEV_MODULE(pktgen, pktgen_modevent, NULL);
157
158 static int
159 pktgen_modevent(module_t mod, int type, void *data)
160 {
161         int error = 0, i;
162
163         switch (type) {
164         case MOD_LOAD:
165                 for (i = 0; i < PKTGEN_DEVCNT; ++i) {
166                         make_dev(&pktgen_ops, 0, UID_ROOT, GID_WHEEL, 0600,
167                             CDEV_NAME"%d", i);
168                 }
169                 break;
170
171         case MOD_UNLOAD:
172                 if (pktgen_refcnt > 0)
173                         return EBUSY;
174                 dev_ops_remove_all(&pktgen_ops);
175                 break;
176
177         default:
178                 error = EOPNOTSUPP;
179                 break;
180         }
181         return error;
182 }
183
184 static int
185 pktgen_open(struct dev_open_args *ap)
186 {
187         cdev_t dev = ap->a_head.a_dev;
188         struct pktgen *pktg;
189         int error, i;
190
191         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
192         if (error)
193                 return error;
194
195         lwkt_gettoken(&pktgen_tok);
196
197         if (dev->si_drv1 != NULL) {
198                 lwkt_reltoken(&pktgen_tok);
199                 return EBUSY;
200         }
201
202         pktg = kmalloc(sizeof(*pktg), M_PKTGEN, M_ZERO | M_WAITOK);
203         for (i = 0; i < ncpus; ++i) {
204                 struct pktgen_pcpu *p = &pktg->pktg_pcpu[i];
205
206                 callout_init_mp(&p->pktg_stop);
207                 LIST_INIT(&p->pktg_buflist);
208         }
209
210         dev->si_drv1 = pktg;
211         pktg->pktg_refcnt = 1;
212
213         atomic_add_int(&pktgen_refcnt, 1);
214
215         lwkt_reltoken(&pktgen_tok);
216         return 0;
217 }
218
219 static int
220 pktgen_close(struct dev_close_args *ap)
221 {
222         cdev_t dev = ap->a_head.a_dev;
223         struct pktgen *pktg = dev->si_drv1;
224
225         lwkt_gettoken(&pktgen_tok);
226         dev->si_drv1 = NULL;
227         lwkt_reltoken(&pktgen_tok);
228
229         pktgen_free(pktg);
230
231         return 0;
232 }
233
234 static int
235 pktgen_ioctl(struct dev_ioctl_args *ap __unused)
236 {
237         cdev_t dev = ap->a_head.a_dev;
238         caddr_t data = ap->a_data;
239         struct pktgen *pktg = dev->si_drv1;
240         int error;
241
242         lwkt_gettoken(&pktgen_tok);
243
244         switch (ap->a_cmd) {
245         case PKTGENSTART:
246                 error = pktgen_start(pktg, 0);
247                 break;
248
249         case PKTGENMQSTART:
250                 error = pktgen_start(pktg, 1);
251                 break;
252
253         case PKTGENSCONF:
254                 error = pktgen_config(pktg, (const struct pktgen_conf *)data);
255                 break;
256
257         default:
258                 error = EOPNOTSUPP;
259                 break;
260         }
261
262         lwkt_reltoken(&pktgen_tok);
263         return error;
264 }
265
266 static int
267 pktgen_config(struct pktgen *pktg, const struct pktgen_conf *conf)
268 {
269         const struct sockaddr_in *sin;
270         struct sockaddr_in *dst = NULL;
271         const struct sockaddr *sa;
272         struct ifnet *ifp;
273         size_t dst_size;
274         int i, error, pktenq;
275
276         if (pktg->pktg_flags & (PKTG_F_RUNNING | PKTG_F_CONFIG))
277                 return EBUSY;
278
279         if (conf->pc_datalen <= 0 ||
280             conf->pc_datalen > ETHERMTU - sizeof(struct udpiphdr))
281                 return EINVAL;
282         if (conf->pc_duration <= 0)
283                 return EINVAL;
284
285         sin = &conf->pc_src;
286         if (sin->sin_family != AF_INET)
287                 return EPROTONOSUPPORT;
288         if (sin->sin_port == 0)
289                 return EINVAL;
290
291         if (conf->pc_ndst <= 0)
292                 return EINVAL;
293         dst_size = conf->pc_ndst * sizeof(struct sockaddr_in);
294
295         dst = kmalloc(dst_size, M_PKTGEN, M_WAITOK | M_NULLOK);
296         if (dst == NULL)
297                 return ENOMEM;
298
299         error = copyin(conf->pc_dst, dst, dst_size);
300         if (error)
301                 goto failed;
302
303         for (i = 0; i < conf->pc_ndst; ++i) {
304                 sin = &dst[i];
305                 if (sin->sin_family != AF_INET) {
306                         error = EPROTONOSUPPORT;
307                         goto failed;
308                 }
309                 if (sin->sin_port == 0) {
310                         error = EINVAL;
311                         goto failed;
312                 }
313         }
314
315         ifp = ifunit(conf->pc_ifname);
316         if (ifp == NULL) {
317                 error = ENXIO;
318                 goto failed;
319         }
320
321         pktenq = conf->pc_pktenq;
322         if (pktenq < 0 || pktenq > ifp->if_snd.altq_maxlen) {
323                 error = ENOBUFS;
324                 goto failed;
325         } else if (pktenq == 0) {
326                 pktenq = (ifp->if_snd.altq_maxlen * 3) / 4;
327         }
328
329         sa = &conf->pc_dst_lladdr;
330         if (sa->sa_family != AF_LINK) {
331                 error = EPROTONOSUPPORT;
332                 goto failed;
333         }
334         if (sa->sa_len != ETHER_ADDR_LEN) {
335                 error = EPROTONOSUPPORT;
336                 goto failed;
337         }
338         if (ETHER_IS_MULTICAST(sa->sa_data) ||
339             bcmp(sa->sa_data, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
340                 error = EADDRNOTAVAIL;
341                 goto failed;
342         }
343
344         /*
345          * Accept the config
346          */
347         pktg->pktg_flags |= PKTG_F_CONFIG;
348
349         pktg->pktg_duration = conf->pc_duration;
350         pktg->pktg_datalen = conf->pc_datalen;
351         pktg->pktg_pktenq = pktenq;
352         pktg->pktg_ifp = ifp;
353         pktg->pktg_src = conf->pc_src;
354         pktg->pktg_ndst = conf->pc_ndst;
355         KKASSERT(pktg->pktg_dst == NULL);
356         pktg->pktg_dst = dst;
357         bcopy(sa->sa_data, pktg->pktg_dst_lladdr, ETHER_ADDR_LEN);
358
359         return 0;
360
361 failed:
362         if (dst != NULL)
363                 kfree(dst, M_PKTGEN);
364         return error;
365 }
366
367 static void
368 pktgen_start_ifsq(struct pktgen *pktg, struct ifaltq_subque *ifsq)
369 {
370         struct netmsg_pktgen *np;
371
372         np = kmalloc(sizeof(*np), M_LWKTMSG, M_WAITOK);
373         netmsg_init(&np->np_base, NULL, &netisr_afree_rport, 0,
374             pktgen_start_ifsq_handler);
375         np->np_pktg = pktg;
376         np->np_ifsq = ifsq;
377
378         lwkt_sendmsg(netisr_portfn(ifsq_get_cpuid(ifsq)), &np->np_base.lmsg);
379 }
380
381 static int
382 pktgen_start(struct pktgen *pktg, int mq)
383 {
384         struct ifaltq *ifq;
385
386         if ((pktg->pktg_flags & PKTG_F_CONFIG) == 0)
387                 return EINVAL;
388         if (pktg->pktg_flags & PKTG_F_RUNNING)
389                 return EBUSY;
390         pktg->pktg_flags |= PKTG_F_RUNNING;
391
392         ifq = &pktg->pktg_ifp->if_snd;
393         if (!mq) {
394                 pktgen_ref(pktg);
395                 pktgen_start_ifsq(pktg, ifq_get_subq_default(ifq));
396         } else {
397                 int i;
398
399                 for (i = 0; i < ifq->altq_subq_cnt; ++i)
400                         pktgen_ref(pktg);
401                 for (i = 0; i < ifq->altq_subq_cnt; ++i)
402                         pktgen_start_ifsq(pktg, ifq_get_subq(ifq, i));
403         }
404         return 0;
405 }
406
407 static void
408 pktgen_start_ifsq_handler(netmsg_t nmsg)
409 {
410         struct netmsg_pktgen *np = (struct netmsg_pktgen *)nmsg;
411         struct pktgen *pktg = np->np_pktg;
412         struct ifaltq_subque *ifsq = np->np_ifsq;
413
414         struct mbuf *m, *head = NULL, **next;
415         struct ifnet *ifp;
416         struct pktgen_pcpu *p;
417         int cpuid, i, alloc_cnt, keep_cnt;
418
419         u_short ulen, psum;
420         int len, ip_len;
421
422         /* Reply ASAP */
423         lwkt_replymsg(&np->np_base.lmsg, 0);
424
425         ifp = pktg->pktg_ifp;
426
427         cpuid = ifsq_get_cpuid(ifsq);
428         KKASSERT(cpuid == mycpuid);
429
430         p = &pktg->pktg_pcpu[cpuid];
431
432         keep_cnt = pktg->pktg_pktenq;
433         alloc_cnt = keep_cnt * 2;
434
435         /*
436          * Prefault enough mbuf into mbuf objcache
437          */
438         next = &head;
439         for (i = 0; i < alloc_cnt; ++i) {
440                 MGETHDR(m, MB_WAIT, MT_DATA);
441                 *next = m;
442                 next = &m->m_nextpkt;
443         }
444
445         for (i = 0; i < alloc_cnt - keep_cnt; ++i) {
446                 m = head;
447                 head = m->m_nextpkt;
448                 m->m_nextpkt = NULL;
449                 m_freem(m);
450         }
451         KKASSERT(head != NULL);
452
453         /*
454          * Setup the packets' data
455          */
456         ip_len = pktg->pktg_datalen + sizeof(struct udpiphdr);
457         len = ip_len + ETHER_HDR_LEN;
458
459         psum = htons((u_short)pktg->pktg_datalen + sizeof(struct udphdr) +
460             IPPROTO_UDP);
461         ulen = htons(pktg->pktg_datalen + sizeof(struct udphdr));
462
463         m = head;
464         i = 0;
465         while (m != NULL) {
466                 struct mbuf *nextm;
467                 const struct sockaddr_in *dst;
468                 struct pktgen_buf *pb;
469                 struct ip *ip;
470                 struct udpiphdr *ui;
471                 struct ether_header *eh;
472
473                 pktgen_ref(pktg);
474
475                 pb = kmalloc(sizeof(*pb), M_PKTGEN, M_WAITOK | M_ZERO);
476                 pb->pb_ifp = ifp;
477                 pb->pb_ifsq = ifsq;
478                 pb->pb_inuse = 1;
479                 pb->pb_buf = kmalloc(PKTGEN_BUFSZ, M_PKTGEN, M_WAITOK);
480                 pb->pb_len = len;
481                 pb->pb_cpuid = cpuid;
482                 pb->pb_pktg = pktg;
483                 netmsg_init(&pb->pb_nmsg, NULL, &netisr_adone_rport, 0,
484                     pktgen_buf_send);
485                 LIST_INSERT_HEAD(&p->pktg_buflist, pb, pb_link);
486
487                 dst = &pktg->pktg_dst[i % pktg->pktg_ndst];
488                 ++i;
489
490                 m->m_ext.ext_arg = pb;
491                 m->m_ext.ext_buf = pb->pb_buf;
492                 m->m_ext.ext_free = pktgen_buf_free;
493                 m->m_ext.ext_ref = pktgen_buf_ref;
494                 m->m_ext.ext_size = PKTGEN_BUFSZ;
495
496                 m->m_data = m->m_ext.ext_buf;
497                 m->m_flags |= M_EXT;
498                 m->m_len = m->m_pkthdr.len = len;
499
500                 m->m_data += ETHER_HDR_LEN;
501                 m->m_len -= ETHER_HDR_LEN;
502                 m->m_pkthdr.len -= ETHER_HDR_LEN;
503
504                 ui = mtod(m, struct udpiphdr *);
505                 ui->ui_pr = IPPROTO_UDP;
506                 ui->ui_src.s_addr = pktg->pktg_src.sin_addr.s_addr;
507                 ui->ui_dst.s_addr = dst->sin_addr.s_addr;
508                 ui->ui_sport = pktg->pktg_src.sin_port;
509                 ui->ui_dport = dst->sin_port;
510                 ui->ui_ulen = ulen;
511                 ui->ui_sum = in_pseudo(ui->ui_src.s_addr, ui->ui_dst.s_addr,
512                     psum);
513                 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
514
515                 ip = (struct ip *)ui;
516                 ip->ip_len = ip_len;
517                 ip->ip_ttl = 64;        /* XXX */
518                 ip->ip_tos = 0;         /* XXX */
519                 ip->ip_vhl = IP_VHL_BORING;
520                 ip->ip_off = 0;
521                 ip->ip_sum = 0;
522                 ip->ip_id = ip_newid();
523
524                 in_delayed_cksum(m);
525
526                 ip->ip_len = htons(ip->ip_len);
527                 ip->ip_sum = in_cksum_hdr(ip);
528
529                 m->m_data -= ETHER_HDR_LEN;
530                 m->m_len += ETHER_HDR_LEN;
531                 m->m_pkthdr.len += ETHER_HDR_LEN;
532
533                 eh = mtod(m, struct ether_header *);
534                 bcopy(pktg->pktg_dst_lladdr, eh->ether_dhost, ETHER_ADDR_LEN);
535                 bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
536                 eh->ether_type = htons(ETHERTYPE_IP);
537
538                 nextm = m->m_nextpkt;
539                 m->m_nextpkt = NULL;
540
541                 ifq_dispatch(ifp, m, NULL);
542
543                 m = nextm;
544         }
545
546         callout_reset(&p->pktg_stop, pktg->pktg_duration * hz,
547             pktgen_pcpu_stop_cb, p);
548
549         pktgen_free(pktg);
550 }
551
552 static void
553 pktgen_mbuf(struct pktgen_buf *pb, struct mbuf *m)
554 {
555         m->m_ext.ext_arg = pb;
556         m->m_ext.ext_buf = pb->pb_buf;
557         m->m_ext.ext_free = pktgen_buf_free;
558         m->m_ext.ext_ref = pktgen_buf_ref;
559         m->m_ext.ext_size = PKTGEN_BUFSZ;
560
561         m->m_data = m->m_ext.ext_buf;
562         m->m_flags |= M_EXT;
563         m->m_len = m->m_pkthdr.len = pb->pb_len;
564 }
565
566 static void
567 pktgen_buf_send(netmsg_t msg)
568 {
569         struct pktgen_buf *pb = (struct pktgen_buf *)msg;
570         struct mbuf *m;
571
572         KKASSERT(&curthread->td_msgport == netisr_portfn(pb->pb_cpuid));
573
574         crit_enter();
575         lwkt_replymsg(&pb->pb_nmsg.lmsg, 0);
576         crit_exit();
577
578         MGETHDR(m, MB_WAIT, MT_DATA);
579         pktgen_mbuf(pb, m);
580         ifq_dispatch(pb->pb_ifp, m, NULL);
581 }
582
583 static void
584 pktgen_buf_free(void *arg)
585 {
586         struct pktgen_buf *pb = arg;
587         struct mbuf *m;
588
589         KKASSERT(pb->pb_inuse > 0);
590         if (pb->pb_done) {
591                 if (atomic_fetchadd_int(&pb->pb_inuse, -1) == 1) {
592                         struct pktgen *pktg;
593
594                         pktg = pb->pb_pktg;
595                         crit_enter();
596                         LIST_REMOVE(pb, pb_link);
597                         crit_exit();
598                         kfree(pb->pb_buf, M_PKTGEN);
599                         kfree(pb, M_PKTGEN);
600
601                         pktgen_free(pktg);
602                 }
603                 return;
604         }
605
606         if (&curthread->td_msgport != netisr_portfn(pb->pb_cpuid)) {
607                 KKASSERT(pb->pb_cpuid == mycpuid);
608                 crit_enter();
609                 KKASSERT(pb->pb_nmsg.lmsg.ms_flags & MSGF_DONE);
610                 lwkt_sendmsg(netisr_portfn(pb->pb_cpuid), &pb->pb_nmsg.lmsg);
611                 crit_exit();
612                 return;
613         }
614
615         MGETHDR(m, MB_WAIT, MT_DATA);
616         pktgen_mbuf(pb, m);
617         ifsq_enqueue(pb->pb_ifsq, m, NULL);
618 }
619
620 static void
621 pktgen_buf_ref(void *arg)
622 {
623         struct pktgen_buf *pb = arg;
624
625         panic("%s should never be called\n", __func__);
626
627         KKASSERT(pb->pb_inuse > 0);
628         atomic_add_int(&pb->pb_inuse, 1);
629 }
630
631 static void
632 pktgen_free(struct pktgen *pktg)
633 {
634         KKASSERT(pktg->pktg_refcnt > 0);
635         if (atomic_fetchadd_int(&pktg->pktg_refcnt, -1) == 1) {
636                 int i;
637
638                 if (pktg->pktg_dst != NULL)
639                         kfree(pktg->pktg_dst, M_PKTGEN);
640
641                 for (i = 0; i < ncpus; ++i)
642                         KKASSERT(LIST_EMPTY(&pktg->pktg_pcpu[i].pktg_buflist));
643                 kfree(pktg, M_PKTGEN);
644         }
645
646         KKASSERT(pktgen_refcnt > 0);
647         atomic_subtract_int(&pktgen_refcnt, 1);
648 }
649
650 static void
651 pktgen_pcpu_stop_cb(void *arg)
652 {
653         struct pktgen_pcpu *p = arg;
654         struct pktgen_buf *pb;
655
656         crit_enter();
657         LIST_FOREACH(pb, &p->pktg_buflist, pb_link)
658                 pb->pb_done = 1;
659         crit_exit();
660 }
661
662 static void
663 pktgen_ref(struct pktgen *pktg)
664 {
665         atomic_add_int(&pktg->pktg_refcnt, 1);
666         atomic_add_int(&pktgen_refcnt, 1);
667 }