ng_source.c revision 125033
1353358Sdim/* 2254721Semaste * ng_source.c 3353358Sdim * 4353358Sdim * Copyright 2002 Sandvine Inc. 5353358Sdim * All rights reserved. 6254721Semaste * 7254721Semaste * Subject to the following obligations and disclaimer of warranty, use and 8254721Semaste * redistribution of this software, in source or object code forms, with or 9254721Semaste * without modifications are expressly permitted by Sandvine Inc.; provided, 10254721Semaste * however, that: 11254721Semaste * 1. Any and all reproductions of the source or object code must include the 12353358Sdim * copyright notice above and the following disclaimer of warranties; and 13353358Sdim * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 14314564Sdim * trademarks, including the mark "SANDVINE" on advertising, endorsements, 15314564Sdim * or otherwise except as such appears in the above copyright notice or in 16314564Sdim * the software. 17276479Sdim * 18254721Semaste * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 19254721Semaste * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 20254721Semaste * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 21254721Semaste * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 22353358Sdim * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 23314564Sdim * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 24314564Sdim * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 25314564Sdim * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 26314564Sdim * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 27314564Sdim * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28314564Sdim * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 29353358Sdim * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 30254721Semaste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31314564Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32314564Sdim * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 33314564Sdim * DAMAGE. 34314564Sdim * 35314564Sdim * Author: Dave Chapeskie <dchapeskie@sandvine.com> 36314564Sdim * 37314564Sdim * $FreeBSD: head/sys/netgraph/ng_source.c 125033 2004-01-26 14:53:16Z harti $ 38314564Sdim */ 39314564Sdim 40314564Sdim/* 41314564Sdim * This node is used for high speed packet geneneration. It queues 42314564Sdim * all data recieved on it's 'input' hook and when told to start via 43314564Sdim * a control message it sends the packets out it's 'output' hook. In 44314564Sdim * this way this node can be preloaded with a packet stream which is 45314564Sdim * continuously sent. 46314564Sdim * 47314564Sdim * Currently it just copies the mbufs as required. It could do various 48314564Sdim * tricks to try and avoid this. Probably the best performance would 49314564Sdim * be achieved by modifying the appropriate drivers to be told to 50314564Sdim * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 51314564Sdim * transmit descriptors) under control of this node; perhaps via some 52314564Sdim * flag in the mbuf or some such. The node would peak at an appropriate 53314564Sdim * ifnet flag to see if such support is available for the connected 54314564Sdim * interface. 55314564Sdim */ 56314564Sdim 57314564Sdim#include <sys/param.h> 58314564Sdim#include <sys/systm.h> 59314564Sdim#include <sys/errno.h> 60314564Sdim#include <sys/kernel.h> 61314564Sdim#include <sys/malloc.h> 62314564Sdim#include <sys/mbuf.h> 63314564Sdim#include <sys/socket.h> 64314564Sdim#include <net/if.h> 65314564Sdim#include <net/if_var.h> 66314564Sdim#include <netgraph/ng_message.h> 67314564Sdim#include <netgraph/netgraph.h> 68314564Sdim#include <netgraph/ng_parse.h> 69254721Semaste#include <netgraph/ng_ether.h> 70254721Semaste#include <netgraph/ng_source.h> 71314564Sdim 72314564Sdim#define NG_SOURCE_INTR_TICKS 1 73314564Sdim#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 74314564Sdim 75314564Sdim 76314564Sdim/* Per hook info */ 77314564Sdimstruct source_hookinfo { 78314564Sdim hook_p hook; 79314564Sdim}; 80314564Sdim 81314564Sdim/* Per node info */ 82314564Sdimstruct privdata { 83314564Sdim node_p node; 84314564Sdim struct source_hookinfo input; 85314564Sdim struct source_hookinfo output; 86314564Sdim struct ng_source_stats stats; 87314564Sdim struct ifqueue snd_queue; /* packets to send */ 88314564Sdim struct ifnet *output_ifp; 89314564Sdim struct callout_handle intr_ch; 90314564Sdim u_int64_t packets; /* packets to send */ 91314564Sdim u_int32_t queueOctets; 92314564Sdim}; 93314564Sdimtypedef struct privdata *sc_p; 94314564Sdim 95314564Sdim/* Node flags */ 96314564Sdim#define NG_SOURCE_ACTIVE (NGF_TYPE1) 97314564Sdim 98314564Sdim/* XXX */ 99314564Sdim#if 1 100314564Sdim#undef KASSERT 101314564Sdim#define KASSERT(expr,msg) do { \ 102314564Sdim if (!(expr)) { \ 103314564Sdim printf msg ; \ 104353358Sdim panic("Assertion"); \ 105254721Semaste } \ 106254721Semaste } while(0) 107314564Sdim#endif 108314564Sdim 109314564Sdim/* Netgraph methods */ 110314564Sdimstatic ng_constructor_t ng_source_constructor; 111314564Sdimstatic ng_rcvmsg_t ng_source_rcvmsg; 112314564Sdimstatic ng_shutdown_t ng_source_rmnode; 113280031Sdimstatic ng_newhook_t ng_source_newhook; 114314564Sdimstatic ng_rcvdata_t ng_source_rcvdata; 115314564Sdimstatic ng_disconnect_t ng_source_disconnect; 116314564Sdim 117314564Sdim/* Other functions */ 118314564Sdimstatic timeout_t ng_source_intr; 119314564Sdimstatic int ng_source_request_output_ifp (sc_p); 120314564Sdimstatic void ng_source_clr_data (sc_p); 121314564Sdimstatic void ng_source_start (sc_p); 122314564Sdimstatic void ng_source_stop (sc_p); 123314564Sdimstatic int ng_source_send (sc_p, int, int *); 124314564Sdimstatic int ng_source_store_output_ifp(sc_p sc, 125314564Sdim struct ng_mesg *msg); 126314564Sdim 127314564Sdim 128314564Sdim/* Parse type for timeval */ 129314564Sdimstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = 130314564Sdim{ 131314564Sdim { "tv_sec", &ng_parse_int32_type }, 132314564Sdim { "tv_usec", &ng_parse_int32_type }, 133314564Sdim { NULL } 134314564Sdim}; 135314564Sdimconst struct ng_parse_type ng_source_timeval_type = { 136314564Sdim &ng_parse_struct_type, 137314564Sdim &ng_source_timeval_type_fields 138314564Sdim}; 139314564Sdim 140314564Sdim/* Parse type for struct ng_source_stats */ 141314564Sdimstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 142314564Sdim = NG_SOURCE_STATS_TYPE_INFO; 143314564Sdimstatic const struct ng_parse_type ng_source_stats_type = { 144314564Sdim &ng_parse_struct_type, 145314564Sdim &ng_source_stats_type_fields 146314564Sdim}; 147314564Sdim 148314564Sdim/* List of commands and how to convert arguments to/from ASCII */ 149280031Sdimstatic const struct ng_cmdlist ng_source_cmds[] = { 150280031Sdim { 151254721Semaste NGM_SOURCE_COOKIE, 152314564Sdim NGM_SOURCE_GET_STATS, 153314564Sdim "getstats", 154314564Sdim NULL, 155314564Sdim &ng_source_stats_type 156314564Sdim }, 157254721Semaste { 158254721Semaste NGM_SOURCE_COOKIE, 159254721Semaste NGM_SOURCE_CLR_STATS, 160254721Semaste "clrstats", 161254721Semaste NULL, 162254721Semaste NULL 163254721Semaste }, 164254721Semaste { 165314564Sdim NGM_SOURCE_COOKIE, 166314564Sdim NGM_SOURCE_GETCLR_STATS, 167314564Sdim "getclrstats", 168314564Sdim NULL, 169314564Sdim &ng_source_stats_type 170314564Sdim }, 171314564Sdim { 172314564Sdim NGM_SOURCE_COOKIE, 173254721Semaste NGM_SOURCE_START, 174254721Semaste "start", 175314564Sdim &ng_parse_uint64_type, 176314564Sdim NULL 177314564Sdim }, 178314564Sdim { 179314564Sdim NGM_SOURCE_COOKIE, 180314564Sdim NGM_SOURCE_STOP, 181314564Sdim "stop", 182314564Sdim NULL, 183314564Sdim NULL 184314564Sdim }, 185314564Sdim { 186314564Sdim NGM_SOURCE_COOKIE, 187314564Sdim NGM_SOURCE_CLR_DATA, 188254721Semaste "clrdata", 189314564Sdim NULL, 190254721Semaste NULL 191314564Sdim }, 192 { 193 NGM_SOURCE_COOKIE, 194 NGM_SOURCE_START_NOW, 195 "start_now", 196 &ng_parse_uint64_type, 197 NULL 198 }, 199 { 0 } 200}; 201 202/* Netgraph type descriptor */ 203static struct ng_type ng_source_typestruct = { 204 NG_ABI_VERSION, 205 NG_SOURCE_NODE_TYPE, 206 NULL, /* module event handler */ 207 ng_source_constructor, 208 ng_source_rcvmsg, 209 ng_source_rmnode, 210 ng_source_newhook, 211 NULL, /* findhook */ 212 NULL, 213 ng_source_rcvdata, /* rcvdata */ 214 ng_source_disconnect, 215 ng_source_cmds 216}; 217NETGRAPH_INIT(source, &ng_source_typestruct); 218 219static int ng_source_set_autosrc(sc_p, u_int32_t); 220 221/* 222 * Node constructor 223 */ 224static int 225ng_source_constructor(node_p node) 226{ 227 sc_p sc; 228 229 sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 230 if (sc == NULL) 231 return (ENOMEM); 232 233 NG_NODE_SET_PRIVATE(node, sc); 234 sc->node = node; 235 sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 236 callout_handle_init(&sc->intr_ch); /* XXX fix.. will 237 cause problems. */ 238 return (0); 239} 240 241/* 242 * Add a hook 243 */ 244static int 245ng_source_newhook(node_p node, hook_p hook, const char *name) 246{ 247 sc_p sc; 248 249 sc = NG_NODE_PRIVATE(node); 250 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 251 if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 252 sc->input.hook = hook; 253 NG_HOOK_SET_PRIVATE(hook, &sc->input); 254 } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 255 sc->output.hook = hook; 256 NG_HOOK_SET_PRIVATE(hook, &sc->output); 257 sc->output_ifp = 0; 258 bzero(&sc->stats, sizeof(sc->stats)); 259 } else 260 return (EINVAL); 261 return (0); 262} 263 264/* 265 * Receive a control message 266 */ 267static int 268ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 269{ 270 sc_p sc; 271 struct ng_mesg *resp = NULL; 272 int error = 0; 273 struct ng_mesg *msg; 274 275 sc = NG_NODE_PRIVATE(node); 276 NGI_GET_MSG(item, msg); 277 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 278 switch (msg->header.typecookie) { 279 case NGM_SOURCE_COOKIE: 280 if (msg->header.flags & NGF_RESP) { 281 error = EINVAL; 282 break; 283 } 284 switch (msg->header.cmd) { 285 case NGM_SOURCE_GET_STATS: 286 case NGM_SOURCE_CLR_STATS: 287 case NGM_SOURCE_GETCLR_STATS: 288 { 289 struct ng_source_stats *stats; 290 291 if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 292 NG_MKRESPONSE(resp, msg, 293 sizeof(*stats), M_NOWAIT); 294 if (resp == NULL) { 295 error = ENOMEM; 296 goto done; 297 } 298 sc->stats.queueOctets = sc->queueOctets; 299 sc->stats.queueFrames = sc->snd_queue.ifq_len; 300 if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 301 && !timevalisset(&sc->stats.endTime)) { 302 getmicrotime(&sc->stats.elapsedTime); 303 timevalsub(&sc->stats.elapsedTime, 304 &sc->stats.startTime); 305 } 306 stats = (struct ng_source_stats *)resp->data; 307 bcopy(&sc->stats, stats, sizeof(* stats)); 308 } 309 if (msg->header.cmd != NGM_SOURCE_GET_STATS) 310 bzero(&sc->stats, sizeof(sc->stats)); 311 } 312 break; 313 case NGM_SOURCE_START: 314 { 315 u_int64_t packets = *(u_int64_t *)msg->data; 316 if (sc->output.hook == NULL) { 317 printf("%s: start on node with no output hook\n" 318 , __FUNCTION__); 319 error = EINVAL; 320 break; 321 } 322 /* TODO validation of packets */ 323 sc->packets = packets; 324 ng_source_start(sc); 325 } 326 break; 327 case NGM_SOURCE_START_NOW: 328 { 329 u_int64_t packets = *(u_int64_t *)msg->data; 330 if (sc->output.hook == NULL) { 331 printf("%s: start on node with no output hook\n" 332 , __FUNCTION__); 333 error = EINVAL; 334 break; 335 } 336 if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 337 error = EBUSY; 338 break; 339 } 340 /* TODO validation of packets */ 341 sc->packets = packets; 342 sc->output_ifp = NULL; 343 344 sc->node->nd_flags |= NG_SOURCE_ACTIVE; 345 timevalclear(&sc->stats.elapsedTime); 346 timevalclear(&sc->stats.endTime); 347 getmicrotime(&sc->stats.startTime); 348 sc->intr_ch = timeout(ng_source_intr, sc, 0); 349 } 350 break; 351 case NGM_SOURCE_STOP: 352 ng_source_stop(sc); 353 break; 354 case NGM_SOURCE_CLR_DATA: 355 ng_source_clr_data(sc); 356 break; 357 default: 358 error = EINVAL; 359 break; 360 } 361 break; 362 case NGM_ETHER_COOKIE: 363 if (!(msg->header.flags & NGF_RESP)) { 364 error = EINVAL; 365 break; 366 } 367 switch (msg->header.cmd) { 368 case NGM_ETHER_GET_IFINDEX: 369 if (ng_source_store_output_ifp(sc, msg) == 0) { 370 ng_source_set_autosrc(sc, 0); 371 sc->node->nd_flags |= NG_SOURCE_ACTIVE; 372 timevalclear(&sc->stats.elapsedTime); 373 timevalclear(&sc->stats.endTime); 374 getmicrotime(&sc->stats.startTime); 375 sc->intr_ch = timeout(ng_source_intr, sc, 0); 376 } 377 break; 378 default: 379 error = EINVAL; 380 } 381 break; 382 default: 383 error = EINVAL; 384 break; 385 } 386 387done: 388 /* Take care of synchronous response, if any */ 389 NG_RESPOND_MSG(error, node, item, resp); 390 /* Free the message and return */ 391 NG_FREE_MSG(msg); 392 return (error); 393} 394 395/* 396 * Receive data on a hook 397 * 398 * If data comes in the input hook, enqueue it on the send queue. 399 * If data comes in the output hook, discard it. 400 */ 401static int 402ng_source_rcvdata(hook_p hook, item_p item) 403{ 404 sc_p sc; 405 struct source_hookinfo *hinfo; 406 int error = 0; 407 struct mbuf *m; 408 409 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 410 NGI_GET_M(item, m); 411 NG_FREE_ITEM(item); 412 hinfo = NG_HOOK_PRIVATE(hook); 413 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 414 KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__)); 415 416 /* Which hook? */ 417 if (hinfo == &sc->output) { 418 /* discard */ 419 NG_FREE_M(m); 420 return (error); 421 } 422 KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__)); 423 424 if ((m->m_flags & M_PKTHDR) == 0) { 425 printf("%s: mbuf without PKTHDR\n", __FUNCTION__); 426 NG_FREE_M(m); 427 return (EINVAL); 428 } 429 430 /* enque packet */ 431 /* XXX should we check IF_QFULL() ? */ 432 _IF_ENQUEUE(&sc->snd_queue, m); 433 sc->queueOctets += m->m_pkthdr.len; 434 435 return (0); 436} 437 438/* 439 * Shutdown processing 440 */ 441static int 442ng_source_rmnode(node_p node) 443{ 444 sc_p sc; 445 446 sc = NG_NODE_PRIVATE(node); 447 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 448 node->nd_flags |= NG_INVALID; 449 ng_source_stop(sc); 450 ng_source_clr_data(sc); 451 NG_NODE_SET_PRIVATE(node, NULL); 452 NG_NODE_UNREF(node); 453 FREE(sc, M_NETGRAPH); 454 return (0); 455} 456 457/* 458 * Hook disconnection 459 */ 460static int 461ng_source_disconnect(hook_p hook) 462{ 463 struct source_hookinfo *hinfo; 464 sc_p sc; 465 466 hinfo = NG_HOOK_PRIVATE(hook); 467 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 468 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 469 hinfo->hook = NULL; 470 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output) 471 ng_rmnode_self(NG_HOOK_NODE(hook)); 472 return (0); 473} 474 475/* 476 * 477 * Ask out neighbour on the output hook side to send us it's interface 478 * information. 479 */ 480static int 481ng_source_request_output_ifp(sc_p sc) 482{ 483 struct ng_mesg *msg; 484 int error = 0; 485 486 sc->output_ifp = NULL; 487 488 /* Ask the attached node for the connected interface's index */ 489 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT); 490 if (msg == NULL) 491 return (ENOBUFS); 492 493 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL); 494 return (error); 495} 496 497/* 498 * Set sc->output_ifp to point to the the struct ifnet of the interface 499 * reached via our output hook. 500 */ 501static int 502ng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg) 503{ 504 struct ifnet *ifp; 505 u_int32_t if_index; 506 int s; 507 508 if (msg->header.arglen < sizeof(u_int32_t)) 509 return (EINVAL); 510 511 if_index = *(u_int32_t *)msg->data; 512 /* Could use ifindex2ifnet[if_index] except that we have no 513 * way of verifying if_index is valid since if_indexlim is 514 * local to if_attach() 515 */ 516 IFNET_RLOCK(); 517 TAILQ_FOREACH(ifp, &ifnet, if_link) { 518 if (ifp->if_index == if_index) 519 break; 520 } 521 IFNET_RUNLOCK(); 522 523 if (ifp == NULL) { 524 printf("%s: can't find interface %d\n", __FUNCTION__, if_index); 525 return (EINVAL); 526 } 527 sc->output_ifp = ifp; 528 529#if 1 530 /* XXX mucking with a drivers ifqueue size is ugly but we need it 531 * to queue a lot of packets to get close to line rate on a gigabit 532 * interface with small packets. 533 * XXX we should restore the original value at stop or disconnect 534 */ 535 s = splimp(); /* XXX is this required? */ 536 if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) 537 { 538 printf("ng_source: changing ifq_maxlen from %d to %d\n", 539 ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 540 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 541 } 542 splx(s); 543#endif 544 return (0); 545} 546 547/* 548 * Set the attached ethernet node's ethernet source address override flag. 549 */ 550static int 551ng_source_set_autosrc(sc_p sc, u_int32_t flag) 552{ 553 struct ng_mesg *msg; 554 int error = 0; 555 556 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 557 sizeof (u_int32_t), M_NOWAIT); 558 if (msg == NULL) 559 return(ENOBUFS); 560 561 *(u_int32_t *)msg->data = flag; 562 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL); 563 return (error); 564} 565 566/* 567 * Clear out the data we've queued 568 */ 569static void 570ng_source_clr_data (sc_p sc) 571{ 572 struct mbuf *m; 573 574 for (;;) { 575 _IF_DEQUEUE(&sc->snd_queue, m); 576 if (m == NULL) 577 break; 578 NG_FREE_M(m); 579 } 580 sc->queueOctets = 0; 581} 582 583/* 584 * Start sending queued data out the output hook 585 */ 586static void 587ng_source_start (sc_p sc) 588{ 589 KASSERT(sc->output.hook != NULL, 590 ("%s: output hook unconnected", __FUNCTION__)); 591 if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) && 592 (sc->output_ifp == NULL)) 593 ng_source_request_output_ifp(sc); 594} 595 596/* 597 * Stop sending queued data out the output hook 598 */ 599static void 600ng_source_stop (sc_p sc) 601{ 602 if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 603 untimeout(ng_source_intr, sc, sc->intr_ch); 604 sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 605 getmicrotime(&sc->stats.endTime); 606 sc->stats.elapsedTime = sc->stats.endTime; 607 timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 608 /* XXX should set this to the initial value instead */ 609 ng_source_set_autosrc(sc, 1); 610 } 611} 612 613/* 614 * While active called every NG_SOURCE_INTR_TICKS ticks. 615 * Sends as many packets as the interface connected to our 616 * output hook is able to enqueue. 617 */ 618static void 619ng_source_intr (void *arg) 620{ 621 sc_p sc = (sc_p) arg; 622 struct ifqueue *ifq; 623 int packets; 624 625 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 626 627 callout_handle_init(&sc->intr_ch); 628 if (sc->packets == 0 || sc->output.hook == NULL 629 || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 630 ng_source_stop(sc); 631 return; 632 } 633 634 if (sc->output_ifp != NULL) { 635 ifq = &sc->output_ifp->if_snd; 636 packets = ifq->ifq_maxlen - ifq->ifq_len; 637 } else 638 packets = sc->snd_queue.ifq_len; 639 640 ng_source_send(sc, packets, NULL); 641 if (sc->packets == 0) { 642 int s = splnet(); 643 ng_source_stop(sc); 644 splx(s); 645 } else 646 sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS); 647} 648 649/* 650 * Send packets out our output hook 651 */ 652static int 653ng_source_send (sc_p sc, int tosend, int *sent_p) 654{ 655 struct ifqueue tmp_queue; 656 struct mbuf *m, *m2; 657 int sent = 0; 658 int error = 0; 659 int s, s2; 660 661 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 662 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__)); 663 KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 664 ("%s: inactive node", __FUNCTION__)); 665 666 if ((u_int64_t)tosend > sc->packets) 667 tosend = sc->packets; 668 669 /* Copy the required number of packets to a temporary queue */ 670 bzero (&tmp_queue, sizeof (tmp_queue)); 671 for (sent = 0; error == 0 && sent < tosend; ++sent) { 672 s = splnet(); 673 _IF_DEQUEUE(&sc->snd_queue, m); 674 splx(s); 675 if (m == NULL) 676 break; 677 678 /* duplicate the packet */ 679 m2 = m_copypacket(m, M_DONTWAIT); 680 if (m2 == NULL) { 681 s = splnet(); 682 _IF_PREPEND(&sc->snd_queue, m); 683 splx(s); 684 error = ENOBUFS; 685 break; 686 } 687 688 /* re-enqueue the original packet for us */ 689 s = splnet(); 690 _IF_ENQUEUE(&sc->snd_queue, m); 691 splx(s); 692 693 /* queue the copy for sending at smplimp */ 694 _IF_ENQUEUE(&tmp_queue, m2); 695 } 696 697 sent = 0; 698 s = splimp(); 699 for (;;) { 700 _IF_DEQUEUE(&tmp_queue, m2); 701 if (m2 == NULL) 702 break; 703 if (error == 0) { 704 ++sent; 705 sc->stats.outFrames++; 706 sc->stats.outOctets += m2->m_pkthdr.len; 707 s2 = splnet(); 708 NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 709 splx(s2); 710 } else { 711 NG_FREE_M(m2); 712 } 713 } 714 splx(s); 715 716 sc->packets -= sent; 717 if (sent_p != NULL) 718 *sent_p = sent; 719 return (error); 720} 721