diff options
Diffstat (limited to 'docs/gettingstarted/developers/vlib.md')
-rw-r--r-- | docs/gettingstarted/developers/vlib.md | 891 |
1 files changed, 0 insertions, 891 deletions
diff --git a/docs/gettingstarted/developers/vlib.md b/docs/gettingstarted/developers/vlib.md deleted file mode 100644 index 3a35978136c..00000000000 --- a/docs/gettingstarted/developers/vlib.md +++ /dev/null @@ -1,891 +0,0 @@ - -VLIB (Vector Processing Library) -================================ - -The files associated with vlib are located in the ./src/{vlib, -vlibapi, vlibmemory} folders. These libraries provide vector -processing support including graph-node scheduling, reliable multicast -support, ultra-lightweight cooperative multi-tasking threads, a CLI, -plug in .DLL support, physical memory and Linux epoll support. Parts of -this library embody US Patent 7,961,636. - -Init function discovery ------------------------ - -vlib applications register for various \[initialization\] events by -placing structures and \_\_attribute\_\_((constructor)) functions into -the image. At appropriate times, the vlib framework walks -constructor-generated singly-linked structure lists, performs a -topological sort based on specified constraints, and calls the -indicated functions. Vlib applications create graph nodes, add CLI -functions, start cooperative multi-tasking threads, etc. etc. using -this mechanism. - -vlib applications invariably include a number of VLIB\_INIT\_FUNCTION -(my\_init\_function) macros. - -Each init / configure / etc. function has the return type clib\_error\_t -\*. Make sure that the function returns 0 if all is well, otherwise the -framework will announce an error and exit. - -vlib applications must link against vppinfra, and often link against -other libraries such as VNET. In the latter case, it may be necessary to -explicitly reference symbol(s) otherwise large portions of the library -may be AWOL at runtime. - -### Init function construction and constraint specification - -It's easy to add an init function: - -``` - static clib_error_t *my_init_function (vlib_main_t *vm) - { - /* ... initialize things ... */ - - return 0; // or return clib_error_return (0, "BROKEN!"); - } - VLIB_INIT_FUNCTION(my_init_function); -``` - -As given, my_init_function will be executed "at some point," but with -no ordering guarantees. - -Specifying ordering constraints is easy: - -``` - VLIB_INIT_FUNCTION(my_init_function) = - { - .runs_before = VLIB_INITS("we_run_before_function_1", - "we_run_before_function_2"), - .runs_after = VLIB_INITS("we_run_after_function_1", - "we_run_after_function_2), - }; -``` - -It's also easy to specify bulk ordering constraints of the form "a -then b then c then d": - -``` - VLIB_INIT_FUNCTION(my_init_function) = - { - .init_order = VLIB_INITS("a", "b", "c", "d"), - }; -``` - -It's OK to specify all three sorts of ordering constraints for a -single init function, although it's hard to imagine why it would be -necessary. - - -Node Graph Initialization -------------------------- - -vlib packet-processing applications invariably define a set of graph -nodes to process packets. - -One constructs a vlib\_node\_registration\_t, most often via the -VLIB\_REGISTER\_NODE macro. At runtime, the framework processes the set -of such registrations into a directed graph. It is easy enough to add -nodes to the graph at runtime. The framework does not support removing -nodes. - -vlib provides several types of vector-processing graph nodes, primarily -to control framework dispatch behaviors. The type member of the -vlib\_node\_registration\_t functions as follows: - -- VLIB\_NODE\_TYPE\_PRE\_INPUT - run before all other node types -- VLIB\_NODE\_TYPE\_INPUT - run as often as possible, after pre\_input - nodes -- VLIB\_NODE\_TYPE\_INTERNAL - only when explicitly made runnable by - adding pending frames for processing -- VLIB\_NODE\_TYPE\_PROCESS - only when explicitly made runnable. - "Process" nodes are actually cooperative multi-tasking threads. They - **must** explicitly suspend after a reasonably short period of time. - -For a precise understanding of the graph node dispatcher, please read -./src/vlib/main.c:vlib\_main\_loop. - -Graph node dispatcher ---------------------- - -Vlib\_main\_loop() dispatches graph nodes. The basic vector processing -algorithm is diabolically simple, but may not be obvious from even a -long stare at the code. Here's how it works: some input node, or set of -input nodes, produce a vector of work to process. The graph node -dispatcher pushes the work vector through the directed graph, -subdividing it as needed, until the original work vector has been -completely processed. At that point, the process recurs. - -This scheme yields a stable equilibrium in frame size, by construction. -Here's why: as the frame size increases, the per-frame-element -processing time decreases. There are several related forces at work; the -simplest to describe is the effect of vector processing on the CPU L1 -I-cache. The first frame element \[packet\] processed by a given node -warms up the node dispatch function in the L1 I-cache. All subsequent -frame elements profit. As we increase the number of frame elements, the -cost per element goes down. - -Under light load, it is a crazy waste of CPU cycles to run the graph -node dispatcher flat-out. So, the graph node dispatcher arranges to wait -for work by sitting in a timed epoll wait if the prevailing frame size -is low. The scheme has a certain amount of hysteresis to avoid -constantly toggling back and forth between interrupt and polling mode. -Although the graph dispatcher supports interrupt and polling modes, our -current default device drivers do not. - -The graph node scheduler uses a hierarchical timer wheel to reschedule -process nodes upon timer expiration. - -Graph dispatcher internals --------------------------- - -This section may be safely skipped. It's not necessary to understand -graph dispatcher internals to create graph nodes. - -Vector Data Structure ---------------------- - -In vpp / vlib, we represent vectors as instances of the vlib_frame_t type: - -```c - typedef struct vlib_frame_t - { - /* Frame flags. */ - u16 flags; - - /* Number of scalar bytes in arguments. */ - u8 scalar_size; - - /* Number of bytes per vector argument. */ - u8 vector_size; - - /* Number of vector elements currently in frame. */ - u16 n_vectors; - - /* Scalar and vector arguments to next node. */ - u8 arguments[0]; - } vlib_frame_t; -``` - -Note that one _could_ construct all kinds of vectors - including -vectors with some associated scalar data - using this structure. In -the vpp application, vectors typically use a 4-byte vector element -size, and zero bytes' worth of associated per-frame scalar data. - -Frames are always allocated on CLIB_CACHE_LINE_BYTES boundaries. -Frames have u32 indices which make use of the alignment property, so -the maximum feasible main heap offset of a frame is -CLIB_CACHE_LINE_BYTES * 0xFFFFFFFF: 64*4 = 256 Gbytes. - -Scheduling Vectors ------------------- - -As you can see, vectors are not directly associated with graph -nodes. We represent that association in a couple of ways. The -simplest is the vlib\_pending\_frame\_t: - -```c - /* A frame pending dispatch by main loop. */ - typedef struct - { - /* Node and runtime for this frame. */ - u32 node_runtime_index; - - /* Frame index (in the heap). */ - u32 frame_index; - - /* Start of next frames for this node. */ - u32 next_frame_index; - - /* Special value for next_frame_index when there is no next frame. */ - #define VLIB_PENDING_FRAME_NO_NEXT_FRAME ((u32) ~0) - } vlib_pending_frame_t; -``` - -Here is the code in .../src/vlib/main.c:vlib_main_or_worker_loop() -which processes frames: - -```c - /* - * Input nodes may have added work to the pending vector. - * Process pending vector until there is nothing left. - * All pending vectors will be processed from input -> output. - */ - for (i = 0; i < _vec_len (nm->pending_frames); i++) - cpu_time_now = dispatch_pending_node (vm, i, cpu_time_now); - /* Reset pending vector for next iteration. */ -``` - -The pending frame node_runtime_index associates the frame with the -node which will process it. - -Complications -------------- - -Fasten your seatbelt. Here's where the story - and the data structures -\- become quite complicated... - -At 100,000 feet: vpp uses a directed graph, not a directed _acyclic_ -graph. It's really quite normal for a packet to visit ip\[46\]-lookup -multiple times. The worst-case: a graph node which enqueues packets to -itself. - -To deal with this issue, the graph dispatcher must force allocation of -a new frame if the current graph node's dispatch function happens to -enqueue a packet back to itself. - -There are no guarantees that a pending frame will be processed -immediately, which means that more packets may be added to the -underlying vlib_frame_t after it has been attached to a -vlib_pending_frame_t. Care must be taken to allocate new -frames and pending frames if a (pending\_frame, frame) pair fills. - -Next frames, next frame ownership ---------------------------------- - -The vlib\_next\_frame\_t is the last key graph dispatcher data structure: - -```c - typedef struct - { - /* Frame index. */ - u32 frame_index; - - /* Node runtime for this next. */ - u32 node_runtime_index; - - /* Next frame flags. */ - u32 flags; - - /* Reflects node frame-used flag for this next. */ - #define VLIB_FRAME_NO_FREE_AFTER_DISPATCH \ - VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH - - /* This next frame owns enqueue to node - corresponding to node_runtime_index. */ - #define VLIB_FRAME_OWNER (1 << 15) - - /* Set when frame has been allocated for this next. */ - #define VLIB_FRAME_IS_ALLOCATED VLIB_NODE_FLAG_IS_OUTPUT - - /* Set when frame has been added to pending vector. */ - #define VLIB_FRAME_PENDING VLIB_NODE_FLAG_IS_DROP - - /* Set when frame is to be freed after dispatch. */ - #define VLIB_FRAME_FREE_AFTER_DISPATCH VLIB_NODE_FLAG_IS_PUNT - - /* Set when frame has traced packets. */ - #define VLIB_FRAME_TRACE VLIB_NODE_FLAG_TRACE - - /* Number of vectors enqueue to this next since last overflow. */ - u32 vectors_since_last_overflow; - } vlib_next_frame_t; -``` - -Graph node dispatch functions call vlib\_get\_next\_frame (...) to -set "(u32 \*)to_next" to the right place in the vlib_frame_t -corresponding to the ith arc (aka next0) from the current node to the -indicated next node. - -After some scuffling around - two levels of macros - processing -reaches vlib\_get\_next\_frame_internal (...). Get-next-frame-internal -digs up the vlib\_next\_frame\_t corresponding to the desired graph -arc. - -The next frame data structure amounts to a graph-arc-centric frame -cache. Once a node finishes adding element to a frame, it will acquire -a vlib_pending_frame_t and end up on the graph dispatcher's -run-queue. But there's no guarantee that more vector elements won't be -added to the underlying frame from the same (source\_node, -next\_index) arc or from a different (source\_node, next\_index) arc. - -Maintaining consistency of the arc-to-frame cache is necessary. The -first step in maintaining consistency is to make sure that only one -graph node at a time thinks it "owns" the target vlib\_frame\_t. - -Back to the graph node dispatch function. In the usual case, a certain -number of packets will be added to the vlib\_frame\_t acquired by -calling vlib\_get\_next\_frame (...). - -Before a dispatch function returns, it's required to call -vlib\_put\_next\_frame (...) for all of the graph arcs it actually -used. This action adds a vlib\_pending\_frame\_t to the graph -dispatcher's pending frame vector. - -Vlib\_put\_next\_frame makes a note in the pending frame of the frame -index, and also of the vlib\_next\_frame\_t index. - -dispatch\_pending\_node actions -------------------------------- - -The main graph dispatch loop calls dispatch pending node as shown -above. - -Dispatch\_pending\_node recovers the pending frame, and the graph node -runtime / dispatch function. Further, it recovers the next\_frame -currently associated with the vlib\_frame\_t, and detaches the -vlib\_frame\_t from the next\_frame. - -In .../src/vlib/main.c:dispatch\_pending\_node(...), note this stanza: - -```c - /* Force allocation of new frame while current frame is being - dispatched. */ - restore_frame_index = ~0; - if (nf->frame_index == p->frame_index) - { - nf->frame_index = ~0; - nf->flags &= ~VLIB_FRAME_IS_ALLOCATED; - if (!(n->flags & VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH)) - restore_frame_index = p->frame_index; - } -``` - -dispatch\_pending\_node is worth a hard stare due to the several -second-order optimizations it implements. Almost as an afterthought, -it calls dispatch_node which actually calls the graph node dispatch -function. - -Process / thread model ----------------------- - -vlib provides an ultra-lightweight cooperative multi-tasking thread -model. The graph node scheduler invokes these processes in much the same -way as traditional vector-processing run-to-completion graph nodes; -plus-or-minus a setjmp/longjmp pair required to switch stacks. Simply -set the vlib\_node\_registration\_t type field to -vlib\_NODE\_TYPE\_PROCESS. Yes, process is a misnomer. These are -cooperative multi-tasking threads. - -As of this writing, the default stack size is 2<<15 = 32kb. -Initialize the node registration's process\_log2\_n\_stack\_bytes member -as needed. The graph node dispatcher makes some effort to detect stack -overrun, e.g. by mapping a no-access page below each thread stack. - -Process node dispatch functions are expected to be "while(1) { }" loops -which suspend when not otherwise occupied, and which must not run for -unreasonably long periods of time. - -"Unreasonably long" is an application-dependent concept. Over the years, -we have constructed frame-size sensitive control-plane nodes which will -use a much higher fraction of the available CPU bandwidth when the frame -size is low. The classic example: modifying forwarding tables. So long -as the table-builder leaves the forwarding tables in a valid state, one -can suspend the table builder to avoid dropping packets as a result of -control-plane activity. - -Process nodes can suspend for fixed amounts of time, or until another -entity signals an event, or both. See the next section for a description -of the vlib process event mechanism. - -When running in vlib process context, one must pay strict attention to -loop invariant issues. If one walks a data structure and calls a -function which may suspend, one had best know by construction that it -cannot change. Often, it's best to simply make a snapshot copy of a data -structure, walk the copy at leisure, then free the copy. - -Process events --------------- - -The vlib process event mechanism API is extremely lightweight and easy -to use. Here is a typical example: - -```c - vlib_main_t *vm = &vlib_global_main; - uword event_type, * event_data = 0; - - while (1) - { - vlib_process_wait_for_event_or_clock (vm, 5.0 /* seconds */); - - event_type = vlib_process_get_events (vm, &event_data); - - switch (event_type) { - case EVENT1: - handle_event1s (event_data); - break; - - case EVENT2: - handle_event2s (event_data); - break; - - case ~0: /* 5-second idle/periodic */ - handle_idle (); - break; - - default: /* bug! */ - ASSERT (0); - } - - vec_reset_length(event_data); - } -``` - -In this example, the VLIB process node waits for an event to occur, or -for 5 seconds to elapse. The code demuxes on the event type, calling -the appropriate handler function. Each call to -vlib\_process\_get\_events returns a vector of per-event-type data -passed to successive vlib\_process\_signal\_event calls; it is a -serious error to process only event\_data\[0\]. - -Resetting the event\_data vector-length to 0 \[instead of calling -vec\_free\] means that the event scheme doesn't burn cycles continuously -allocating and freeing the event data vector. This is a common vppinfra -/ vlib coding pattern, well worth using when appropriate. - -Signaling an event is easy, for example: - -```c - vlib_process_signal_event (vm, process_node_index, EVENT1, - (uword)arbitrary_event1_data); /* and so forth */ -``` - -One can either know the process node index by construction - dig it out -of the appropriate vlib\_node\_registration\_t - or by finding the -vlib\_node\_t with vlib\_get\_node\_by\_name(...). - -Buffers -------- - -vlib buffering solves the usual set of packet-processing problems, -albeit at high performance. Key in terms of performance: one ordinarily -allocates / frees N buffers at a time rather than one at a time. Except -when operating directly on a specific buffer, one deals with buffers by -index, not by pointer. - -Packet-processing frames are u32\[\] arrays, not -vlib\_buffer\_t\[\] arrays. - -Packets comprise one or more vlib buffers, chained together as required. -Multiple particle sizes are supported; hardware input nodes simply ask -for the required size(s). Coalescing support is available. For obvious -reasons one is discouraged from writing one's own wild and wacky buffer -chain traversal code. - -vlib buffer headers are allocated immediately prior to the buffer data -area. In typical packet processing this saves a dependent read wait: -given a buffer's address, one can prefetch the buffer header -\[metadata\] at the same time as the first cache line of buffer data. - -Buffer header metadata (vlib\_buffer\_t) includes the usual rewrite -expansion space, a current\_data offset, RX and TX interface indices, -packet trace information, and a opaque areas. - -The opaque data is intended to control packet processing in arbitrary -subgraph-dependent ways. The programmer shoulders responsibility for -data lifetime analysis, type-checking, etc. - -Buffers have reference-counts in support of e.g. multicast replication. - -Shared-memory message API -------------------------- - -Local control-plane and application processes interact with the vpp -dataplane via asynchronous message-passing in shared memory over -unidirectional queues. The same application APIs are available via -sockets. - -Capturing API traces and replaying them in a simulation environment -requires a disciplined approach to the problem. This seems like a -make-work task, but it is not. When something goes wrong in the -control-plane after 300,000 or 3,000,000 operations, high-speed replay -of the events leading up to the accident is a huge win. - -The shared-memory message API message allocator vl\_api\_msg\_alloc uses -a particularly cute trick. Since messages are processed in order, we try -to allocate message buffering from a set of fixed-size, preallocated -rings. Each ring item has a "busy" bit. Freeing one of the preallocated -message buffers merely requires the message consumer to clear the busy -bit. No locking required. - -Debug CLI ---------- - -Adding debug CLI commands to VLIB applications is very simple. - -Here is a complete example: - -```c - static clib_error_t * - show_ip_tuple_match (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) - { - vlib_cli_output (vm, "%U\n", format_ip_tuple_match_tables, &routing_main); - return 0; - } - - /* *INDENT-OFF* */ - static VLIB_CLI_COMMAND (show_ip_tuple_command) = - { - .path = "show ip tuple match", - .short_help = "Show ip 5-tuple match-and-broadcast tables", - .function = show_ip_tuple_match, - }; - /* *INDENT-ON* */ -``` - -This example implements the "show ip tuple match" debug cli -command. In ordinary usage, the vlib cli is available via the "vppctl" -application, which sends traffic to a named pipe. One can configure -debug CLI telnet access on a configurable port. - -The cli implementation has an output redirection facility which makes it -simple to deliver cli output via shared-memory API messaging, - -Particularly for debug or "show tech support" type commands, it would be -wasteful to write vlib application code to pack binary data, write more -code elsewhere to unpack the data and finally print the answer. If a -certain cli command has the potential to hurt packet processing -performance by running for too long, do the work incrementally in a -process node. The client can wait. - -### Macro expansion - -The vpp debug CLI engine includes a recursive macro expander. This -is quite useful for factoring out address and/or interface name -specifics: - -``` - define ip1 192.168.1.1/24 - define ip2 192.168.2.1/24 - define iface1 GigabitEthernet3/0/0 - define iface2 loop1 - - set int ip address $iface1 $ip1 - set int ip address $iface2 $(ip2) - - undefine ip1 - undefine ip2 - undefine iface1 - undefine iface2 -``` - -Each socket (or telnet) debug CLI session has its own macro -tables. All debug CLI sessions which use CLI_INBAND binary API -messages share a single table. - -The macro expander recognizes circular defintions: - -``` - define foo \$(bar) - define bar \$(mumble) - define mumble \$(foo) -``` - -At 8 levels of recursion, the macro expander throws up its hands and -replies "CIRCULAR." - -### Macro-related debug CLI commands - -In addition to the "define" and "undefine" debug CLI commands, use -"show macro [noevaluate]" to dump the macro table. The "echo" debug -CLI command will evaluate and print its argument: - -``` - vpp# define foo This\ Is\ Foo - vpp# echo $foo - This Is Foo -``` - -Handing off buffers between threads ------------------------------------ - -Vlib includes an easy-to-use mechanism for handing off buffers between -worker threads. A typical use-case: software ingress flow hashing. At -a high level, one creates a per-worker-thread queue which sends packets -to a specific graph node in the indicated worker thread. With the -queue in hand, enqueue packets to the worker thread of your choice. - -### Initialize a handoff queue - -Simple enough, call vlib_frame_queue_main_init: - -```c - main_ptr->frame_queue_index - = vlib_frame_queue_main_init (dest_node.index, frame_queue_size); -``` - -Frame_queue_size means what it says: the number of frames which may be -queued. Since frames contain 1...256 packets, frame_queue_size should -be a reasonably small number (32...64). If the frame queue producer(s) -are faster than the frame queue consumer(s), congestion will -occur. Suggest letting the enqueue operator deal with queue -congestion, as shown in the enqueue example below. - -Under the floorboards, vlib_frame_queue_main_init creates an input queue -for each worker thread. - -Please do NOT create frame queues until it's clear that they will be -used. Although the main dispatch loop is reasonably smart about how -often it polls the (entire set of) frame queues, polling unused frame -queues is a waste of clock cycles. - -### Hand off packets - -The actual handoff mechanics are simple, and integrate nicely with -a typical graph-node dispatch function: - -```c - always_inline uword - do_handoff_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame, - int is_ip4, int is_trace) - { - u32 n_left_from, *from; - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; - u16 thread_indices [VLIB_FRAME_SIZE]; - u16 nexts[VLIB_FRAME_SIZE], *next; - u32 n_enq; - htest_main_t *hmp = &htest_main; - int i; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_get_buffers (vm, from, bufs, n_left_from); - next = nexts; - b = bufs; - - /* - * Typical frame traversal loop, details vary with - * use case. Make sure to set thread_indices[i] with - * the desired destination thread index. You may - * or may not bother to set next[i]. - */ - - for (i = 0; i < frame->n_vectors; i++) - { - <snip> - /* Pick a thread to handle this packet */ - thread_indices[i] = f (packet_data_or_whatever); - <snip> - - b += 1; - next += 1; - n_left_from -= 1; - } - - /* Enqueue buffers to threads */ - n_enq = - vlib_buffer_enqueue_to_thread (vm, node, hmp->frame_queue_index, - from, thread_indices, frame->n_vectors, - 1 /* drop on congestion */); - /* Typical counters, - if (n_enq < frame->n_vectors) - vlib_node_increment_counter (vm, node->node_index, - XXX_ERROR_CONGESTION_DROP, - frame->n_vectors - n_enq); - vlib_node_increment_counter (vm, node->node_index, - XXX_ERROR_HANDED_OFF, n_enq); - return frame->n_vectors; -} -``` - -Notes about calling vlib_buffer_enqueue_to_thread(...): - -* If you pass "drop on congestion" non-zero, all packets in the -inbound frame will be consumed one way or the other. This is the -recommended setting. - -* In the drop-on-congestion case, please don't try to "help" in the -enqueue node by freeing dropped packets, or by pushing them to -"error-drop." Either of those actions would be a severe error. - -* It's perfectly OK to enqueue packets to the current thread. - -Handoff Demo Plugin -------------------- - -Check out the sample (plugin) example in -.../src/examples/handoffdemo. If you want to build the handoff demo plugin: - -``` -$ cd .../src/plugins -$ ln -s ../examples/handoffdemo -``` - -This plugin provides a simple example of how to hand off packets -between threads. We used it to debug packet-tracer handoff tracing -support. - -# Packet generator input script - -``` - packet-generator new { - name x - limit 5 - size 128-128 - interface local0 - node handoffdemo-1 - data { - incrementing 30 - } - } -``` -# Start vpp with 2 worker threads - -The demo plugin hands packets from worker 1 to worker 2. - -# Enable tracing, and start the packet generator - -``` - trace add pg-input 100 - packet-generator enable -``` - -# Sample Run - -``` - DBGvpp# ex /tmp/pg_input_script - DBGvpp# pa en - DBGvpp# sh err - Count Node Reason - 5 handoffdemo-1 packets handed off processed - 5 handoffdemo-2 completed packets - DBGvpp# show run - Thread 1 vpp_wk_0 (lcore 0) - Time 133.9, average vectors/node 5.00, last 128 main loops 0.00 per node 0.00 - vector rates in 3.7331e-2, out 0.0000e0, drop 0.0000e0, punt 0.0000e0 - Name State Calls Vectors Suspends Clocks Vectors/Call - handoffdemo-1 active 1 5 0 4.76e3 5.00 - pg-input disabled 2 5 0 5.58e4 2.50 - unix-epoll-input polling 22760 0 0 2.14e7 0.00 - --------------- - Thread 2 vpp_wk_1 (lcore 2) - Time 133.9, average vectors/node 5.00, last 128 main loops 0.00 per node 0.00 - vector rates in 0.0000e0, out 0.0000e0, drop 3.7331e-2, punt 0.0000e0 - Name State Calls Vectors Suspends Clocks Vectors/Call - drop active 1 5 0 1.35e4 5.00 - error-drop active 1 5 0 2.52e4 5.00 - handoffdemo-2 active 1 5 0 2.56e4 5.00 - unix-epoll-input polling 22406 0 0 2.18e7 0.00 -``` - -Enable the packet tracer and run it again... - -``` - DBGvpp# trace add pg-input 100 - DBGvpp# pa en - DBGvpp# sh trace - sh trace - ------------------- Start of thread 0 vpp_main ------------------- - No packets in trace buffer - ------------------- Start of thread 1 vpp_wk_0 ------------------- - Packet 1 - - 00:06:50:520688: pg-input - stream x, 128 bytes, 0 sw_if_index - current data 0, length 128, buffer-pool 0, ref-count 1, trace handle 0x1000000 - 00000000: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d0000 - 00000020: 0000000000000000000000000000000000000000000000000000000000000000 - 00000040: 0000000000000000000000000000000000000000000000000000000000000000 - 00000060: 0000000000000000000000000000000000000000000000000000000000000000 - 00:06:50:520762: handoffdemo-1 - HANDOFFDEMO: current thread 1 - - Packet 2 - - 00:06:50:520688: pg-input - stream x, 128 bytes, 0 sw_if_index - current data 0, length 128, buffer-pool 0, ref-count 1, trace handle 0x1000001 - 00000000: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d0000 - 00000020: 0000000000000000000000000000000000000000000000000000000000000000 - 00000040: 0000000000000000000000000000000000000000000000000000000000000000 - 00000060: 0000000000000000000000000000000000000000000000000000000000000000 - 00:06:50:520762: handoffdemo-1 - HANDOFFDEMO: current thread 1 - - Packet 3 - - 00:06:50:520688: pg-input - stream x, 128 bytes, 0 sw_if_index - current data 0, length 128, buffer-pool 0, ref-count 1, trace handle 0x1000002 - 00000000: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d0000 - 00000020: 0000000000000000000000000000000000000000000000000000000000000000 - 00000040: 0000000000000000000000000000000000000000000000000000000000000000 - 00000060: 0000000000000000000000000000000000000000000000000000000000000000 - 00:06:50:520762: handoffdemo-1 - HANDOFFDEMO: current thread 1 - - Packet 4 - - 00:06:50:520688: pg-input - stream x, 128 bytes, 0 sw_if_index - current data 0, length 128, buffer-pool 0, ref-count 1, trace handle 0x1000003 - 00000000: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d0000 - 00000020: 0000000000000000000000000000000000000000000000000000000000000000 - 00000040: 0000000000000000000000000000000000000000000000000000000000000000 - 00000060: 0000000000000000000000000000000000000000000000000000000000000000 - 00:06:50:520762: handoffdemo-1 - HANDOFFDEMO: current thread 1 - - Packet 5 - - 00:06:50:520688: pg-input - stream x, 128 bytes, 0 sw_if_index - current data 0, length 128, buffer-pool 0, ref-count 1, trace handle 0x1000004 - 00000000: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d0000 - 00000020: 0000000000000000000000000000000000000000000000000000000000000000 - 00000040: 0000000000000000000000000000000000000000000000000000000000000000 - 00000060: 0000000000000000000000000000000000000000000000000000000000000000 - 00:06:50:520762: handoffdemo-1 - HANDOFFDEMO: current thread 1 - - ------------------- Start of thread 2 vpp_wk_1 ------------------- - Packet 1 - - 00:06:50:520796: handoff_trace - HANDED-OFF: from thread 1 trace index 0 - 00:06:50:520796: handoffdemo-2 - HANDOFFDEMO: current thread 2 - 00:06:50:520867: error-drop - rx:local0 - 00:06:50:520914: drop - handoffdemo-2: completed packets - - Packet 2 - - 00:06:50:520796: handoff_trace - HANDED-OFF: from thread 1 trace index 1 - 00:06:50:520796: handoffdemo-2 - HANDOFFDEMO: current thread 2 - 00:06:50:520867: error-drop - rx:local0 - 00:06:50:520914: drop - handoffdemo-2: completed packets - - Packet 3 - - 00:06:50:520796: handoff_trace - HANDED-OFF: from thread 1 trace index 2 - 00:06:50:520796: handoffdemo-2 - HANDOFFDEMO: current thread 2 - 00:06:50:520867: error-drop - rx:local0 - 00:06:50:520914: drop - handoffdemo-2: completed packets - - Packet 4 - - 00:06:50:520796: handoff_trace - HANDED-OFF: from thread 1 trace index 3 - 00:06:50:520796: handoffdemo-2 - HANDOFFDEMO: current thread 2 - 00:06:50:520867: error-drop - rx:local0 - 00:06:50:520914: drop - handoffdemo-2: completed packets - - Packet 5 - - 00:06:50:520796: handoff_trace - HANDED-OFF: from thread 1 trace index 4 - 00:06:50:520796: handoffdemo-2 - HANDOFFDEMO: current thread 2 - 00:06:50:520867: error-drop - rx:local0 - 00:06:50:520914: drop - handoffdemo-2: completed packets - DBGvpp# -``` |