1 /*- 2 * BSD LICENSE 3 * 4 * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 35 #include <rte_cycles.h> 36 #include <rte_ethdev.h> 37 #include <rte_flow.h> 38 39 #include "testpmd.h" 40 41 /** 42 * The structure of a PTP V2 packet. 43 * 44 * Only the minimum fields used by the ieee1588 test are represented. 45 */ 46 struct ptpv2_msg { 47 uint8_t msg_id; 48 uint8_t version; /**< must be 0x02 */ 49 uint8_t unused[34]; 50 }; 51 52 #define PTP_SYNC_MESSAGE 0x0 53 #define PTP_DELAY_REQ_MESSAGE 0x1 54 #define PTP_PATH_DELAY_REQ_MESSAGE 0x2 55 #define PTP_PATH_DELAY_RESP_MESSAGE 0x3 56 #define PTP_FOLLOWUP_MESSAGE 0x8 57 #define PTP_DELAY_RESP_MESSAGE 0x9 58 #define PTP_PATH_DELAY_FOLLOWUP_MESSAGE 0xA 59 #define PTP_ANNOUNCE_MESSAGE 0xB 60 #define PTP_SIGNALLING_MESSAGE 0xC 61 #define PTP_MANAGEMENT_MESSAGE 0xD 62 63 /* 64 * Forwarding of IEEE1588 Precise Time Protocol (PTP) packets. 65 * 66 * In this mode, packets are received one by one and are expected to be 67 * PTP V2 L2 Ethernet frames (with the specific Ethernet type "0x88F7") 68 * containing PTP "sync" messages (version 2 at offset 1, and message ID 69 * 0 at offset 0). 70 * 71 * Check that each received packet is a IEEE1588 PTP V2 packet of type 72 * PTP_SYNC_MESSAGE, and that it has been identified and timestamped 73 * by the hardware. 74 * Check that the value of the last RX timestamp recorded by the controller 75 * is greater than the previous one. 76 * 77 * If everything is OK, send the received packet back on the same port, 78 * requesting for it to be timestamped by the hardware. 79 * Check that the value of the last TX timestamp recorded by the controller 80 * is greater than the previous one. 81 */ 82 83 static void 84 port_ieee1588_rx_timestamp_check(portid_t pi, uint32_t index) 85 { 86 struct timespec timestamp = {0, 0}; 87 88 if (rte_eth_timesync_read_rx_timestamp(pi, ×tamp, index) < 0) { 89 printf("Port %u RX timestamp registers not valid\n", 90 (unsigned) pi); 91 return; 92 } 93 printf("Port %u RX timestamp value %lu s %lu ns\n", 94 (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec); 95 } 96 97 #define MAX_TX_TMST_WAIT_MICROSECS 1000 /**< 1 milli-second */ 98 99 static void 100 port_ieee1588_tx_timestamp_check(portid_t pi) 101 { 102 struct timespec timestamp = {0, 0}; 103 unsigned wait_us = 0; 104 105 while ((rte_eth_timesync_read_tx_timestamp(pi, ×tamp) < 0) && 106 (wait_us < MAX_TX_TMST_WAIT_MICROSECS)) { 107 rte_delay_us(1); 108 wait_us++; 109 } 110 if (wait_us >= MAX_TX_TMST_WAIT_MICROSECS) { 111 printf("Port %u TX timestamp registers not valid after " 112 "%u micro-seconds\n", 113 (unsigned) pi, (unsigned) MAX_TX_TMST_WAIT_MICROSECS); 114 return; 115 } 116 printf("Port %u TX timestamp value %lu s %lu ns validated after " 117 "%u micro-second%s\n", 118 (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec, wait_us, 119 (wait_us == 1) ? "" : "s"); 120 } 121 122 static void 123 ieee1588_packet_fwd(struct fwd_stream *fs) 124 { 125 struct rte_mbuf *mb; 126 struct ether_hdr *eth_hdr; 127 struct ether_addr addr; 128 struct ptpv2_msg *ptp_hdr; 129 uint16_t eth_type; 130 uint32_t timesync_index; 131 132 /* 133 * Receive 1 packet at a time. 134 */ 135 if (rte_eth_rx_burst(fs->rx_port, fs->rx_queue, &mb, 1) == 0) 136 return; 137 138 fs->rx_packets += 1; 139 140 /* 141 * Check that the received packet is a PTP packet that was detected 142 * by the hardware. 143 */ 144 eth_hdr = rte_pktmbuf_mtod(mb, struct ether_hdr *); 145 eth_type = rte_be_to_cpu_16(eth_hdr->ether_type); 146 147 if (! (mb->ol_flags & PKT_RX_IEEE1588_PTP)) { 148 if (eth_type == ETHER_TYPE_1588) { 149 printf("Port %u Received PTP packet not filtered" 150 " by hardware\n", 151 (unsigned) fs->rx_port); 152 } else { 153 printf("Port %u Received non PTP packet type=0x%4x " 154 "len=%u\n", 155 (unsigned) fs->rx_port, eth_type, 156 (unsigned) mb->pkt_len); 157 } 158 rte_pktmbuf_free(mb); 159 return; 160 } 161 if (eth_type != ETHER_TYPE_1588) { 162 printf("Port %u Received NON PTP packet incorrectly" 163 " detected by hardware\n", 164 (unsigned) fs->rx_port); 165 rte_pktmbuf_free(mb); 166 return; 167 } 168 169 /* 170 * Check that the received PTP packet is a PTP V2 packet of type 171 * PTP_SYNC_MESSAGE. 172 */ 173 ptp_hdr = (struct ptpv2_msg *) (rte_pktmbuf_mtod(mb, char *) + 174 sizeof(struct ether_hdr)); 175 if (ptp_hdr->version != 0x02) { 176 printf("Port %u Received PTP V2 Ethernet frame with wrong PTP" 177 " protocol version 0x%x (should be 0x02)\n", 178 (unsigned) fs->rx_port, ptp_hdr->version); 179 rte_pktmbuf_free(mb); 180 return; 181 } 182 if (ptp_hdr->msg_id != PTP_SYNC_MESSAGE) { 183 printf("Port %u Received PTP V2 Ethernet frame with unexpected" 184 " message ID 0x%x (expected 0x0 - PTP_SYNC_MESSAGE)\n", 185 (unsigned) fs->rx_port, ptp_hdr->msg_id); 186 rte_pktmbuf_free(mb); 187 return; 188 } 189 printf("Port %u IEEE1588 PTP V2 SYNC Message filtered by hardware\n", 190 (unsigned) fs->rx_port); 191 192 /* 193 * Check that the received PTP packet has been timestamped by the 194 * hardware. 195 */ 196 if (! (mb->ol_flags & PKT_RX_IEEE1588_TMST)) { 197 printf("Port %u Received PTP packet not timestamped" 198 " by hardware\n", 199 (unsigned) fs->rx_port); 200 rte_pktmbuf_free(mb); 201 return; 202 } 203 204 /* For i40e we need the timesync register index. It is ignored for the 205 * other PMDs. */ 206 timesync_index = mb->timesync & 0x3; 207 /* Read and check the RX timestamp. */ 208 port_ieee1588_rx_timestamp_check(fs->rx_port, timesync_index); 209 210 /* Swap dest and src mac addresses. */ 211 ether_addr_copy(ð_hdr->d_addr, &addr); 212 ether_addr_copy(ð_hdr->s_addr, ð_hdr->d_addr); 213 ether_addr_copy(&addr, ð_hdr->s_addr); 214 215 /* Forward PTP packet with hardware TX timestamp */ 216 mb->ol_flags |= PKT_TX_IEEE1588_TMST; 217 fs->tx_packets += 1; 218 if (rte_eth_tx_burst(fs->rx_port, fs->tx_queue, &mb, 1) == 0) { 219 printf("Port %u sent PTP packet dropped\n", 220 (unsigned) fs->rx_port); 221 fs->fwd_dropped += 1; 222 rte_pktmbuf_free(mb); 223 return; 224 } 225 226 /* 227 * Check the TX timestamp. 228 */ 229 port_ieee1588_tx_timestamp_check(fs->rx_port); 230 } 231 232 static void 233 port_ieee1588_fwd_begin(portid_t pi) 234 { 235 rte_eth_timesync_enable(pi); 236 } 237 238 static void 239 port_ieee1588_fwd_end(portid_t pi) 240 { 241 rte_eth_timesync_disable(pi); 242 } 243 244 struct fwd_engine ieee1588_fwd_engine = { 245 .fwd_mode_name = "ieee1588", 246 .port_fwd_begin = port_ieee1588_fwd_begin, 247 .port_fwd_end = port_ieee1588_fwd_end, 248 .packet_fwd = ieee1588_packet_fwd, 249 }; 250