1.. BSD LICENSE 2 Copyright(c) 2015 Intel Corporation. All rights reserved. 3 All rights reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions 7 are met: 8 9 * Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in 13 the documentation and/or other materials provided with the 14 distribution. 15 * Neither the name of Intel Corporation nor the names of its 16 contributors may be used to endorse or promote products derived 17 from this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 32RX/TX Callbacks Sample Application 33================================== 34 35The RX/TX Callbacks sample application is a packet forwarding application that 36demonstrates the use of user defined callbacks on received and transmitted 37packets. The application performs a simple latency check, using callbacks, to 38determine the time packets spend within the application. 39 40In the sample application a user defined callback is applied to all received 41packets to add a timestamp. A separate callback is applied to all packets 42prior to transmission to calculate the elapsed time, in CPU cycles. 43 44 45Compiling the Application 46------------------------- 47 48To compile the application export the path to the DPDK source tree and go to 49the example directory: 50 51.. code-block:: console 52 53 export RTE_SDK=/path/to/rte_sdk 54 55 cd ${RTE_SDK}/examples/rxtx_callbacks 56 57 58Set the target, for example: 59 60.. code-block:: console 61 62 export RTE_TARGET=x86_64-native-linuxapp-gcc 63 64See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values. 65 66The callbacks feature requires that the ``CONFIG_RTE_ETHDEV_RXTX_CALLBACKS`` 67setting is on in the ``config/common_`` config file that applies to the 68target. This is generally on by default: 69 70.. code-block:: console 71 72 CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y 73 74Build the application as follows: 75 76.. code-block:: console 77 78 make 79 80 81Running the Application 82----------------------- 83 84To run the example in a ``linuxapp`` environment: 85 86.. code-block:: console 87 88 ./build/rxtx_callbacks -l 1 -n 4 89 90Refer to *DPDK Getting Started Guide* for general information on running 91applications and the Environment Abstraction Layer (EAL) options. 92 93 94 95Explanation 96----------- 97 98The ``rxtx_callbacks`` application is mainly a simple forwarding application 99based on the :doc:`skeleton`. See that section of the documentation for more 100details of the forwarding part of the application. 101 102The sections below explain the additional RX/TX callback code. 103 104 105The Main Function 106~~~~~~~~~~~~~~~~~ 107 108The ``main()`` function performs the application initialization and calls the 109execution threads for each lcore. This function is effectively identical to 110the ``main()`` function explained in :doc:`skeleton`. 111 112The ``lcore_main()`` function is also identical. 113 114The main difference is in the user defined ``port_init()`` function where the 115callbacks are added. This is explained in the next section: 116 117 118The Port Initialization Function 119~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 120 121The main functional part of the port initialization is shown below with 122comments: 123 124.. code-block:: c 125 126 static inline int 127 port_init(uint16_t port, struct rte_mempool *mbuf_pool) 128 { 129 struct rte_eth_conf port_conf = port_conf_default; 130 const uint16_t rx_rings = 1, tx_rings = 1; 131 struct ether_addr addr; 132 int retval; 133 uint16_t q; 134 135 if (port >= rte_eth_dev_count()) 136 return -1; 137 138 /* Configure the Ethernet device. */ 139 retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); 140 if (retval != 0) 141 return retval; 142 143 /* Allocate and set up 1 RX queue per Ethernet port. */ 144 for (q = 0; q < rx_rings; q++) { 145 retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, 146 rte_eth_dev_socket_id(port), NULL, mbuf_pool); 147 if (retval < 0) 148 return retval; 149 } 150 151 /* Allocate and set up 1 TX queue per Ethernet port. */ 152 for (q = 0; q < tx_rings; q++) { 153 retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, 154 rte_eth_dev_socket_id(port), NULL); 155 if (retval < 0) 156 return retval; 157 } 158 159 /* Start the Ethernet port. */ 160 retval = rte_eth_dev_start(port); 161 if (retval < 0) 162 return retval; 163 164 /* Enable RX in promiscuous mode for the Ethernet device. */ 165 rte_eth_promiscuous_enable(port); 166 167 168 /* Add the callbacks for RX and TX.*/ 169 rte_eth_add_rx_callback(port, 0, add_timestamps, NULL); 170 rte_eth_add_tx_callback(port, 0, calc_latency, NULL); 171 172 return 0; 173 } 174 175 176The RX and TX callbacks are added to the ports/queues as function pointers: 177 178.. code-block:: c 179 180 rte_eth_add_rx_callback(port, 0, add_timestamps, NULL); 181 rte_eth_add_tx_callback(port, 0, calc_latency, NULL); 182 183More than one callback can be added and additional information can be passed 184to callback function pointers as a ``void*``. In the examples above ``NULL`` 185is used. 186 187The ``add_timestamps()`` and ``calc_latency()`` functions are explained below. 188 189 190The add_timestamps() Callback 191~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 192 193The ``add_timestamps()`` callback is added to the RX port and is applied to 194all packets received: 195 196.. code-block:: c 197 198 static uint16_t 199 add_timestamps(uint16_t port __rte_unused, uint16_t qidx __rte_unused, 200 struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused) 201 { 202 unsigned i; 203 uint64_t now = rte_rdtsc(); 204 205 for (i = 0; i < nb_pkts; i++) 206 pkts[i]->udata64 = now; 207 208 return nb_pkts; 209 } 210 211The DPDK function ``rte_rdtsc()`` is used to add a cycle count timestamp to 212each packet (see the *cycles* section of the *DPDK API Documentation* for 213details). 214 215 216The calc_latency() Callback 217~~~~~~~~~~~~~~~~~~~~~~~~~~~ 218 219The ``calc_latency()`` callback is added to the TX port and is applied to all 220packets prior to transmission: 221 222.. code-block:: c 223 224 static uint16_t 225 calc_latency(uint16_t port __rte_unused, uint16_t qidx __rte_unused, 226 struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused) 227 { 228 uint64_t cycles = 0; 229 uint64_t now = rte_rdtsc(); 230 unsigned i; 231 232 for (i = 0; i < nb_pkts; i++) 233 cycles += now - pkts[i]->udata64; 234 235 latency_numbers.total_cycles += cycles; 236 latency_numbers.total_pkts += nb_pkts; 237 238 if (latency_numbers.total_pkts > (100 * 1000 * 1000ULL)) { 239 printf("Latency = %"PRIu64" cycles\n", 240 latency_numbers.total_cycles / latency_numbers.total_pkts); 241 242 latency_numbers.total_cycles = latency_numbers.total_pkts = 0; 243 } 244 245 return nb_pkts; 246 } 247 248The ``calc_latency()`` function accumulates the total number of packets and 249the total number of cycles used. Once more than 100 million packets have been 250transmitted the average cycle count per packet is printed out and the counters 251are reset. 252