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