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 32PTP Client Sample Application 33============================= 34 35The PTP (Precision Time Protocol) client sample application is a simple 36example of using the DPDK IEEE1588 API to communicate with a PTP master clock 37to synchronize the time on the NIC and, optionally, on the Linux system. 38 39Note, PTP is a time syncing protocol and cannot be used within DPDK as a 40time-stamping mechanism. See the following for an explanation of the protocol: 41`Precision Time Protocol 42<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_. 43 44 45Limitations 46----------- 47 48The PTP sample application is intended as a simple reference implementation of 49a PTP client using the DPDK IEEE1588 API. 50In order to keep the application simple the following assumptions are made: 51 52* The first discovered master is the master for the session. 53* Only L2 PTP packets are supported. 54* Only the PTP v2 protocol is supported. 55* Only the slave clock is implemented. 56 57 58How the Application Works 59------------------------- 60 61.. _figure_ptpclient_highlevel: 62 63.. figure:: img/ptpclient.* 64 65 PTP Synchronization Protocol 66 67The PTP synchronization in the sample application works as follows: 68 69* Master sends *Sync* message - the slave saves it as T2. 70* Master sends *Follow Up* message and sends time of T1. 71* Slave sends *Delay Request* frame to PTP Master and stores T3. 72* Master sends *Delay Response* T4 time which is time of received T3. 73 74The adjustment for slave can be represented as: 75 76 adj = -[(T2-T1)-(T4 - T3)]/2 77 78If the command line parameter ``-T 1`` is used the application also 79synchronizes the PTP PHC clock with the Linux kernel clock. 80 81 82Compiling the Application 83------------------------- 84 85To compile the application, export the path to the DPDK source tree and edit 86the ``config/common_linuxapp`` configuration file to enable IEEE1588: 87 88.. code-block:: console 89 90 export RTE_SDK=/path/to/rte_sdk 91 92 # Edit common_linuxapp and set the following options: 93 CONFIG_RTE_LIBRTE_IEEE1588=y 94 95Set the target, for example: 96 97.. code-block:: console 98 99 export RTE_TARGET=x86_64-native-linuxapp-gcc 100 101See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values. 102 103Build the application as follows: 104 105.. code-block:: console 106 107 # Recompile DPDK. 108 make install T=$RTE_TARGET 109 110 # Compile the application. 111 cd ${RTE_SDK}/examples/ptpclient 112 make 113 114 115Running the Application 116----------------------- 117 118To run the example in a ``linuxapp`` environment: 119 120.. code-block:: console 121 122 ./build/ptpclient -l 1 -n 4 -- -p 0x1 -T 0 123 124Refer to *DPDK Getting Started Guide* for general information on running 125applications and the Environment Abstraction Layer (EAL) options. 126 127* ``-p portmask``: Hexadecimal portmask. 128* ``-T 0``: Update only the PTP slave clock. 129* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock. 130 131 132Code Explanation 133---------------- 134 135The following sections provide an explanation of the main components of the 136code. 137 138All DPDK library functions used in the sample code are prefixed with ``rte_`` 139and are explained in detail in the *DPDK API Documentation*. 140 141 142The Main Function 143~~~~~~~~~~~~~~~~~ 144 145The ``main()`` function performs the initialization and calls the execution 146threads for each lcore. 147 148The first task is to initialize the Environment Abstraction Layer (EAL). The 149``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` 150function. The value returned is the number of parsed arguments: 151 152.. code-block:: c 153 154 int ret = rte_eal_init(argc, argv); 155 if (ret < 0) 156 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 157 158And than we parse application specific arguments 159 160.. code-block:: c 161 162 argc -= ret; 163 argv += ret; 164 165 ret = ptp_parse_args(argc, argv); 166 if (ret < 0) 167 rte_exit(EXIT_FAILURE, "Error with PTP initialization\n"); 168 169The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) 170used by the application: 171 172.. code-block:: c 173 174 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports, 175 MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 176 177Mbufs are the packet buffer structure used by DPDK. They are explained in 178detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. 179 180The ``main()`` function also initializes all the ports using the user defined 181``port_init()`` function with portmask provided by user: 182 183.. code-block:: c 184 185 for (portid = 0; portid < nb_ports; portid++) 186 if ((ptp_enabled_port_mask & (1 << portid)) != 0) { 187 188 if (port_init(portid, mbuf_pool) == 0) { 189 ptp_enabled_ports[ptp_enabled_port_nb] = portid; 190 ptp_enabled_port_nb++; 191 } else { 192 rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", 193 portid); 194 } 195 } 196 197 198Once the initialization is complete, the application is ready to launch a 199function on an lcore. In this example ``lcore_main()`` is called on a single 200lcore. 201 202.. code-block:: c 203 204 lcore_main(); 205 206The ``lcore_main()`` function is explained below. 207 208 209The Lcores Main 210~~~~~~~~~~~~~~~ 211 212As we saw above the ``main()`` function calls an application function on the 213available lcores. 214 215The main work of the application is done within the loop: 216 217.. code-block:: c 218 219 for (portid = 0; portid < ptp_enabled_port_nb; portid++) { 220 221 portid = ptp_enabled_ports[portid]; 222 nb_rx = rte_eth_rx_burst(portid, 0, &m, 1); 223 224 if (likely(nb_rx == 0)) 225 continue; 226 227 if (m->ol_flags & PKT_RX_IEEE1588_PTP) 228 parse_ptp_frames(portid, m); 229 230 rte_pktmbuf_free(m); 231 } 232 233Packets are received one by one on the RX ports and, if required, PTP response 234packets are transmitted on the TX ports. 235 236If the offload flags in the mbuf indicate that the packet is a PTP packet then 237the packet is parsed to determine which type: 238 239.. code-block:: c 240 241 if (m->ol_flags & PKT_RX_IEEE1588_PTP) 242 parse_ptp_frames(portid, m); 243 244 245All packets are freed explicitly using ``rte_pktmbuf_free()``. 246 247The forwarding loop can be interrupted and the application closed using 248``Ctrl-C``. 249 250 251PTP parsing 252~~~~~~~~~~~ 253 254The ``parse_ptp_frames()`` function processes PTP packets, implementing slave 255PTP IEEE1588 L2 functionality. 256 257.. code-block:: c 258 259 void 260 parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) { 261 struct ptp_header *ptp_hdr; 262 struct ether_hdr *eth_hdr; 263 uint16_t eth_type; 264 265 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *); 266 eth_type = rte_be_to_cpu_16(eth_hdr->ether_type); 267 268 if (eth_type == PTP_PROTOCOL) { 269 ptp_data.m = m; 270 ptp_data.portid = portid; 271 ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *) 272 + sizeof(struct ether_hdr)); 273 274 switch (ptp_hdr->msgtype) { 275 case SYNC: 276 parse_sync(&ptp_data); 277 break; 278 case FOLLOW_UP: 279 parse_fup(&ptp_data); 280 break; 281 case DELAY_RESP: 282 parse_drsp(&ptp_data); 283 print_clock_info(&ptp_data); 284 break; 285 default: 286 break; 287 } 288 } 289 } 290 291There are 3 types of packets on the RX path which we must parse to create a minimal 292implementation of the PTP slave client: 293 294* SYNC packet. 295* FOLLOW UP packet 296* DELAY RESPONSE packet. 297 298When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet. 299Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock. 300