This patchset includes two new sample app guides. The first is for the existing basic forwarding/skeleton application. The second is for the recently added rxtx_callbacks sample application. John McNamara (3): examples/skeleton: minor refactoring to help documentation doc: add docs for basic forwarding skeleton app doc: add docs for the rxtx_callbacks sample app MAINTAINERS | 4 + doc/guides/sample_app_ug/index.rst | 4 +- doc/guides/sample_app_ug/rxtx_callbacks.rst | 251 ++++++++++++++++++++ doc/guides/sample_app_ug/skeleton.rst | 338 +++++++++++++++++++++++++++ examples/skeleton/basicfwd.c | 77 +++++-- 5 files changed, 653 insertions(+), 21 deletions(-) create mode 100644 doc/guides/sample_app_ug/rxtx_callbacks.rst create mode 100644 doc/guides/sample_app_ug/skeleton.rst -- 1.7.4.1
Minor refactoring and comments to make the sample app and code examples clearer for the sample app guide. Signed-off-by: John McNamara <john.mcnamara@intel.com> --- examples/skeleton/basicfwd.c | 77 +++++++++++++++++++++++++++++++----------- 1 files changed, 57 insertions(+), 20 deletions(-) diff --git a/examples/skeleton/basicfwd.c b/examples/skeleton/basicfwd.c index 6aa931e..1bce6e7 100644 --- a/examples/skeleton/basicfwd.c +++ b/examples/skeleton/basicfwd.c @@ -1,7 +1,7 @@ /*- * BSD LICENSE * - * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,12 +48,14 @@ #define BURST_SIZE 32 static const struct rte_eth_conf port_conf_default = { - .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN, }, + .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN } }; +/* basicfwd.c: Basic DPDK skeleton forwarding example. */ + /* - * Initialises a given port using global settings and with the rx buffers - * coming from the mbuf_pool passed as parameter + * Initializes a given port using global settings and with the RX buffers + * coming from the mbuf_pool passed as a parameter. */ static inline int port_init(uint8_t port, struct rte_mempool *mbuf_pool) @@ -66,10 +68,12 @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool) if (port >= rte_eth_dev_count()) return -1; + /* Configure the Ethernet device. */ retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); if (retval != 0) return retval; + /* Allocate and set up 1 RX queue per Ethernet port. */ for (q = 0; q < rx_rings; q++) { retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, rte_eth_dev_socket_id(port), NULL, mbuf_pool); @@ -77,6 +81,7 @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool) return retval; } + /* Allocate and set up 1 TX queue per Ethernet port. */ for (q = 0; q < tx_rings; q++) { retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, rte_eth_dev_socket_id(port), NULL); @@ -84,33 +89,41 @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool) return retval; } - retval = rte_eth_dev_start(port); + /* Start the Ethernet port. */ + retval = rte_eth_dev_start(port); if (retval < 0) return retval; + /* Display the port MAC address. */ struct ether_addr addr; rte_eth_macaddr_get(port, &addr); - printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 - " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", + printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8 + " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n", (unsigned)port, addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2], addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]); + /* Enable RX in promiscuous mode for the Ethernet device. */ rte_eth_promiscuous_enable(port); return 0; } /* - * Main thread that does the work, reading from INPUT_PORT - * and writing to OUTPUT_PORT + * The lcore main. This is the main thread that does the work, reading from + * an input port and writing to an output port. */ -static __attribute__((noreturn)) void +static __attribute__((noreturn)) void lcore_main(void) { const uint8_t nb_ports = rte_eth_dev_count(); uint8_t port; + + /* + * Check that the port is on the same NUMA node as the polling thread + * for best performance. + */ for (port = 0; port < nb_ports; port++) if (rte_eth_dev_socket_id(port) > 0 && rte_eth_dev_socket_id(port) != @@ -121,15 +134,28 @@ lcore_main(void) printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", rte_lcore_id()); + + /* Run until the application is quit or killed. */ for (;;) { + /* + * Receive packets on a port and forward them on the paired + * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. + */ for (port = 0; port < nb_ports; port++) { + + /* Get burst of RX packets, from first port of pair. */ struct rte_mbuf *bufs[BURST_SIZE]; const uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE); + if (unlikely(nb_rx == 0)) continue; + + /* Send burst of TX packets, to second port of pair. */ const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, bufs, nb_rx); + + /* Free any unsent packets. */ if (unlikely(nb_tx < nb_rx)) { uint16_t buf; for (buf = nb_tx; buf < nb_rx; buf++) @@ -139,7 +165,10 @@ lcore_main(void) } } -/* Main function, does initialisation and calls the per-lcore functions */ +/* + * The main function, which does initialization and calls the per-lcore + * functions. + */ int main(int argc, char *argv[]) { @@ -147,36 +176,44 @@ main(int argc, char *argv[]) unsigned nb_ports; uint8_t portid; - /* init EAL */ + /* Initialize the Environment Abstraction Layer (EAL). */ int ret = rte_eal_init(argc, argv); if (ret < 0) rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + argc -= ret; argv += ret; + /* Check that there is an even number of ports to send/receive on. */ nb_ports = rte_eth_dev_count(); if (nb_ports < 2 || (nb_ports & 1)) rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n"); - mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports, - MBUF_SIZE, MBUF_CACHE_SIZE, + /* Creates a new mempool in memory to hold the mbufs. */ + mbuf_pool = rte_mempool_create("MBUF_POOL", + NUM_MBUFS * nb_ports, + MBUF_SIZE, + MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, - rte_pktmbuf_init, NULL, - rte_socket_id(), 0); + rte_pktmbuf_init, NULL, + rte_socket_id(), + 0); + if (mbuf_pool == NULL) rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); - /* initialize all ports */ + /* Initialize all ports. */ for (portid = 0; portid < nb_ports; portid++) if (port_init(portid, mbuf_pool) != 0) - rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8"\n", + rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", portid); if (rte_lcore_count() > 1) - printf("\nWARNING: Too much enabled lcores - App uses only 1 lcore\n"); + printf("\nWARNING: Too many lcores enabled. Only 1 used.\n"); - /* call lcore_main on master core only */ + /* Call lcore_main on the master core only. */ lcore_main(); + return 0; } -- 1.7.4.1
Added a sample application guide for the basic forwarding /skeleton app. Signed-off-by: John McNamara <john.mcnamara@intel.com> --- MAINTAINERS | 3 + doc/guides/sample_app_ug/index.rst | 3 +- doc/guides/sample_app_ug/skeleton.rst | 338 +++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+), 1 deletions(-) create mode 100644 doc/guides/sample_app_ug/skeleton.rst diff --git a/MAINTAINERS b/MAINTAINERS index 349ad2b..86c1c6b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -444,7 +444,10 @@ M: Bruce Richardson <bruce.richardson@intel.com> M: John McNamara <john.mcnamara@intel.com> F: examples/rxtx_callbacks/ +M: Bruce Richardson <bruce.richardson@intel.com> +M: John McNamara <john.mcnamara@intel.com> F: examples/skeleton/ +F: doc/guides/sample_app_ug/skeleton.rst F: examples/vmdq/ F: examples/vmdq_dcb/ diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index 5720181..4e9d59b 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -1,5 +1,5 @@ .. BSD LICENSE - Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + Copyright(c) 2010-2015 Intel Corporation. All rights reserved. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ Sample Applications User Guide cmd_line exception_path hello_world + skeleton ip_frag ipv4_multicast ip_reassembly diff --git a/doc/guides/sample_app_ug/skeleton.rst b/doc/guides/sample_app_ug/skeleton.rst new file mode 100644 index 0000000..e832c13 --- /dev/null +++ b/doc/guides/sample_app_ug/skeleton.rst @@ -0,0 +1,338 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Basic Forwarding Sample Application +=================================== + +The Basic Forwarding sample application is a simple *skeleton* example of a +forwarding application. + +It is intended as a demonstration of the basic components of a DPDK forwarding +application. For more detailed implementations see the L2 and L3 forwarding +sample applications. + + +Compiling the Application +------------------------- + +To compile the application export the path to the DPDK source tree and go to +the example directory: + +.. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + + cd ${RTE_SDK}/examples/skeleton + +Set the target, for example: + +.. code-block:: console + + export RTE_TARGET=x86_64-native-linuxapp-gcc + +See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values. + +Build the application as follows: + +.. code-block:: console + + make + + +Running the Application +----------------------- + +To run the example in a ``linuxapp`` environment: + +.. code-block:: console + + ./build/basicfwd -c 2 -n 4 + +Refer to *DPDK Getting Started Guide* for general information on running +applications and the Environment Abstraction Layer (EAL) options. + + +Explanation +----------- + +The following sections provide an explanation of the main components of the +code. + +All DPDK library functions used in the sample code are prefixed with ``rte_`` +and are explained in detail in the *DPDK API Documentation*. + + +The Main Function +~~~~~~~~~~~~~~~~~ + +The ``main()`` function performs the initialization and calls the execution +threads for each lcore. + +The first task is to initialize the Environment Abstraction Layer (EAL). The +``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` +function. The value returned is the number of parsed arguments: + +.. code-block:: c + + int ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + + +The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) +used by the application: + +.. code-block:: c + + mbuf_pool = rte_mempool_create("MBUF_POOL", + NUM_MBUFS * nb_ports, + MBUF_SIZE, + MBUF_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), + 0); + +Mbufs are the packet buffer structure used by DPDK. They are explained in +detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. + +The ``main()`` function also initializes all the ports using the user defined +``port_init()`` function which is explained in the next section: + +.. code-block:: c + + for (portid = 0; portid < nb_ports; portid++) { + if (port_init(portid, mbuf_pool) != 0) { + rte_exit(EXIT_FAILURE, + "Cannot init port %" PRIu8 "\n", portid); + } + } + + +Once the initialization is complete, the application is ready to launch a +function on an lcore. In this example ``lcore_main()`` is called on a single +lcore. + + +.. code-block:: c + + lcore_main(); + +The ``lcore_main()`` function is explained below. + + + +The Port Initialization Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The main functional part of the port initialization used in the Basic +Forwarding application is shown below: + +.. code-block:: c + + static inline int + port_init(uint8_t port, struct rte_mempool *mbuf_pool) + { + struct rte_eth_conf port_conf = port_conf_default; + const uint16_t rx_rings = 1, tx_rings = 1; + struct ether_addr addr; + int retval; + uint16_t q; + + if (port >= rte_eth_dev_count()) + return -1; + + /* Configure the Ethernet device. */ + retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); + if (retval != 0) + return retval; + + /* Allocate and set up 1 RX queue per Ethernet port. */ + for (q = 0; q < rx_rings; q++) { + retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, + rte_eth_dev_socket_id(port), NULL, mbuf_pool); + if (retval < 0) + return retval; + } + + /* Allocate and set up 1 TX queue per Ethernet port. */ + for (q = 0; q < tx_rings; q++) { + retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, + rte_eth_dev_socket_id(port), NULL); + if (retval < 0) + return retval; + } + + /* Start the Ethernet port. */ + retval = rte_eth_dev_start(port); + if (retval < 0) + return retval; + + /* Enable RX in promiscuous mode for the Ethernet device. */ + rte_eth_promiscuous_enable(port); + + return 0; + } + +The Ethernet ports are configured with default settings using the +``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct: + +.. code-block:: c + + static const struct rte_eth_conf port_conf_default = { + .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN } + }; + +For this example the ports are set up with 1 RX and 1 TX queue using the +``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions. + +The Ethernet port is then started: + +.. code-block:: c + + retval = rte_eth_dev_start(port); + + +Finally the RX port is set in promiscuous mode: + +.. code-block:: c + + rte_eth_promiscuous_enable(port); + + +The Lcores Main +~~~~~~~~~~~~~~~ + +As we saw above the ``main()`` function calls an application function on the +available lcores. For the Basic Forwarding application the lcore function +looks like the following: + +.. code-block:: c + + static __attribute__((noreturn)) void + lcore_main(void) + { + const uint8_t nb_ports = rte_eth_dev_count(); + uint8_t port; + + /* + * Check that the port is on the same NUMA node as the polling thread + * for best performance. + */ + for (port = 0; port < nb_ports; port++) + if (rte_eth_dev_socket_id(port) > 0 && + rte_eth_dev_socket_id(port) != + (int)rte_socket_id()) + printf("WARNING, port %u is on remote NUMA node to " + "polling thread.\n\tPerformance will " + "not be optimal.\n", port); + + printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", + rte_lcore_id()); + + /* Run until the application is quit or killed. */ + for (;;) { + /* + * Receive packets on a port and forward them on the paired + * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. + */ + for (port = 0; port < nb_ports; port++) { + + /* Get burst of RX packets, from first port of pair. */ + struct rte_mbuf *bufs[BURST_SIZE]; + const uint16_t nb_rx = rte_eth_rx_burst(port, 0, + bufs, BURST_SIZE); + + if (unlikely(nb_rx == 0)) + continue; + + /* Send burst of TX packets, to second port of pair. */ + const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, + bufs, nb_rx); + + /* Free any unsent packets. */ + if (unlikely(nb_tx < nb_rx)) { + uint16_t buf; + for (buf = nb_tx; buf < nb_rx; buf++) + rte_pktmbuf_free(bufs[buf]); + } + } + } + } + + +The main work of the application is done within the loop: + +.. code-block:: c + + for (;;) { + for (port = 0; port < nb_ports; port++) { + + /* Get burst of RX packets, from first port of pair. */ + struct rte_mbuf *bufs[BURST_SIZE]; + const uint16_t nb_rx = rte_eth_rx_burst(port, 0, + bufs, BURST_SIZE); + + if (unlikely(nb_rx == 0)) + continue; + + /* Send burst of TX packets, to second port of pair. */ + const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, + bufs, nb_rx); + + /* Free any unsent packets. */ + if (unlikely(nb_tx < nb_rx)) { + uint16_t buf; + for (buf = nb_tx; buf < nb_rx; buf++) + rte_pktmbuf_free(bufs[buf]); + } + } + } + +Packets are received in bursts on the RX ports and transmitted in bursts on +the TX ports. The ports are grouped in pairs with a simple mapping scheme +using the an XOR on the port number:: + + 0 -> 1 + 1 -> 0 + + 2 -> 3 + 3 -> 2 + + etc. + +The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that +are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they +must be freed explicitly using ``rte_pktmbuf_free()``. + +The forwarding loop can be interrupted and the application closed using +``Ctrl-C``. -- 1.7.4.1
Added a sample application guide for the rxtx_callbacks app. Signed-off-by: John McNamara <john.mcnamara@intel.com> --- MAINTAINERS | 1 + doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/rxtx_callbacks.rst | 251 +++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 0 deletions(-) create mode 100644 doc/guides/sample_app_ug/rxtx_callbacks.rst diff --git a/MAINTAINERS b/MAINTAINERS index 86c1c6b..2ddb312 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -443,6 +443,7 @@ F: doc/guides/sample_app_ug/quota_watermark.rst M: Bruce Richardson <bruce.richardson@intel.com> M: John McNamara <john.mcnamara@intel.com> F: examples/rxtx_callbacks/ +F: doc/guides/sample_app_ug/rxtx_callbacks.rst M: Bruce Richardson <bruce.richardson@intel.com> M: John McNamara <john.mcnamara@intel.com> diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index 4e9d59b..4a86459 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -44,6 +44,7 @@ Sample Applications User Guide exception_path hello_world skeleton + rxtx_callbacks ip_frag ipv4_multicast ip_reassembly diff --git a/doc/guides/sample_app_ug/rxtx_callbacks.rst b/doc/guides/sample_app_ug/rxtx_callbacks.rst new file mode 100644 index 0000000..9df57ed --- /dev/null +++ b/doc/guides/sample_app_ug/rxtx_callbacks.rst @@ -0,0 +1,251 @@ +.. BSD LICENSE + Copyright(c) 2015 Intel Corporation. All rights reserved. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +RX/TX Callbacks Sample Application +================================== + +The RX/TX Callbacks sample application is a packet forwarding application that +demonstrates the use of user defined callbacks on received and transmitted +packets. The application performs a simple latency check, using callbacks, to +determine the time packets spend within the application. + +In the sample application a user defined callback is applied to all received +packets to add a timestamp. A separate callback is applied to all packets +prior to transmission to calculate the elapsed time, in CPU cycles. + + +Compiling the Application +------------------------- + +To compile the application export the path to the DPDK source tree and go to +the example directory: + +.. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + + cd ${RTE_SDK}/examples/rxtx_callbacks + + +Set the target, for example: + +.. code-block:: console + + export RTE_TARGET=x86_64-native-linuxapp-gcc + +See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values. + +The callbacks feature requires that the ``CONFIG_RTE_ETHDEV_RXTX_CALLBACKS`` +setting is on in the ``config/common_`` config file that applies to the +target. This is generally on by default: + +.. code-block:: console + + CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y + +Build the application as follows: + +.. code-block:: console + + make + + +Running the Application +----------------------- + +To run the example in a ``linuxapp`` environment: + +.. code-block:: console + + ./build/rxtx_callbacks -c 2 -n 4 + +Refer to *DPDK Getting Started Guide* for general information on running +applications and the Environment Abstraction Layer (EAL) options. + + + +Explanation +----------- + +The ``rxtx_callbacks`` application is mainly a simple forwarding application +based on the :doc:`skeleton`. See that section of the documentation for more +details of the forwarding part of the application. + +The sections below explain the additional RX/TX callback code. + + +The Main Function +~~~~~~~~~~~~~~~~~ + +The ``main()`` function performs the application initialization and calls the +execution threads for each lcore. This function is effectively identical to +the ``main()`` function explained in :doc:`skeleton`. + +The ``lcore_main()`` function is also identical. + +The main difference is in the user defined ``port_init()`` function where the +callbacks are added. This is explained in the next section: + + +The Port Initialization Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The main functional part of the port initialization is shown below with +comments: + +.. code-block:: c + + static inline int + port_init(uint8_t port, struct rte_mempool *mbuf_pool) + { + struct rte_eth_conf port_conf = port_conf_default; + const uint16_t rx_rings = 1, tx_rings = 1; + struct ether_addr addr; + int retval; + uint16_t q; + + if (port >= rte_eth_dev_count()) + return -1; + + /* Configure the Ethernet device. */ + retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); + if (retval != 0) + return retval; + + /* Allocate and set up 1 RX queue per Ethernet port. */ + for (q = 0; q < rx_rings; q++) { + retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, + rte_eth_dev_socket_id(port), NULL, mbuf_pool); + if (retval < 0) + return retval; + } + + /* Allocate and set up 1 TX queue per Ethernet port. */ + for (q = 0; q < tx_rings; q++) { + retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, + rte_eth_dev_socket_id(port), NULL); + if (retval < 0) + return retval; + } + + /* Start the Ethernet port. */ + retval = rte_eth_dev_start(port); + if (retval < 0) + return retval; + + /* Enable RX in promiscuous mode for the Ethernet device. */ + rte_eth_promiscuous_enable(port); + + + /* Add the callbacks for RX and TX.*/ + rte_eth_add_rx_callback(port, 0, add_timestamps, NULL); + rte_eth_add_tx_callback(port, 0, calc_latency, NULL); + + return 0; + } + + +The RX and TX callbacks are added to the ports/queues as function pointers: + +.. code-block:: c + + rte_eth_add_rx_callback(port, 0, add_timestamps, NULL); + rte_eth_add_tx_callback(port, 0, calc_latency, NULL); + +More than one callback can be added and additional information can be passed +to callback function pointers as a ``void*``. In the examples above ``NULL`` +is used. + +The ``add_timestamps()`` and ``calc_latency()`` functions are explained below. + + +The add_timestamps() Callback +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``add_timestamps()`` callback is added to the RX port and is applied to +all packets received: + +.. code-block:: c + + static uint16_t + add_timestamps(uint8_t port __rte_unused, uint16_t qidx __rte_unused, + struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused) + { + unsigned i; + uint64_t now = rte_rdtsc(); + + for (i = 0; i < nb_pkts; i++) + pkts[i]->udata64 = now; + + return nb_pkts; + } + +The DPDK function ``rte_rdtsc()`` is used to add a cycle count timestamp to +each packet (see the *cycles* section of the *DPDK API Documentation* for +details). + + +The calc_latency() Callback +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``calc_latency()`` callback is added to the TX port and is applied to all +packets prior to transmission: + +.. code-block:: c + + static uint16_t + calc_latency(uint8_t port __rte_unused, uint16_t qidx __rte_unused, + struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused) + { + uint64_t cycles = 0; + uint64_t now = rte_rdtsc(); + unsigned i; + + for (i = 0; i < nb_pkts; i++) + cycles += now - pkts[i]->udata64; + + latency_numbers.total_cycles += cycles; + latency_numbers.total_pkts += nb_pkts; + + if (latency_numbers.total_pkts > (100 * 1000 * 1000ULL)) { + printf("Latency = %"PRIu64" cycles\n", + latency_numbers.total_cycles / latency_numbers.total_pkts); + + latency_numbers.total_cycles = latency_numbers.total_pkts = 0; + } + + return nb_pkts; + } + +The ``calc_latency()`` function accumulates the total number of packets and +the total number of cycles used. Once more than 100 million packets have been +transmitted the average cycle count per packet is printed out and the counters +are reset. -- 1.7.4.1
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of John McNamara
> Sent: Wednesday, February 25, 2015 7:46 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH 0/3] additional sample app guides
>
> This patchset includes two new sample app guides.
>
> The first is for the existing basic forwarding/skeleton application.
>
> The second is for the recently added rxtx_callbacks sample application.
>
>
>
> John McNamara (3):
> examples/skeleton: minor refactoring to help documentation
> doc: add docs for basic forwarding skeleton app
> doc: add docs for the rxtx_callbacks sample app
>
> MAINTAINERS | 4 +
> doc/guides/sample_app_ug/index.rst | 4 +-
> doc/guides/sample_app_ug/rxtx_callbacks.rst | 251
> ++++++++++++++++++++
> doc/guides/sample_app_ug/skeleton.rst | 338
> +++++++++++++++++++++++++++
> examples/skeleton/basicfwd.c | 77 +++++--
> 5 files changed, 653 insertions(+), 21 deletions(-)
> create mode 100644 doc/guides/sample_app_ug/rxtx_callbacks.rst
> create mode 100644 doc/guides/sample_app_ug/skeleton.rst
>
> --
> 1.7.4.1
Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of John McNamara
> Sent: Wednesday, February 25, 2015 7:46 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH 1/3] examples/skeleton: minor refactoring to
> help documentation
>
> Minor refactoring and comments to make the sample app and code
> examples clearer for the sample app guide.
>
> Signed-off-by: John McNamara <john.mcnamara@intel.com>
> ---
> examples/skeleton/basicfwd.c | 77
> +++++++++++++++++++++++++++++++-----------
> 1 files changed, 57 insertions(+), 20 deletions(-)
>
> diff --git a/examples/skeleton/basicfwd.c b/examples/skeleton/basicfwd.c
> index 6aa931e..1bce6e7 100644
> --- a/examples/skeleton/basicfwd.c
> +++ b/examples/skeleton/basicfwd.c
> @@ -1,7 +1,7 @@
> /*-
> * BSD LICENSE
> *
> - * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> * All rights reserved.
> *
> * Redistribution and use in source and binary forms, with or without
> @@ -48,12 +48,14 @@
> #define BURST_SIZE 32
>
> static const struct rte_eth_conf port_conf_default = {
> - .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN, },
> + .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
> };
>
> +/* basicfwd.c: Basic DPDK skeleton forwarding example. */
> +
> /*
> - * Initialises a given port using global settings and with the rx buffers
> - * coming from the mbuf_pool passed as parameter
> + * Initializes a given port using global settings and with the RX
> + buffers
> + * coming from the mbuf_pool passed as a parameter.
> */
> static inline int
> port_init(uint8_t port, struct rte_mempool *mbuf_pool) @@ -66,10 +68,12
> @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool)
> if (port >= rte_eth_dev_count())
> return -1;
>
> + /* Configure the Ethernet device. */
> retval = rte_eth_dev_configure(port, rx_rings, tx_rings,
> &port_conf);
> if (retval != 0)
> return retval;
>
> + /* Allocate and set up 1 RX queue per Ethernet port. */
> for (q = 0; q < rx_rings; q++) {
> retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
> rte_eth_dev_socket_id(port), NULL,
> mbuf_pool); @@ -77,6 +81,7 @@ port_init(uint8_t port, struct rte_mempool
> *mbuf_pool)
> return retval;
> }
>
> + /* Allocate and set up 1 TX queue per Ethernet port. */
> for (q = 0; q < tx_rings; q++) {
> retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
> rte_eth_dev_socket_id(port), NULL); @@ -
> 84,33 +89,41 @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool)
> return retval;
> }
>
> - retval = rte_eth_dev_start(port);
> + /* Start the Ethernet port. */
> + retval = rte_eth_dev_start(port);
> if (retval < 0)
> return retval;
>
> + /* Display the port MAC address. */
> struct ether_addr addr;
> rte_eth_macaddr_get(port, &addr);
> - printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
> - " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
> + printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
> + " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
> (unsigned)port,
> addr.addr_bytes[0], addr.addr_bytes[1],
> addr.addr_bytes[2], addr.addr_bytes[3],
> addr.addr_bytes[4], addr.addr_bytes[5]);
>
> + /* Enable RX in promiscuous mode for the Ethernet device. */
> rte_eth_promiscuous_enable(port);
>
> return 0;
> }
>
> /*
> - * Main thread that does the work, reading from INPUT_PORT
> - * and writing to OUTPUT_PORT
> + * The lcore main. This is the main thread that does the work, reading
> + from
> + * an input port and writing to an output port.
> */
> -static __attribute__((noreturn)) void
> +static __attribute__((noreturn)) void
> lcore_main(void)
> {
> const uint8_t nb_ports = rte_eth_dev_count();
> uint8_t port;
> +
> + /*
> + * Check that the port is on the same NUMA node as the polling
> thread
> + * for best performance.
> + */
> for (port = 0; port < nb_ports; port++)
> if (rte_eth_dev_socket_id(port) > 0 &&
> rte_eth_dev_socket_id(port) !=
> @@ -121,15 +134,28 @@ lcore_main(void)
>
> printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
> rte_lcore_id());
> +
> + /* Run until the application is quit or killed. */
> for (;;) {
> + /*
> + * Receive packets on a port and forward them on the paired
> + * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
> + */
> for (port = 0; port < nb_ports; port++) {
> +
> + /* Get burst of RX packets, from first port of pair. */
> struct rte_mbuf *bufs[BURST_SIZE];
> const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
> bufs, BURST_SIZE);
> +
> if (unlikely(nb_rx == 0))
> continue;
> +
> + /* Send burst of TX packets, to second port of pair. */
> const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
> bufs, nb_rx);
> +
> + /* Free any unsent packets. */
> if (unlikely(nb_tx < nb_rx)) {
> uint16_t buf;
> for (buf = nb_tx; buf < nb_rx; buf++) @@ -
> 139,7 +165,10 @@ lcore_main(void)
> }
> }
>
> -/* Main function, does initialisation and calls the per-lcore functions */
> +/*
> + * The main function, which does initialization and calls the per-lcore
> + * functions.
> + */
> int
> main(int argc, char *argv[])
> {
> @@ -147,36 +176,44 @@ main(int argc, char *argv[])
> unsigned nb_ports;
> uint8_t portid;
>
> - /* init EAL */
> + /* Initialize the Environment Abstraction Layer (EAL). */
> int ret = rte_eal_init(argc, argv);
> if (ret < 0)
> rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
> +
> argc -= ret;
> argv += ret;
>
> + /* Check that there is an even number of ports to send/receive on.
> */
> nb_ports = rte_eth_dev_count();
> if (nb_ports < 2 || (nb_ports & 1))
> rte_exit(EXIT_FAILURE, "Error: number of ports must be
> even\n");
>
> - mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS *
> nb_ports,
> - MBUF_SIZE, MBUF_CACHE_SIZE,
> + /* Creates a new mempool in memory to hold the mbufs. */
> + mbuf_pool = rte_mempool_create("MBUF_POOL",
> + NUM_MBUFS * nb_ports,
> + MBUF_SIZE,
> + MBUF_CACHE_SIZE,
> sizeof(struct rte_pktmbuf_pool_private),
> rte_pktmbuf_pool_init, NULL,
> - rte_pktmbuf_init, NULL,
> - rte_socket_id(), 0);
> + rte_pktmbuf_init, NULL,
> + rte_socket_id(),
> + 0);
> +
> if (mbuf_pool == NULL)
> rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
>
> - /* initialize all ports */
> + /* Initialize all ports. */
> for (portid = 0; portid < nb_ports; portid++)
> if (port_init(portid, mbuf_pool) != 0)
> - rte_exit(EXIT_FAILURE, "Cannot init port
> %"PRIu8"\n",
> + rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8
> "\n",
> portid);
>
> if (rte_lcore_count() > 1)
> - printf("\nWARNING: Too much enabled lcores - App uses
> only 1 lcore\n");
> + printf("\nWARNING: Too many lcores enabled. Only 1
> used.\n");
>
> - /* call lcore_main on master core only */
> + /* Call lcore_main on the master core only. */
> lcore_main();
> +
> return 0;
> }
> --
> 1.7.4.1
Acked-by: Siobhan Butler <siobhan.a.butler@intel.com>
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of John McNamara
> Sent: Wednesday, February 25, 2015 7:46 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH 3/3] doc: add docs for the rxtx_callbacks sample
> app
>
> Added a sample application guide for the rxtx_callbacks app.
>
> Signed-off-by: John McNamara <john.mcnamara@intel.com>
> ---
> MAINTAINERS | 1 +
> doc/guides/sample_app_ug/index.rst | 1 +
> doc/guides/sample_app_ug/rxtx_callbacks.rst | 251
> +++++++++++++++++++++++++++
> 3 files changed, 253 insertions(+), 0 deletions(-) create mode 100644
> doc/guides/sample_app_ug/rxtx_callbacks.rst
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 86c1c6b..2ddb312 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -443,6 +443,7 @@ F: doc/guides/sample_app_ug/quota_watermark.rst
> M: Bruce Richardson <bruce.richardson@intel.com>
> M: John McNamara <john.mcnamara@intel.com>
> F: examples/rxtx_callbacks/
> +F: doc/guides/sample_app_ug/rxtx_callbacks.rst
>
> M: Bruce Richardson <bruce.richardson@intel.com>
> M: John McNamara <john.mcnamara@intel.com> diff --git
> a/doc/guides/sample_app_ug/index.rst
> b/doc/guides/sample_app_ug/index.rst
> index 4e9d59b..4a86459 100644
> --- a/doc/guides/sample_app_ug/index.rst
> +++ b/doc/guides/sample_app_ug/index.rst
> @@ -44,6 +44,7 @@ Sample Applications User Guide
> exception_path
> hello_world
> skeleton
> + rxtx_callbacks
> ip_frag
> ipv4_multicast
> ip_reassembly
> diff --git a/doc/guides/sample_app_ug/rxtx_callbacks.rst
> b/doc/guides/sample_app_ug/rxtx_callbacks.rst
> new file mode 100644
> index 0000000..9df57ed
> --- /dev/null
> +++ b/doc/guides/sample_app_ug/rxtx_callbacks.rst
> @@ -0,0 +1,251 @@
> +.. BSD LICENSE
> + Copyright(c) 2015 Intel Corporation. All rights reserved.
> + All rights reserved.
> +
> + Redistribution and use in source and binary forms, with or without
> + modification, are permitted provided that the following conditions
> + are met:
> +
> + * Redistributions of source code must retain the above copyright
> + notice, this list of conditions and the following disclaimer.
> + * Redistributions in binary form must reproduce the above copyright
> + notice, this list of conditions and the following disclaimer in
> + the documentation and/or other materials provided with the
> + distribution.
> + * Neither the name of Intel Corporation nor the names of its
> + contributors may be used to endorse or promote products derived
> + from this software without specific prior written permission.
> +
> + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> +
> +
> +RX/TX Callbacks Sample Application
> +==================================
> +
> +The RX/TX Callbacks sample application is a packet forwarding
> +application that demonstrates the use of user defined callbacks on
> +received and transmitted packets. The application performs a simple
> +latency check, using callbacks, to determine the time packets spend within
> the application.
> +
> +In the sample application a user defined callback is applied to all
> +received packets to add a timestamp. A separate callback is applied to
> +all packets prior to transmission to calculate the elapsed time, in CPU cycles.
> +
> +
> +Compiling the Application
> +-------------------------
> +
> +To compile the application export the path to the DPDK source tree and
> +go to the example directory:
> +
> +.. code-block:: console
> +
> + export RTE_SDK=/path/to/rte_sdk
> +
> + cd ${RTE_SDK}/examples/rxtx_callbacks
> +
> +
> +Set the target, for example:
> +
> +.. code-block:: console
> +
> + export RTE_TARGET=x86_64-native-linuxapp-gcc
> +
> +See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
> +
> +The callbacks feature requires that the
> +``CONFIG_RTE_ETHDEV_RXTX_CALLBACKS``
> +setting is on in the ``config/common_`` config file that applies to the
> +target. This is generally on by default:
> +
> +.. code-block:: console
> +
> + CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
> +
> +Build the application as follows:
> +
> +.. code-block:: console
> +
> + make
> +
> +
> +Running the Application
> +-----------------------
> +
> +To run the example in a ``linuxapp`` environment:
> +
> +.. code-block:: console
> +
> + ./build/rxtx_callbacks -c 2 -n 4
> +
> +Refer to *DPDK Getting Started Guide* for general information on
> +running applications and the Environment Abstraction Layer (EAL) options.
> +
> +
> +
> +Explanation
> +-----------
> +
> +The ``rxtx_callbacks`` application is mainly a simple forwarding
> +application based on the :doc:`skeleton`. See that section of the
> +documentation for more details of the forwarding part of the application.
> +
> +The sections below explain the additional RX/TX callback code.
> +
> +
> +The Main Function
> +~~~~~~~~~~~~~~~~~
> +
> +The ``main()`` function performs the application initialization and
> +calls the execution threads for each lcore. This function is
> +effectively identical to the ``main()`` function explained in :doc:`skeleton`.
> +
> +The ``lcore_main()`` function is also identical.
> +
> +The main difference is in the user defined ``port_init()`` function
> +where the callbacks are added. This is explained in the next section:
> +
> +
> +The Port Initialization Function
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The main functional part of the port initialization is shown below with
> +comments:
> +
> +.. code-block:: c
> +
> + static inline int
> + port_init(uint8_t port, struct rte_mempool *mbuf_pool)
> + {
> + struct rte_eth_conf port_conf = port_conf_default;
> + const uint16_t rx_rings = 1, tx_rings = 1;
> + struct ether_addr addr;
> + int retval;
> + uint16_t q;
> +
> + if (port >= rte_eth_dev_count())
> + return -1;
> +
> + /* Configure the Ethernet device. */
> + retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
> + if (retval != 0)
> + return retval;
> +
> + /* Allocate and set up 1 RX queue per Ethernet port. */
> + for (q = 0; q < rx_rings; q++) {
> + retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
> + rte_eth_dev_socket_id(port), NULL, mbuf_pool);
> + if (retval < 0)
> + return retval;
> + }
> +
> + /* Allocate and set up 1 TX queue per Ethernet port. */
> + for (q = 0; q < tx_rings; q++) {
> + retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
> + rte_eth_dev_socket_id(port), NULL);
> + if (retval < 0)
> + return retval;
> + }
> +
> + /* Start the Ethernet port. */
> + retval = rte_eth_dev_start(port);
> + if (retval < 0)
> + return retval;
> +
> + /* Enable RX in promiscuous mode for the Ethernet device. */
> + rte_eth_promiscuous_enable(port);
> +
> +
> + /* Add the callbacks for RX and TX.*/
> + rte_eth_add_rx_callback(port, 0, add_timestamps, NULL);
> + rte_eth_add_tx_callback(port, 0, calc_latency, NULL);
> +
> + return 0;
> + }
> +
> +
> +The RX and TX callbacks are added to the ports/queues as function pointers:
> +
> +.. code-block:: c
> +
> + rte_eth_add_rx_callback(port, 0, add_timestamps, NULL);
> + rte_eth_add_tx_callback(port, 0, calc_latency, NULL);
> +
> +More than one callback can be added and additional information can be
> +passed to callback function pointers as a ``void*``. In the examples
> +above ``NULL`` is used.
> +
> +The ``add_timestamps()`` and ``calc_latency()`` functions are explained
> below.
> +
> +
> +The add_timestamps() Callback
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The ``add_timestamps()`` callback is added to the RX port and is
> +applied to all packets received:
> +
> +.. code-block:: c
> +
> + static uint16_t
> + add_timestamps(uint8_t port __rte_unused, uint16_t qidx
> __rte_unused,
> + struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused)
> + {
> + unsigned i;
> + uint64_t now = rte_rdtsc();
> +
> + for (i = 0; i < nb_pkts; i++)
> + pkts[i]->udata64 = now;
> +
> + return nb_pkts;
> + }
> +
> +The DPDK function ``rte_rdtsc()`` is used to add a cycle count
> +timestamp to each packet (see the *cycles* section of the *DPDK API
> +Documentation* for details).
> +
> +
> +The calc_latency() Callback
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The ``calc_latency()`` callback is added to the TX port and is applied
> +to all packets prior to transmission:
> +
> +.. code-block:: c
> +
> + static uint16_t
> + calc_latency(uint8_t port __rte_unused, uint16_t qidx __rte_unused,
> + struct rte_mbuf **pkts, uint16_t nb_pkts, void *_ __rte_unused)
> + {
> + uint64_t cycles = 0;
> + uint64_t now = rte_rdtsc();
> + unsigned i;
> +
> + for (i = 0; i < nb_pkts; i++)
> + cycles += now - pkts[i]->udata64;
> +
> + latency_numbers.total_cycles += cycles;
> + latency_numbers.total_pkts += nb_pkts;
> +
> + if (latency_numbers.total_pkts > (100 * 1000 * 1000ULL)) {
> + printf("Latency = %"PRIu64" cycles\n",
> + latency_numbers.total_cycles /
> + latency_numbers.total_pkts);
> +
> + latency_numbers.total_cycles = latency_numbers.total_pkts = 0;
> + }
> +
> + return nb_pkts;
> + }
> +
> +The ``calc_latency()`` function accumulates the total number of packets
> +and the total number of cycles used. Once more than 100 million packets
> +have been transmitted the average cycle count per packet is printed out
> +and the counters are reset.
> --
> 1.7.4.1
Acked-by: Siobhan Butler <siobhan.a.butler@intel.com>
> > This patchset includes two new sample app guides.
> >
> > The first is for the existing basic forwarding/skeleton application.
> >
> > The second is for the recently added rxtx_callbacks sample application.
> >
> > John McNamara (3):
> > examples/skeleton: minor refactoring to help documentation
> > doc: add docs for basic forwarding skeleton app
> > doc: add docs for the rxtx_callbacks sample app
>
> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Applied, thanks