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 81Compiling the Application 82------------------------- 83 84To compile the sample application see :doc:`compiling`. 85 86The application is located in the ``ptpclient`` sub-directory. 87 88.. note:: 89 To compile the application edit the ``config/common_linuxapp`` configuration file to enable IEEE1588 90 and then recompile DPDK: 91 92 .. code-block:: console 93 94 CONFIG_RTE_LIBRTE_IEEE1588=y 95 96Running the Application 97----------------------- 98 99To run the example in a ``linuxapp`` environment: 100 101.. code-block:: console 102 103 ./build/ptpclient -l 1 -n 4 -- -p 0x1 -T 0 104 105Refer to *DPDK Getting Started Guide* for general information on running 106applications and the Environment Abstraction Layer (EAL) options. 107 108* ``-p portmask``: Hexadecimal portmask. 109* ``-T 0``: Update only the PTP slave clock. 110* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock. 111 112 113Code Explanation 114---------------- 115 116The following sections provide an explanation of the main components of the 117code. 118 119All DPDK library functions used in the sample code are prefixed with ``rte_`` 120and are explained in detail in the *DPDK API Documentation*. 121 122 123The Main Function 124~~~~~~~~~~~~~~~~~ 125 126The ``main()`` function performs the initialization and calls the execution 127threads for each lcore. 128 129The first task is to initialize the Environment Abstraction Layer (EAL). The 130``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` 131function. The value returned is the number of parsed arguments: 132 133.. code-block:: c 134 135 int ret = rte_eal_init(argc, argv); 136 if (ret < 0) 137 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 138 139And than we parse application specific arguments 140 141.. code-block:: c 142 143 argc -= ret; 144 argv += ret; 145 146 ret = ptp_parse_args(argc, argv); 147 if (ret < 0) 148 rte_exit(EXIT_FAILURE, "Error with PTP initialization\n"); 149 150The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) 151used by the application: 152 153.. code-block:: c 154 155 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports, 156 MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 157 158Mbufs are the packet buffer structure used by DPDK. They are explained in 159detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. 160 161The ``main()`` function also initializes all the ports using the user defined 162``port_init()`` function with portmask provided by user: 163 164.. code-block:: c 165 166 for (portid = 0; portid < nb_ports; portid++) 167 if ((ptp_enabled_port_mask & (1 << portid)) != 0) { 168 169 if (port_init(portid, mbuf_pool) == 0) { 170 ptp_enabled_ports[ptp_enabled_port_nb] = portid; 171 ptp_enabled_port_nb++; 172 } else { 173 rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", 174 portid); 175 } 176 } 177 178 179Once the initialization is complete, the application is ready to launch a 180function on an lcore. In this example ``lcore_main()`` is called on a single 181lcore. 182 183.. code-block:: c 184 185 lcore_main(); 186 187The ``lcore_main()`` function is explained below. 188 189 190The Lcores Main 191~~~~~~~~~~~~~~~ 192 193As we saw above the ``main()`` function calls an application function on the 194available lcores. 195 196The main work of the application is done within the loop: 197 198.. code-block:: c 199 200 for (portid = 0; portid < ptp_enabled_port_nb; portid++) { 201 202 portid = ptp_enabled_ports[portid]; 203 nb_rx = rte_eth_rx_burst(portid, 0, &m, 1); 204 205 if (likely(nb_rx == 0)) 206 continue; 207 208 if (m->ol_flags & PKT_RX_IEEE1588_PTP) 209 parse_ptp_frames(portid, m); 210 211 rte_pktmbuf_free(m); 212 } 213 214Packets are received one by one on the RX ports and, if required, PTP response 215packets are transmitted on the TX ports. 216 217If the offload flags in the mbuf indicate that the packet is a PTP packet then 218the packet is parsed to determine which type: 219 220.. code-block:: c 221 222 if (m->ol_flags & PKT_RX_IEEE1588_PTP) 223 parse_ptp_frames(portid, m); 224 225 226All packets are freed explicitly using ``rte_pktmbuf_free()``. 227 228The forwarding loop can be interrupted and the application closed using 229``Ctrl-C``. 230 231 232PTP parsing 233~~~~~~~~~~~ 234 235The ``parse_ptp_frames()`` function processes PTP packets, implementing slave 236PTP IEEE1588 L2 functionality. 237 238.. code-block:: c 239 240 void 241 parse_ptp_frames(uint16_t portid, struct rte_mbuf *m) { 242 struct ptp_header *ptp_hdr; 243 struct ether_hdr *eth_hdr; 244 uint16_t eth_type; 245 246 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *); 247 eth_type = rte_be_to_cpu_16(eth_hdr->ether_type); 248 249 if (eth_type == PTP_PROTOCOL) { 250 ptp_data.m = m; 251 ptp_data.portid = portid; 252 ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *) 253 + sizeof(struct ether_hdr)); 254 255 switch (ptp_hdr->msgtype) { 256 case SYNC: 257 parse_sync(&ptp_data); 258 break; 259 case FOLLOW_UP: 260 parse_fup(&ptp_data); 261 break; 262 case DELAY_RESP: 263 parse_drsp(&ptp_data); 264 print_clock_info(&ptp_data); 265 break; 266 default: 267 break; 268 } 269 } 270 } 271 272There are 3 types of packets on the RX path which we must parse to create a minimal 273implementation of the PTP slave client: 274 275* SYNC packet. 276* FOLLOW UP packet 277* DELAY RESPONSE packet. 278 279When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet. 280Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock. 281