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