diff options
Diffstat (limited to 'src')
31 files changed, 1781 insertions, 307 deletions
diff --git a/src/bp_sim.h b/src/bp_sim.h index a51a520d..c9550dcf 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -43,6 +43,7 @@ limitations under the License. #include <math.h> #include <common/bitMan.h> #include <yaml-cpp/yaml.h> +#include "trex_defs.h" #include "os_time.h" #include "pal_utl.h" #include "rx_check_header.h" @@ -69,7 +70,7 @@ usec_to_sec(double usec) { #define FORCE_NO_INLINE __attribute__ ((noinline)) -#define MAX_LATENCY_PORTS 12 +#define MAX_LATENCY_PORTS TREX_MAX_PORTS /* IP address, last 32-bits of IPv6 remaps IPv4 */ typedef struct { diff --git a/src/common/Network/Packet/IPHeader.h b/src/common/Network/Packet/IPHeader.h index 5dfd03d8..b9ef8a2e 100755 --- a/src/common/Network/Packet/IPHeader.h +++ b/src/common/Network/Packet/IPHeader.h @@ -134,7 +134,7 @@ public: inline void updateIpDst(uint32_t ipsrc); - + inline void updateCheckSum(uint16_t old_val, uint16_t new_val); inline void updateCheckSum (); inline void updateCheckSum2(uint8_t* data1, uint16_t len1, uint8_t* data2 , uint16_t len2); diff --git a/src/common/Network/Packet/IPHeader.inl b/src/common/Network/Packet/IPHeader.inl index e7b87f06..26ea551a 100755 --- a/src/common/Network/Packet/IPHeader.inl +++ b/src/common/Network/Packet/IPHeader.inl @@ -258,6 +258,12 @@ inline void IPHeader::updateTotalLength(uint16_t newlen) myChecksum = pkt_UpdateInetChecksum(myChecksum,oldLen,myLength); } +// updating checksum after changing old val to new +inline void IPHeader::updateCheckSum(uint16_t old_val, uint16_t new_val) +{ + myChecksum = pkt_UpdateInetChecksum(myChecksum, old_val, new_val); +} + inline void IPHeader::updateCheckSum() { myChecksum = 0; diff --git a/src/debug.cpp b/src/debug.cpp index ed4900f9..0ca34545 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -14,10 +14,10 @@ limitations under the License. */ -// DPDK c++ issue +// DPDK c++ issue #define UINT8_MAX 255 #define UINT16_MAX 0xFFFF -// DPDK c++ issue +// DPDK c++ issue #include <stdio.h> #include <unistd.h> @@ -30,22 +30,22 @@ #include "main_dpdk.h" #include "debug.h" -const uint8_t udp_pkt[] = { +const uint8_t udp_pkt[] = { 0x00,0x00,0x00,0x01,0x00,0x00, 0x00,0x00,0x00,0x01,0x00,0x00, 0x08,0x00, - + 0x45,0x00,0x00,0x81, 0xaf,0x7e,0x00,0x00, 0xfe,0x06,0xd9,0x23, 0x01,0x01,0x01,0x01, 0x3d,0xad,0x72,0x1b, - + + 0x11,0x11, 0x11,0x11, - 0x11,0x11, 0x00,0x6d, 0x00,0x00, - + 0x64,0x31,0x3a,0x61, 0x64,0x32,0x3a,0x69,0x64, 0x32,0x30,0x3a,0xd0,0x0e, @@ -63,9 +63,9 @@ const uint8_t udp_pkt[] = { }; CTrexDebug::CTrexDebug(CPhyEthIF m_ports_arg[12], int max_ports) { - m_test = NULL; - m_ports = m_ports_arg; - m_max_ports = max_ports; + m_test = NULL; + m_ports = m_ports_arg; + m_max_ports = max_ports; } int CTrexDebug::rcv_send(int port, int queue_id) { @@ -86,6 +86,7 @@ int CTrexDebug::rcv_send(int port, int queue_id) { return 0; } +// receive packets on queue_id int CTrexDebug::rcv_send_all(int queue_id) { int i; for (i=0; i<m_max_ports; i++) { @@ -95,76 +96,82 @@ int CTrexDebug::rcv_send_all(int queue_id) { } // For playing around, and testing packet sending in debug mode -rte_mbuf_t *CTrexDebug::create_test_pkt(int pkt_type) { +rte_mbuf_t *CTrexDebug::create_test_pkt(int pkt_type, uint8_t ttl, uint16_t ip_id) { uint8_t proto; int pkt_size = 0; // ASA 2 - uint8_t dst_mac[6] = {0x74, 0xa2, 0xe6, 0xd5, 0x39, 0x25}; - uint8_t src_mac[6] = {0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x02}; - // ASA 1 - // uint8_t dst_mac[6] = {0xd4, 0x8c, 0xb5, 0xc9, 0x54, 0x2b}; - // uint8_t src_mac[6] = {0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x0}; + uint8_t dst_mac[6] = {0x74, 0xa2, 0xe6, 0xd5, 0x39, 0x25}; + uint8_t src_mac[6] = {0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x02}; + // ASA 1 + // uint8_t dst_mac[6] = {0xd4, 0x8c, 0xb5, 0xc9, 0x54, 0x2b}; + // uint8_t src_mac[6] = {0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x0}; + //#define VLAN +#ifdef VLAN + uint16_t l2_proto = 0x0081; + uint8_t vlan_header[4] = {0x0a, 0xbc, 0x08, 0x00}; +#else uint16_t l2_proto = 0x0008; +#endif uint8_t ip_header[] = { - 0x45,0x02,0x00,0x30, - 0x00,0x00,0x40,0x00, - 0xff,0x01,0xbd,0x04, - 0x10,0x0,0x0,0x1, //SIP - 0x30,0x0,0x0,0x1, //DIP - // 0x82, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // IP option. change 45 to 48 (header len) if using it. + 0x45,0x02,0x00,0x30, + 0x00,0x00,0x40,0x00, + 0xff,0x01,0xbd,0x04, + 0x10,0x0,0x0,0x1, //SIP + 0x30,0x0,0x0,0x1, //DIP + // 0x82, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // IP option. change 45 to 48 (header len) if using it. }; uint8_t udp_header[] = {0x11, 0x11, 0x11,0x11, 0x00, 0x6d, 0x00, 0x00}; uint8_t udp_data[] = {0x64,0x31,0x3a,0x61, - 0x64,0x32,0x3a,0x69,0x64, - 0x32,0x30,0x3a,0xd0,0x0e, - 0xa1,0x4b,0x7b,0xbd,0xbd, - 0x16,0xc6,0xdb,0xc4,0xbb,0x43, - 0xf9,0x4b,0x51,0x68,0x33,0x72, - 0x20,0x39,0x3a,0x69,0x6e,0x66,0x6f, - 0x5f,0x68,0x61,0x73,0x68,0x32,0x30,0x3a,0xee,0xc6,0xa3, - 0xd3,0x13,0xa8,0x43,0x06,0x03,0xd8,0x9e,0x3f,0x67,0x6f, - 0xe7,0x0a,0xfd,0x18,0x13,0x8d,0x65,0x31,0x3a,0x71,0x39, - 0x3a,0x67,0x65,0x74,0x5f,0x70,0x65,0x65,0x72,0x73,0x31, - 0x3a,0x74,0x38,0x3a,0x3d,0xeb,0x0c,0xbf,0x0d,0x6a,0x0d, - 0xa5,0x31,0x3a,0x79,0x31,0x3a,0x71,0x65,0x87,0xa6,0x7d, - 0xe7 + 0x64,0x32,0x3a,0x69,0x64, + 0x32,0x30,0x3a,0xd0,0x0e, + 0xa1,0x4b,0x7b,0xbd,0xbd, + 0x16,0xc6,0xdb,0xc4,0xbb,0x43, + 0xf9,0x4b,0x51,0x68,0x33,0x72, + 0x20,0x39,0x3a,0x69,0x6e,0x66,0x6f, + 0x5f,0x68,0x61,0x73,0x68,0x32,0x30,0x3a,0xee,0xc6,0xa3, + 0xd3,0x13,0xa8,0x43,0x06,0x03,0xd8,0x9e,0x3f,0x67,0x6f, + 0xe7,0x0a,0xfd,0x18,0x13,0x8d,0x65,0x31,0x3a,0x71,0x39, + 0x3a,0x67,0x65,0x74,0x5f,0x70,0x65,0x65,0x72,0x73,0x31, + 0x3a,0x74,0x38,0x3a,0x3d,0xeb,0x0c,0xbf,0x0d,0x6a,0x0d, + 0xa5,0x31,0x3a,0x79,0x31,0x3a,0x71,0x65,0x87,0xa6,0x7d, + 0xe7 }; uint8_t tcp_header[] = {0xab, 0xcd, 0x00, 0x80, // src, dst ports - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // seq num, ack num - 0x50, 0x00, 0xff, 0xff, // Header size, flags, window size - 0x00, 0x00, 0x00, 0x00, // checksum ,urgent pointer + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // seq num, ack num + 0x50, 0x00, 0xff, 0xff, // Header size, flags, window size + 0x00, 0x00, 0x00, 0x00, // checksum ,urgent pointer }; uint8_t tcp_data[] = {0x8, 0xa, 0x1, 0x2, 0x3, 0x4, 0x3, 0x4, 0x6, 0x5}; uint8_t icmp_header[] = { - 0x08, 0x00, - 0xb8, 0x21, //checksum - 0xaa, 0xbb, // id - 0x00, 0x01, // Sequence number + 0x08, 0x00, + 0xb8, 0x21, //checksum + 0xaa, 0xbb, // id + 0x00, 0x01, // Sequence number }; uint8_t icmp_data[] = { - 0xd6, 0x6e, 0x64, 0x34, // magic - 0x6a, 0xad, 0x0f, 0x00, //64 bit counter - 0x00, 0x56, 0x34, 0x12, - 0x78, 0x56, 0x34, 0x12, 0x00, 0x00 // seq + 0xd6, 0x6e, 0x64, 0x34, // magic + 0x6a, 0xad, 0x0f, 0x00, //64 bit counter + 0x00, 0x56, 0x34, 0x12, + 0x78, 0x56, 0x34, 0x12, 0x00, 0x00 // seq }; switch (pkt_type) { case 1: - proto = IPPROTO_ICMP; - pkt_size = 14 + sizeof(ip_header) + sizeof(icmp_header) + sizeof (icmp_data); - break; + proto = IPPROTO_ICMP; + pkt_size = 14 + sizeof(ip_header) + sizeof(icmp_header) + sizeof (icmp_data); + break; case 2: - proto = IPPROTO_UDP; - pkt_size = 14 + sizeof(ip_header) + sizeof(udp_header) + sizeof (udp_data); - break; + proto = IPPROTO_UDP; + pkt_size = 14 + sizeof(ip_header) + sizeof(udp_header) + sizeof (udp_data); + break; case 3: - proto = IPPROTO_TCP; - pkt_size = 14 + sizeof(ip_header) + sizeof(tcp_header) + sizeof (tcp_data); - break; + proto = IPPROTO_TCP; + pkt_size = 14 + sizeof(ip_header) + sizeof(tcp_header) + sizeof (tcp_data); + break; default: - return NULL; + return NULL; } rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(0, pkt_size); @@ -179,34 +186,39 @@ rte_mbuf_t *CTrexDebug::create_test_pkt(int pkt_type) { memcpy(p, dst_mac, sizeof(dst_mac)); p += sizeof(dst_mac); memcpy(p, src_mac, sizeof(src_mac)); p += sizeof(src_mac); memcpy(p, &l2_proto, sizeof(l2_proto)); p += sizeof(l2_proto); +#ifdef VLAN + memcpy(p, &vlan_header, sizeof(vlan_header)); p += sizeof(vlan_header); +#endif struct IPHeader *ip = (IPHeader *)p; memcpy(p, ip_header, sizeof(ip_header)); p += sizeof(ip_header); ip->setProtocol(proto); ip->setTotalLength(pkt_size - 14); + ip->setId(ip_id); - struct TCPHeader *tcp = (TCPHeader *)p; + struct TCPHeader *tcp = (TCPHeader *)p; struct ICMPHeader *icmp= (ICMPHeader *)p; switch (pkt_type) { case 1: - memcpy(p, icmp_header, sizeof(icmp_header)); p += sizeof(icmp_header); - memcpy(p, icmp_data, sizeof(icmp_data)); p += sizeof(icmp_data); - icmp->updateCheckSum(sizeof(icmp_header) + sizeof(icmp_data)); - break; + memcpy(p, icmp_header, sizeof(icmp_header)); p += sizeof(icmp_header); + memcpy(p, icmp_data, sizeof(icmp_data)); p += sizeof(icmp_data); + icmp->updateCheckSum(sizeof(icmp_header) + sizeof(icmp_data)); + break; case 2: - memcpy(p, udp_header, sizeof(udp_header)); p += sizeof(udp_header); - memcpy(p, udp_data, sizeof(udp_data)); p += sizeof(udp_data); - break; + memcpy(p, udp_header, sizeof(udp_header)); p += sizeof(udp_header); + memcpy(p, udp_data, sizeof(udp_data)); p += sizeof(udp_data); + break; case 3: - memcpy(p, tcp_header, sizeof(tcp_header)); p += sizeof(tcp_header); - memcpy(p, tcp_data, sizeof(tcp_data)); p += sizeof(tcp_data); - tcp->setSynFlag(true); - printf("Sending TCP header:"); - tcp->dump(stdout); - break; + memcpy(p, tcp_header, sizeof(tcp_header)); p += sizeof(tcp_header); + memcpy(p, tcp_data, sizeof(tcp_data)); p += sizeof(tcp_data); + tcp->setSynFlag(true); + printf("Sending TCP header:"); + tcp->dump(stdout); + break; default: - return NULL; + return NULL; } + ip->setTimeToLive(ttl); ip->updateCheckSum(); return m; } @@ -276,25 +288,81 @@ int CTrexDebug::set_promisc_all(bool enable) { return 0; } +static void rte_stat_dump_array(const uint64_t *c, const char *name, int size) { + int i; + + // dont print anything if all values are 0 + for (i = 0; i < size; i++) { + if (c[i] != 0) + break; + } + if (i == size) + return; + + printf("%s:", name); + for (i = 0; i < size; i++) { + if (((i % 32) == 0) && (size > 32)) { + printf ("\n %4d:", i); + } + printf(" %2ld", c[i]); + } + printf("\n"); +} + +static void rte_stat_dump_one(uint64_t c, const char *name) { + if (c != 0) + printf("%s:%ld\n", name, c); +} + +static void rte_stats_dump(const struct rte_eth_stats &stats) { + rte_stat_dump_one(stats.ipackets, "ipackets"); + rte_stat_dump_one(stats.opackets, "opackets"); + rte_stat_dump_one(stats.ibytes, "ibytes"); + rte_stat_dump_one(stats.obytes, "obytes"); + rte_stat_dump_one(stats.imissed, "imissed"); + rte_stat_dump_one(stats.ierrors, "ierrors"); + rte_stat_dump_one(stats.oerrors, "oerrors"); + rte_stat_dump_one(stats.rx_nombuf, "rx_nombuf"); + rte_stat_dump_array(stats.q_ipackets, "queue rx", RTE_ETHDEV_QUEUE_STAT_CNTRS); + rte_stat_dump_array(stats.q_opackets, "queue tx", RTE_ETHDEV_QUEUE_STAT_CNTRS); + rte_stat_dump_array(stats.q_ibytes, "queue rx bytes", RTE_ETHDEV_QUEUE_STAT_CNTRS); + rte_stat_dump_array(stats.q_obytes, "queue tx bytes", RTE_ETHDEV_QUEUE_STAT_CNTRS); + rte_stat_dump_array(stats.q_errors, "queue dropped", RTE_ETHDEV_QUEUE_STAT_CNTRS); + rte_stat_dump_one(stats.ilbpackets, "rx loopback"); + rte_stat_dump_one(stats.olbpackets, "tx loopback"); + rte_stat_dump_one(stats.ilbbytes, "rx bytes loopback"); + rte_stat_dump_one(stats.olbbytes, "tx bytes loopback"); +} + int CTrexDebug::test_send(uint pkt_type) { + int port_id; + set_promisc_all(true); - rte_mbuf_t *m, *d; + rte_mbuf_t *m, *d, *d2=NULL, *d3=NULL; if (pkt_type < 1 || pkt_type > 4) { - printf("Unsupported packet type %d\n", pkt_type); - printf("Supported packet types are: %d(ICMP), %d(UDP), %d(TCP) %d(9k UDP)\n", 1, 2, 3, 4); - exit(-1); + printf("Unsupported packet type %d\n", pkt_type); + printf("Supported packet types are: %d(ICMP), %d(UDP), %d(TCP) %d(9k UDP)\n", 1, 2, 3, 4); + exit(-1); } if (pkt_type == 4) { - m = create_udp_9k_pkt(); - assert (m); - d = create_pkt_indirect(m, 9*1024+18); + m = create_udp_9k_pkt(); + assert (m); + d = create_pkt_indirect(m, 9*1024+18); } else { - d = create_test_pkt(pkt_type); + d = create_test_pkt(pkt_type, 255, 0xff35); + // d2 = create_test_pkt(pkt_type, 253, 0xfe01); + // d3 = create_test_pkt(pkt_type, 251, 0xfe02); } if (d == NULL) { - printf("Packet creation failed\n"); - exit(-1); + printf("Packet creation failed\n"); + exit(-1); + } + + // read first time to zero statistics + for (port_id = 0; port_id < m_max_ports; port_id++) { + CPhyEthIF * lp=&m_ports[port_id]; + lp->get_rx_stats(NULL, -1, true); } printf("Sending packet:\n"); @@ -302,32 +370,51 @@ int CTrexDebug::test_send(uint pkt_type) { test_send_pkts(d, 0, 2, 0); test_send_pkts(d, 0, 1, 1); + if (d2) { + test_send_pkts(d2, 0, 4, 0); + test_send_pkts(d2, 0, 3, 1); + } + if (d3) { + test_send_pkts(d3, 0, 6, 0); + test_send_pkts(d3, 0, 5, 1); + } delay(1000); - printf(" ---------\n"); - printf(" rx queue 0 \n"); - printf(" ---------\n"); - rcv_send_all(0); - printf("\n\n"); - - printf(" ---------\n"); - printf(" rx queue 1 \n"); - printf(" ---------\n"); - rcv_send_all(1); - printf(" ---------\n"); + int j=0; + for (j = 0; j < 2; j++) { + printf(" =========\n"); + printf(" rx queue %d \n", j); + printf(" =========\n"); + rcv_send_all(j); + printf("\n\n"); + } delay(1000); - int j=0; - for (j=0; j<m_max_ports; j++) { - CPhyEthIF * lp=&m_ports[j]; - printf(" port : %d \n",j); - printf(" ----------\n"); - lp->update_counters(); - lp->get_stats().Dump(stdout); + struct rte_eth_stats stats; + for (port_id = 0; port_id < m_max_ports; port_id++) { + CPhyEthIF * lp=&m_ports[port_id]; + std::cout << "=====================\n"; + std::cout << "Statistics for port " << port_id << std::endl; + std::cout << "=====================\n"; + + if (rte_eth_stats_get(port_id, &stats) == 0) { + rte_stats_dump(stats); + } else { + // For NICs which does not support rte_eth_stats_get, we have our own implementation. + lp->update_counters(); + lp->get_stats().Dump(stdout); + } + lp->dump_stats_extended(stdout); } + for (port_id = 0; port_id < m_max_ports; port_id++) { + uint64_t fdir_stat[TREX_FDIR_STAT_SIZE]; + CPhyEthIF *lp = &m_ports[port_id]; + if (lp->get_rx_stats(fdir_stat, -1, false) == 0) + rte_stat_dump_array(fdir_stat, "FDIR stat", TREX_FDIR_STAT_SIZE); + } return (0); } diff --git a/src/debug.h b/src/debug.h index fe37c186..4fc23d93 100644 --- a/src/debug.h +++ b/src/debug.h @@ -32,7 +32,7 @@ class CTrexDebug { rte_mbuf_t *create_udp_9k_pkt(); int set_promisc_all(bool enable); int test_send_pkts(rte_mbuf_t *, uint16_t queue_id, int pkt, int port); - rte_mbuf_t *create_test_pkt(int proto); + rte_mbuf_t *create_test_pkt(int proto, uint8_t ttl, uint16_t ip_id); public: CTrexDebug(CPhyEthIF *m_ports_arg, int max_ports); diff --git a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c index 0a1e9efc..510a98cd 100644 --- a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c +++ b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c @@ -695,6 +695,12 @@ static inline void i40e_flex_payload_reg_init(struct i40e_hw *hw) #define I40E_PRTQF_FD_INSET(_i, _j) (0x00250000 + ((_i) * 64 + (_j) * 32)) #define I40E_GLQF_FD_MSK(_i, _j) (0x00267200 + ((_i) * 4 + (_j) * 8)) +// 0 - statfull mode. 1 stateless. +static int trex_mode=0; +void i40e_set_trex_mode(int mode) { + trex_mode = mode; +} + static void i40e_dump_filter_regs(struct i40e_hw *hw) { int reg_nums[] = {31, 33, 34, 35, 41, 43}; @@ -716,9 +722,7 @@ static inline void i40e_filter_fields_reg_init(struct i40e_hw *hw) I40E_WRITE_REG(hw, I40E_GLQF_ORT(12), 0x00000062); I40E_WRITE_REG(hw, I40E_GLQF_PIT(2), 0x000024A0); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(31, 0), 0); - I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(31, 1), 0x00040000); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(33, 0), 0); - I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(33, 1), 0x00040000); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(41, 0), 0); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(41, 1), 0x00080000); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(43, 0), 0); @@ -727,7 +731,15 @@ static inline void i40e_filter_fields_reg_init(struct i40e_hw *hw) I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(34, 1), 0x00040000); // filter IP according to ttl and L4 protocol I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(35, 0), 0); - I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(35, 1), 0x00040000); + if (trex_mode == 1) { + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(35, 1), 0x00100000); + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(31, 1), 0x00100000); + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(33, 1), 0x00100000); + } else { + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(35, 1), 0x00040000); + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(31, 1), 0x00040000); + I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(33, 1), 0x00040000); + } I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(44, 0), 0); I40E_WRITE_REG(hw, I40E_PRTQF_FD_INSET(44, 1), 0x00080000); I40E_WRITE_REG(hw, I40E_GLQF_FD_MSK(0, 34), 0x000DFF00); @@ -2059,9 +2071,12 @@ i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw) I40E_GLPRT_PTC9522L(hw->port), pf->offset_loaded, &os->tx_size_big, &ns->tx_size_big); +#ifndef TREX_PATCH i40e_stat_update_32(hw, I40E_GLQF_PCNT(pf->fdir.match_counter_index), pf->offset_loaded, &os->fd_sb_match, &ns->fd_sb_match); +#endif + /* GLPRT_MSPDC not supported */ /* GLPRT_XEC not supported */ @@ -2084,6 +2099,19 @@ i40e_trex_get_speed(struct rte_eth_dev *dev) } } +//TREX_PATCH +// fill stats array with fdir rules match count statistics +void +i40e_trex_fdir_stats_get(struct rte_eth_dev *dev, uint32_t *stats, uint32_t start, uint32_t len) +{ + int i; + struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + for (i = 0; i < len; i++) { + stats[i] = I40E_READ_REG(hw, I40E_GLQF_PCNT(i + start)); + } +} + /* Get all statistics of a port */ static void i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) diff --git a/src/dpdk22/drivers/net/i40e/i40e_fdir.c b/src/dpdk22/drivers/net/i40e/i40e_fdir.c index 194f8629..f644ef3d 100644 --- a/src/dpdk22/drivers/net/i40e/i40e_fdir.c +++ b/src/dpdk22/drivers/net/i40e/i40e_fdir.c @@ -78,6 +78,7 @@ #define I40E_FDIR_FLUSH_RETRY 50 #define I40E_FDIR_FLUSH_INTERVAL_MS 5 +#define TREX_PATCH #define I40E_COUNTER_PF 2 /* Statistic counter index for one pf */ #define I40E_COUNTER_INDEX_FDIR(pf_id) (0 + (pf_id) * I40E_COUNTER_PF) @@ -719,8 +720,10 @@ i40e_fdir_fill_eth_ip_head(const struct rte_eth_fdir_input *fdir_input, ip->version_ihl = I40E_FDIR_IP_DEFAULT_VERSION_IHL; /* set len to by default */ ip->total_length = rte_cpu_to_be_16(I40E_FDIR_IP_DEFAULT_LEN); - // TREX_PATCH +#ifdef TREX_PATCH ip->time_to_live = fdir_input->flow.ip4_flow.ttl; + ip->packet_id = rte_cpu_to_be_16(fdir_input->flow.ip4_flow.ip_id); +#endif /* * The source and destination fields in the transmitted packet * need to be presented in a reversed order with respect @@ -1145,7 +1148,11 @@ i40e_fdir_filter_programming(struct i40e_pf *pf, fdirdp->dtype_cmd_cntindex |= rte_cpu_to_le_32(I40E_TXD_FLTR_QW1_CNT_ENA_MASK); fdirdp->dtype_cmd_cntindex |= +#ifdef TREX_PATCH + rte_cpu_to_le_32((fdir_action->stat_count_index << +#else rte_cpu_to_le_32((pf->fdir.match_counter_index << +#endif I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) & I40E_TXD_FLTR_QW1_CNTINDEX_MASK); diff --git a/src/dpdk22/lib/librte_ether/rte_eth_ctrl.h b/src/dpdk22/lib/librte_ether/rte_eth_ctrl.h index dc26439d..419ca90e 100644 --- a/src/dpdk22/lib/librte_ether/rte_eth_ctrl.h +++ b/src/dpdk22/lib/librte_ether/rte_eth_ctrl.h @@ -407,8 +407,9 @@ struct rte_eth_l2_flow { struct rte_eth_ipv4_flow { uint32_t src_ip; /**< IPv4 source address to match. */ uint32_t dst_ip; /**< IPv4 destination address to match. */ - // TREX_PATCH + // TREX_PATCH (ttl and ip_id) uint8_t ttl; /**< IPv4 ttl to match */ + uint16_t ip_id; /**< IPv4 IP ID to match */ uint8_t l4_protocol; /**< IPv4 l4 protocol to match */ }; @@ -575,6 +576,9 @@ struct rte_eth_fdir_action { /**< If report_status is RTE_ETH_FDIR_REPORT_ID_FLEX_4 or RTE_ETH_FDIR_REPORT_FLEX_8, flex_off specifies where the reported flex bytes start from in flexible payload. */ + // TREX_PATCH + // Index for statistics counter that will count FDIR matches. + uint16_t stat_count_index; }; /** diff --git a/src/dpdk22/lib/librte_ether/rte_ethdev.c b/src/dpdk22/lib/librte_ether/rte_ethdev.c index 43ec0265..383ad120 100644 --- a/src/dpdk22/lib/librte_ether/rte_ethdev.c +++ b/src/dpdk22/lib/librte_ether/rte_ethdev.c @@ -1445,6 +1445,23 @@ rte_eth_get_speed(uint8_t port_id, int *speed) return 0; } +// TREX_PATCH +// return in stats, statistics starting from start, for len counters. +int +rte_eth_fdir_stats_get(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len) +{ + struct rte_eth_dev *dev; + + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL); + + dev = &rte_eth_devices[port_id]; + + // Only xl710 support this + i40e_trex_fdir_stats_get(dev, stats, start, len); + + return 0; +} + int rte_eth_stats_get(uint8_t port_id, struct rte_eth_stats *stats) { diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp new file mode 100644 index 00000000..298bcb52 --- /dev/null +++ b/src/flow_stat.cpp @@ -0,0 +1,619 @@ +/* + Ido Barnea + Cisco Systems, Inc. +*/ + +/* + Copyright (c) 2015-2016 Cisco Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include <sstream> +#include <string> +#include <iostream> +#include <assert.h> +#include <os_time.h> +#include <internal_api/trex_platform_api.h> +#include "trex_stateless.h" +#include "trex_stream.h" +#include "flow_stat_parser.h" +#include "flow_stat.h" + + +#define FLOW_STAT_ADD_ALL_PORTS 255 + +static const uint16_t FREE_HW_ID = UINT16_MAX; + +#ifdef __DEBUG_FUNC_ENTRY__ +inline std::string methodName(const std::string& prettyFunction) +{ + size_t colons = prettyFunction.find("::"); + size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; + size_t end = prettyFunction.rfind("(") - begin; + + return prettyFunction.substr(begin,end) + "()"; +} + +#define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__) +#define FUNC_ENTRY (std::cout << __METHOD_NAME__ << std::endl); +#else +#define FUNC_ENTRY +#endif + +/************** class CFlowStatUserIdInfo ***************/ +CFlowStatUserIdInfo::CFlowStatUserIdInfo(uint8_t proto) { + memset(m_rx_counter, 0, sizeof(m_rx_counter)); + memset(m_rx_counter_base, 0, sizeof(m_rx_counter)); + memset(m_tx_counter, 0, sizeof(m_tx_counter)); + memset(m_tx_counter_base, 0, sizeof(m_tx_counter)); + m_hw_id = UINT16_MAX; + m_proto = proto; + m_ref_count = 1; + m_trans_ref_count = 0; +} + +std::ostream& operator<<(std::ostream& os, const class CFlowStatUserIdInfo& cf) { + os << "hw_id:" << cf.m_hw_id << " proto:" << (uint16_t) cf.m_proto << " ref(" + << (uint16_t) cf.m_ref_count << "," << (uint16_t) cf.m_trans_ref_count << ")"; + os << " rx count ("; + os << cf.m_rx_counter[0]; + for (int i = 1; i < TREX_MAX_PORTS; i++) { + os << "," << cf.m_rx_counter[i]; + } + os << ")"; + os << " rx count base("; + os << cf.m_rx_counter_base[0]; + for (int i = 1; i < TREX_MAX_PORTS; i++) { + os << "," << cf.m_rx_counter_base[i]; + } + os << ")"; + + os << " tx count ("; + os << cf.m_tx_counter[0]; + for (int i = 1; i < TREX_MAX_PORTS; i++) { + os << "," << cf.m_tx_counter[i]; + } + os << ")"; + os << " tx count base("; + os << cf.m_tx_counter_base[0]; + for (int i = 1; i < TREX_MAX_PORTS; i++) { + os << "," << cf.m_tx_counter_base[i]; + } + os << ")"; + + return os; +} + +int CFlowStatUserIdInfo::add_stream(uint8_t proto) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " proto:" << (uint16_t)proto << std::endl; +#endif + + if (proto != m_proto) + return -1; + + m_ref_count++; + + return 0; +} + +void CFlowStatUserIdInfo::reset_hw_id() { + FUNC_ENTRY; + + m_hw_id = UINT16_MAX; + // we are not attached to hw. Save packet count of session. + // Next session will start counting from 0. + for (int i = 0; i < TREX_MAX_PORTS; i++) { + m_rx_counter_base[i] += m_rx_counter[i]; + m_rx_counter[i] = 0; + m_tx_counter_base[i] += m_tx_counter[i]; + m_tx_counter[i] = 0; + } +} +/************** class CFlowStatUserIdMap ***************/ +CFlowStatUserIdMap::CFlowStatUserIdMap() { + +} + +std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdMap& cf) { + std::map<unsigned int, CFlowStatUserIdInfo*>::const_iterator it; + for (it = cf.m_map.begin(); it != cf.m_map.end(); it++) { + CFlowStatUserIdInfo *user_id_info = it->second; + uint32_t user_id = it->first; + os << "Flow stat user id info:\n"; + os << " " << user_id << ":" << *user_id_info << std::endl; + } + return os; +} + +uint16_t CFlowStatUserIdMap::get_hw_id(uint32_t user_id) { + class CFlowStatUserIdInfo *cf = find_user_id(user_id); + + if (cf == NULL) { + return FREE_HW_ID; + } else { + return cf->get_hw_id(); + } +} + +class CFlowStatUserIdInfo * +CFlowStatUserIdMap::find_user_id(uint32_t user_id) { + flow_stat_user_id_map_it_t it = m_map.find(user_id); + + if (it == m_map.end()) { + return NULL; + } else { + return it->second; + } +} + +class CFlowStatUserIdInfo * +CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint8_t proto) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << " proto:" << (uint16_t)proto + << std::endl; +#endif + + class CFlowStatUserIdInfo *new_id = new CFlowStatUserIdInfo(proto); + if (new_id != NULL) { + std::pair<flow_stat_user_id_map_it_t, bool> ret; + ret = m_map.insert(std::pair<uint32_t, class CFlowStatUserIdInfo *>(user_id, new_id)); + if (ret.second == false) { + printf("%s Error: Trying to add user id %d which already exist\n", __func__, user_id); + delete new_id; + return NULL; + } + return new_id; + } else { + return NULL; + } +} + +int CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << " proto:" << (uint16_t)proto + << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + c_user_id = add_user_id(user_id, proto); + if (! c_user_id) + return -1; + return 0; + } else { + return c_user_id->add_stream(proto); + } +} + +int CFlowStatUserIdMap::del_stream(uint32_t user_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + return -1; + } + + if (c_user_id->del_stream() == 0) { + // ref count of this port became 0. can release this entry. + m_map.erase(user_id); + delete c_user_id; + } + + return 0; +} + +int CFlowStatUserIdMap::start_stream(uint32_t user_id, uint16_t hw_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << " hw_id:" << hw_id << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + fprintf(stderr, "%s Error: Trying to associate hw id %d to user_id %d but it does not exist\n" + , __func__, hw_id, user_id); + return -1; + } + + if (c_user_id->is_hw_id()) { + fprintf(stderr, "%s Error: Trying to associate hw id %d to user_id %d but it is already associate to %ld\n" + , __func__, hw_id, user_id, c_user_id->get_hw_id()); + return -1; + } + c_user_id->set_hw_id(hw_id); + c_user_id->add_started_stream(); + + return 0; +} + +int CFlowStatUserIdMap::start_stream(uint32_t user_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + fprintf(stderr, "%s Error: Trying to start stream on user_id %d but it does not exist\n" + , __func__, user_id); + return -1; + } + + c_user_id->add_started_stream(); + + return 0; +} + +// return: negative number in case of error. +// Number of started streams attached to used_id otherwise. +int CFlowStatUserIdMap::stop_stream(uint32_t user_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + fprintf(stderr, "%s Error: Trying to stop stream on user_id %d but it does not exist\n" + , __func__, user_id); + return -1; + } + + return c_user_id->stop_started_stream(); +} + +bool CFlowStatUserIdMap::is_started(uint32_t user_id) { + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + return false; + } + + return c_user_id->is_started(); +} + +uint8_t CFlowStatUserIdMap::l4_proto(uint32_t user_id) { + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + return 0; + } + + return c_user_id->get_proto(); +} + +uint16_t CFlowStatUserIdMap::unmap(uint32_t user_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << user_id << std::endl; +#endif + + class CFlowStatUserIdInfo *c_user_id; + + c_user_id = find_user_id(user_id); + if (! c_user_id) { + return UINT16_MAX; + } + uint16_t old_hw_id = c_user_id->get_hw_id(); + c_user_id->reset_hw_id(); + + return old_hw_id; +} + +/************** class CFlowStatHwIdMap ***************/ +CFlowStatHwIdMap::CFlowStatHwIdMap() { + m_num_free = MAX_FLOW_STATS; + for (int i = 0; i < MAX_FLOW_STATS; i++) { + m_map[i] = FREE_HW_ID; + } +} + +std::ostream& operator<<(std::ostream& os, const CFlowStatHwIdMap& cf) { + int count = 0; + + os << "HW id map:\n"; + os << " num free:" << cf.m_num_free << std::endl; + for (int i = 0; i < MAX_FLOW_STATS; i++) { + if (cf.m_map[i] != 0) { + count++; + os << "(" << i << ":" << cf.m_map[i] << ")"; + if (count == 10) { + os << std::endl; + count = 0; + } + } + } + + return os; +} + +uint16_t CFlowStatHwIdMap::find_free_hw_id() { + for (int i = 0; i < MAX_FLOW_STATS; i++) { + if (m_map[i] == FREE_HW_ID) + return i; + } + + return FREE_HW_ID; +} + +void CFlowStatHwIdMap::map(uint16_t hw_id, uint32_t user_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " hw id:" << hw_id << " user id:" << user_id << std::endl; +#endif + + m_map[hw_id] = user_id; + m_num_free--; +} + +void CFlowStatHwIdMap::unmap(uint16_t hw_id) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " hw id:" << hw_id << std::endl; +#endif + + m_map[hw_id] = FREE_HW_ID; + m_num_free++; +} + +/************** class CFlowStatRuleMgr ***************/ +CFlowStatRuleMgr::CFlowStatRuleMgr() { + m_api = NULL; +} + +std::ostream& operator<<(std::ostream& os, const CFlowStatRuleMgr& cf) { + os << "Flow stat rule mgr (" << cf.m_num_ports << ") ports:" << std::endl; + os << cf.m_hw_id_map; + os << cf.m_user_id_map; + return os; +} + +int CFlowStatRuleMgr::compile_stream(const TrexStream * stream, Cxl710Parser &parser) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << stream->m_rx_check.m_user_id << " en:"; + std::cout << stream->m_rx_check.m_enabled << std::endl; +#endif + + // currently we support only IP ID rule types + // all our ports are the same type, so testing port 0 is enough + uint16_t num_counters, capabilities; + m_api->get_interface_stat_info(0, num_counters, capabilities); + if ((capabilities & TrexPlatformApi::IF_STAT_IPV4_ID) == 0) { + return -2; + } + + if (parser.parse(stream->m_pkt.binary, stream->m_pkt.len) != 0) { + // if we could not parse the packet, but no stat count needed, it is probably OK. + if (stream->m_rx_check.m_enabled) { + fprintf(stderr, "Error: %s - Compilation failed\n", __func__); + return -1; + } else { + return 0; + } + } + + if (!parser.is_fdir_supported()) { + if (stream->m_stream_id <= 0) { + // rx stat not needed. Do nothing. + return 0; + } else { + // rx stat needed, but packet format is not supported + fprintf(stderr, "Error: %s - Unsupported packet format for rx stat\n", __func__); + return -1; + } + } + return 0; +} + +int CFlowStatRuleMgr::add_stream(const TrexStream * stream) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << stream->m_rx_check.m_user_id << std::endl; +#endif + + if (! m_api ) { + TrexStateless *tstateless = get_stateless_obj(); + m_api = tstateless->get_platform_api(); + // m_api = get_stateless_obj()->get_platform_api(); + m_api->get_port_num(m_num_ports); + } + + Cxl710Parser parser; + int ret; + + if (! stream->m_rx_check.m_enabled) { + return 0; + } + + if ((ret = compile_stream(stream, parser)) < 0) + return ret; + + uint8_t l4_proto; + if (parser.get_l4_proto(l4_proto) < 0) { + printf("Error: %s failed finding l4 proto\n", __func__); + return -1; + } + + return m_user_id_map.add_stream(stream->m_rx_check.m_user_id, l4_proto); +} + +int CFlowStatRuleMgr::del_stream(const TrexStream * stream) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << stream->m_rx_check.m_user_id << std::endl; +#endif + + if (! stream->m_rx_check.m_enabled) { + return 0; + } + + return m_user_id_map.del_stream(stream->m_rx_check.m_user_id); +} + +// called on all streams, when stream start to transmit +// If stream need flow stat counting, make sure the type of packet is supported, and +// embed needed info in packet. +// If stream does not need flow stat counting, make sure it does not interfere with +// other streams that do need stat counting. +// Might change the IP ID of the stream packet +int CFlowStatRuleMgr::start_stream(TrexStream * stream) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << stream->m_rx_check.m_user_id << std::endl; +#endif + + Cxl710Parser parser; + int ret; + + if (! m_api ) { + return 0; + } + + if ((ret = compile_stream(stream, parser)) < 0) + return ret; + + // first handle streams that do not need rx stat + if (! stream->m_rx_check.m_enabled) { + // no need for stat count + uint16_t ip_id; + if (parser.get_ip_id(ip_id) < 0) { + return 0; // if we could not find and ip id, no need to fix + } + // verify no reserved IP_ID used, and change if needed + if (ip_id >= IP_ID_RESERVE_BASE) { + if (parser.set_ip_id(ip_id & 0xefff) < 0) { + return -1; + } + } + return 0; + } + + // from here, we know the stream need rx stat + if (m_user_id_map.is_started(stream->m_rx_check.m_user_id)) { + m_user_id_map.start_stream(stream->m_rx_check.m_user_id); // just increase ref count; + } else { + uint16_t hw_id = m_hw_id_map.find_free_hw_id(); + if (hw_id == FREE_HW_ID) { + printf("Error: %s failed finding free hw_id\n", __func__); + return -1; + } else { + uint32_t user_id = stream->m_rx_check.m_user_id; + m_user_id_map.start_stream(user_id, hw_id); + m_hw_id_map.map(hw_id, user_id); + add_hw_rule(hw_id, m_user_id_map.l4_proto(user_id)); + } + } + + uint16_t hw_id = m_user_id_map.get_hw_id(stream->m_rx_check.m_user_id); // can't fail if we got here + parser.set_ip_id(IP_ID_RESERVE_BASE + hw_id); + + return 0; +} + +int CFlowStatRuleMgr::add_hw_rule(uint16_t hw_id, uint8_t proto) { + for (int port = 0; port < m_num_ports; port++) { + m_api->add_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id); + } + + return 0; +} + +int CFlowStatRuleMgr::stop_stream(const TrexStream * stream) { +#ifdef __DEBUG_FUNC_ENTRY__ + std::cout << __METHOD_NAME__ << " user id:" << stream->m_rx_check.m_user_id << std::endl; +#endif + if (! stream->m_rx_check.m_enabled) { + return 0; + } + if (! m_api ) { + return 0; + } + + if (m_user_id_map.stop_stream(stream->m_rx_check.m_user_id) == 0) { + // last stream associated with the entry stopped transmittig. + // remove user_id <--> hw_id mapping + uint8_t proto = m_user_id_map.l4_proto(stream->m_rx_check.m_user_id); + uint16_t hw_id = m_user_id_map.unmap(stream->m_rx_check.m_user_id); + if (hw_id >= MAX_FLOW_STATS) { + fprintf(stderr, "Error: %s got wrong hw_id %d from unmap\n", __func__, hw_id); + return -1; + } else { + // update counters, and reset before unmapping + class CFlowStatUserIdInfo *p_user_id = m_user_id_map.find_user_id(m_hw_id_map.get_user_id(hw_id)); + assert(p_user_id != NULL); + uint64_t counter; + for (uint8_t port = 0; port < m_num_ports; port++) { + m_api->del_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id); + m_api->get_rx_stats(port, &counter, hw_id, true); + p_user_id->set_rx_counter(port, counter); + p_user_id->set_tx_counter(port, counter); //??? until tx work, just set for same value + } + m_hw_id_map.unmap(hw_id); + } + } + return 0; +} + +// return false if no counters changed since last run. true otherwise +bool CFlowStatRuleMgr::dump_json(std::string & json) { + uint64_t stats[TREX_FDIR_STAT_SIZE]; + Json::FastWriter writer; + Json::Value root; + bool ret = false; + + if (! m_api ) { + return false; + } + root["name"] = "rx-stats"; + root["type"] = 0; + Json::Value &data_section = root["data"]; + + // read hw counters, and update + data_section["timestamp"] = Json::Value::UInt64(os_get_hr_tick_64()); + for (uint8_t port = 0; port < m_num_ports; port++) { + m_api->get_rx_stats(port, stats, -1, false); + for (int i = 0; i < TREX_FDIR_STAT_SIZE; i++) { + if (stats[i] != 0) { + m_user_id_map.find_user_id(m_hw_id_map.get_user_id(i))->set_rx_counter(port, stats[i]); + m_user_id_map.find_user_id(m_hw_id_map.get_user_id(i))->set_tx_counter(port, stats[i]); //??? until tx work, just set for same value + } + } + } + + // build json report + flow_stat_user_id_map_it_t it; + for (it = m_user_id_map.begin(); it != m_user_id_map.end(); it++) { + CFlowStatUserIdInfo *user_id_info = it->second; + uint32_t user_id = it->first; + std::string str_user_id = static_cast<std::ostringstream*>( &(std::ostringstream() + << user_id) )->str(); + for (uint8_t port = 0; port < m_num_ports; port++) { + if ((user_id_info->get_tx_counter(port) != 0) || (user_id_info->get_rx_counter(port) != 0)) { + std::string str_port = static_cast<std::ostringstream*>( &(std::ostringstream() + << port) )->str(); + data_section[str_user_id]["rx"][str_port] = Json::Value::UInt64(user_id_info->get_rx_counter(port)); + data_section[str_user_id]["tx"][str_port] = Json::Value::UInt64(user_id_info->get_tx_counter(port)); + ret = true; + } + } + } + + json = writer.write(root); + return ret; +} diff --git a/src/flow_stat.h b/src/flow_stat.h new file mode 100644 index 00000000..444daab0 --- /dev/null +++ b/src/flow_stat.h @@ -0,0 +1,139 @@ +/* + Ido Barnea + Cisco Systems, Inc. +*/ + +/* + Copyright (c) 2015-2016 Cisco Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __FLOW_STAT_H__ +#define __FLOW_STAT_H__ +#include <stdio.h> +#include <string> +#include <map> +#include "trex_defs.h" + +#define MAX_FLOW_STATS 128 +// range reserved for rx stat measurement is from IP_ID_RESERVE_BASE to 0xffff +// Do not change this value. In i350 cards, we filter according to first byte of IP ID +// In other places, we identify packets by if (ip_id > IP_ID_RESERVE_BASE) +#define IP_ID_RESERVE_BASE 0xff00 + +typedef std::map<uint32_t, uint16_t> flow_stat_map_t; +typedef std::map<uint32_t, uint16_t>::iterator flow_stat_map_it_t; + +class CPhyEthIF; +class Cxl710Parser; + +class CFlowStatUserIdInfo { + public: + CFlowStatUserIdInfo(uint8_t proto); + friend std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdInfo& cf); + void set_rx_counter(uint8_t port, uint64_t val) {m_rx_counter[port] = val;} + uint64_t get_rx_counter(uint8_t port) {return m_rx_counter[port] + m_rx_counter_base[port];} + void set_tx_counter(uint8_t port, uint64_t val) {m_tx_counter[port] = val;} + uint64_t get_tx_counter(uint8_t port) {return m_tx_counter[port] + m_tx_counter_base[port];} + void set_hw_id(uint16_t hw_id) {m_hw_id = hw_id;} + uint64_t get_hw_id() {return m_hw_id;} + void reset_hw_id(); + bool is_hw_id() {return (m_hw_id != UINT16_MAX);} + uint64_t get_proto() {return m_proto;} + uint8_t get_ref_count() {return m_ref_count;} + int add_stream(uint8_t proto); + int del_stream() {m_ref_count--; return m_ref_count;} + void add_started_stream() {m_trans_ref_count++;} + int stop_started_stream() {m_trans_ref_count--; return m_trans_ref_count;} + bool is_started() {return (m_trans_ref_count != 0);} + + private: + uint64_t m_rx_counter[TREX_MAX_PORTS]; // How many packets received with this user id since stream start + // How many packets received with this user id, since stream creation, before stream start. + uint64_t m_rx_counter_base[TREX_MAX_PORTS]; + uint64_t m_tx_counter[TREX_MAX_PORTS]; // How many packets transmitted with this user id since stream start + // How many packets transmitted with this user id, since stream creation, before stream start. + uint64_t m_tx_counter_base[TREX_MAX_PORTS]; + uint16_t m_hw_id; // Associated hw id. UINT16_MAX if no associated hw id. + uint8_t m_proto; // protocol (UDP, TCP, other), associated with this user id. + uint8_t m_ref_count; // How many streams with this ref count exists + uint8_t m_trans_ref_count; // How many streams with this ref count currently transmit +}; + +typedef std::map<uint32_t, class CFlowStatUserIdInfo *> flow_stat_user_id_map_t; +typedef std::map<uint32_t, class CFlowStatUserIdInfo *>::iterator flow_stat_user_id_map_it_t; + +class CFlowStatUserIdMap { + public: + CFlowStatUserIdMap(); + friend std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdMap& cf); + uint16_t get_hw_id(uint32_t user_id); + class CFlowStatUserIdInfo * find_user_id(uint32_t user_id); + class CFlowStatUserIdInfo * add_user_id(uint32_t user_id, uint8_t proto); + int add_stream(uint32_t user_id, uint8_t proto); + int del_stream(uint32_t user_id); + int start_stream(uint32_t user_id, uint16_t hw_id); + int start_stream(uint32_t user_id); + int stop_stream(uint32_t user_id); + bool is_started(uint32_t user_id); + uint8_t l4_proto(uint32_t user_id); + uint16_t unmap(uint32_t user_id); + flow_stat_user_id_map_it_t begin() {return m_map.begin();} + flow_stat_user_id_map_it_t end() {return m_map.end();} + private: + flow_stat_user_id_map_t m_map; +}; + +class CFlowStatHwIdMap { + public: + CFlowStatHwIdMap(); + friend std::ostream& operator<<(std::ostream& os, const CFlowStatHwIdMap& cf); + uint16_t find_free_hw_id(); + void map(uint16_t hw_id, uint32_t user_id); + void unmap(uint16_t hw_id); + uint32_t get_user_id(uint16_t hw_id) {return m_map[hw_id];}; + private: + uint32_t m_map[MAX_FLOW_STATS]; // translation from hw id to user id + uint16_t m_num_free; // How many free entries in the m_rules array +}; + +class CFlowStatRuleMgr { + public: + enum flow_stat_rule_types_e { + FLOW_STAT_RULE_TYPE_NONE, + FLOW_STAT_RULE_TYPE_IPV4_ID, + FLOW_STAT_RULE_TYPE_PAYLOAD, + FLOW_STAT_RULE_TYPE_IPV6_FLOW_LABEL, + }; + + CFlowStatRuleMgr(); + friend std::ostream& operator<<(std::ostream& os, const CFlowStatRuleMgr& cf); + int add_stream(const TrexStream * stream); + int del_stream(const TrexStream * stream); + int start_stream(TrexStream * stream); + int stop_stream(const TrexStream * stream); + bool dump_json(std::string & json); + + private: + int compile_stream(const TrexStream * stream, Cxl710Parser &parser); + int add_hw_rule(uint16_t hw_id, uint8_t proto); + + private: + class CFlowStatHwIdMap m_hw_id_map; // map hw ids to user ids + class CFlowStatUserIdMap m_user_id_map; // map user ids to hw ids + uint8_t m_num_ports; // How many ports are being used + const TrexPlatformApi *m_api; +}; + +#endif diff --git a/src/flow_stat_parser.cpp b/src/flow_stat_parser.cpp new file mode 100644 index 00000000..52824f73 --- /dev/null +++ b/src/flow_stat_parser.cpp @@ -0,0 +1,137 @@ +/* + Ido Barnea + Cisco Systems, Inc. +*/ + +/* + Copyright (c) 2016-2016 Cisco Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include <common/basic_utils.h> +#include <common/Network/Packet/IPHeader.h> +#include <common/Network/Packet/IPv6Header.h> +#include <common/Network/Packet/EthernetHeader.h> +#include <flow_stat_parser.h> + +Cxl710Parser::Cxl710Parser() { + reset(); +} + +void Cxl710Parser::reset() { + m_ipv4 = 0; + m_l4_proto = 0; + m_fdir_supported = false; +} + +int Cxl710Parser::parse(uint8_t *p, uint16_t len) { + EthernetHeader *ether = (EthernetHeader *)p; + + switch( ether->getNextProtocol() ) { + case EthernetHeader::Protocol::IP : + m_ipv4 = (IPHeader *)(p + 14); + m_fdir_supported = true; + break; + case EthernetHeader::Protocol::VLAN : + switch ( ether->getVlanProtocol() ){ + case EthernetHeader::Protocol::IP: + m_ipv4 = (IPHeader *)(p + 18); + m_fdir_supported = true; + break; + default: + m_fdir_supported = false; + return -1; + } + + break; + default: + m_fdir_supported = false; + return -1; + break; + } + + return 0; +} + +int Cxl710Parser::get_ip_id(uint16_t &ip_id) { + if (! m_ipv4) + return -1; + + ip_id = m_ipv4->getId(); + + return 0; +} + +int Cxl710Parser::set_ip_id(uint16_t new_id) { + if (! m_ipv4) + return -1; + + // Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad + m_ipv4->updateCheckSum(m_ipv4->getId(), PKT_NTOHS(new_id)); + m_ipv4->setId(new_id); + + return 0; +} + +int Cxl710Parser::get_l4_proto(uint8_t &proto) { + if (! m_ipv4) + return -1; + + proto = m_ipv4->getProtocol(); + + return 0; +} + +static const uint16_t TEST_IP_ID = 0xabcd; +static const uint8_t TEST_L4_PROTO = 0x11; + +int Cxl710Parser::test() { + uint16_t ip_id = 0; + uint8_t l4_proto; + uint8_t test_pkt[] = { + // ether header + 0x74, 0xa2, 0xe6, 0xd5, 0x39, 0x25, + 0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x02, + 0x81, 0x00, + 0x0a, 0xbc, 0x08, 0x00, // vlan + // IP header + 0x45,0x02,0x00,0x30, + 0x00,0x00,0x40,0x00, + 0xff, TEST_L4_PROTO, 0xbd,0x04, + 0x10,0x0,0x0,0x1, + 0x30,0x0,0x0,0x1, + }; + + // good packet + assert (parse(test_pkt, sizeof(test_pkt)) == 0); + m_ipv4->updateCheckSum(); + assert(m_ipv4->isChecksumOK() == true); + set_ip_id(TEST_IP_ID); + // utl_DumpBuffer(stdout, test_pkt, sizeof(test_pkt), 0); + get_ip_id(ip_id); + assert(ip_id == TEST_IP_ID); + assert(m_ipv4->isChecksumOK() == true); + assert(get_l4_proto(l4_proto) == 0); + assert(l4_proto == TEST_L4_PROTO); + assert(m_fdir_supported == true); + + reset(); + + // bad packet + test_pkt[16] = 0xaa; + assert (parse(test_pkt, sizeof(test_pkt)) == -1); + assert(m_fdir_supported == false); + + return 0; +} diff --git a/src/flow_stat_parser.h b/src/flow_stat_parser.h new file mode 100644 index 00000000..606a1bec --- /dev/null +++ b/src/flow_stat_parser.h @@ -0,0 +1,37 @@ +/* + Ido Barnea + Cisco Systems, Inc. +*/ + +/* + Copyright (c) 2016-2016 Cisco Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +class Cxl710Parser { + public: + Cxl710Parser(); + void reset(); + int parse(uint8_t *pkt, uint16_t len); + bool is_fdir_supported() {return m_fdir_supported == true;}; + int get_ip_id(uint16_t &ip_id); + int set_ip_id(uint16_t ip_id); + int get_l4_proto(uint8_t &proto); + int test(); + + private: + IPHeader *m_ipv4; + bool m_fdir_supported; + uint8_t m_l4_proto; +}; diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index 576f7d6e..e946f5d5 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -20,6 +20,7 @@ limitations under the License. */ #include "bp_sim.h" +#include "flow_stat_parser.h" #include <common/gtest.h> #include <common/basic_utils.h> #include <trex_stateless.h> @@ -3570,3 +3571,18 @@ TEST_F(basic_stl, vm_split_client_var) { } /********************************************* Itay Tests End *************************************/ +class rx_stat_pkt_parse : public testing::Test { + protected: + virtual void SetUp() { + } + virtual void TearDown() { + } + public: +}; + + +TEST_F(rx_stat_pkt_parse, x710_parser) { + Cxl710Parser parser; + + parser.test(); +} diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index f8bc10d5..831fd778 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -31,6 +31,7 @@ limitations under the License. * * @author imarom (06-Oct-15) */ + class TrexPlatformGlobalStats { public: TrexPlatformGlobalStats() { @@ -98,7 +99,12 @@ public: class TrexPlatformApi { public: - + enum driver_stat_capabilities_e { + IF_STAT_IPV4_ID = 1, + IF_STAT_PAYLOAD = 2, + IF_STAT_IPV6_FLOW_LABEL = 4, + }; + enum driver_speed_e { SPEED_INVALID, SPEED_1G, @@ -109,9 +115,18 @@ public: virtual void port_id_to_cores(uint8_t port_id, std::vector<std::pair<uint8_t, uint8_t>> &cores_id_list) const = 0; virtual void get_global_stats(TrexPlatformGlobalStats &stats) const = 0; virtual void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const = 0; - virtual void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const = 0; + + virtual void get_interface_info(uint8_t interface_id, std::string &driver_name, + driver_speed_e &speed, + bool &has_crc) const = 0; + virtual void publish_async_data_now(uint32_t key) const = 0; virtual uint8_t get_dp_core_count() const = 0; + virtual void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const =0; + virtual int get_rx_stats(uint8_t port_id, uint64_t *stats, int index, bool reset) const = 0; + virtual void get_port_num(uint8_t &port_num) const = 0; + virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0; + virtual int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0; virtual ~TrexPlatformApi() {} }; @@ -127,10 +142,19 @@ public: void port_id_to_cores(uint8_t port_id, std::vector<std::pair<uint8_t, uint8_t>> &cores_id_list) const; void get_global_stats(TrexPlatformGlobalStats &stats) const; void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const; - void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const; + + void get_interface_info(uint8_t interface_id, + std::string &driver_name, + driver_speed_e &speed, + bool &has_crc) const; + void publish_async_data_now(uint32_t key) const; uint8_t get_dp_core_count() const; - + void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const; + int get_rx_stats(uint8_t port_id, uint64_t *stats, int index, bool reset) const; + void get_port_num(uint8_t &port_num) const; + int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const; + int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const; }; /** @@ -143,13 +167,24 @@ public: void port_id_to_cores(uint8_t port_id, std::vector<std::pair<uint8_t, uint8_t>> &cores_id_list) const; void get_global_stats(TrexPlatformGlobalStats &stats) const; void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const; - void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const { + + void get_interface_info(uint8_t interface_id, + std::string &driver_name, + driver_speed_e &speed, + bool &has_crc) const { driver_name = "MOCK"; speed = SPEED_INVALID; + has_crc = false; } void publish_async_data_now(uint32_t key) const {} uint8_t get_dp_core_count() const; + void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const + {num_counters = 0; capabilities = 0;} + int get_rx_stats(uint8_t port_id, uint64_t *stats, int index, bool reset) const {return 0;} + void get_port_num(uint8_t &port_num) const {port_num = 2;}; + int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;} + int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;} }; #endif /* __TREX_PLATFORM_API_H__ */ diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 701ae13e..8cc94e7d 100755..100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -107,7 +107,7 @@ extern "C" { typedef struct rte_mbuf * (*rte_mbuf_convert_to_one_seg_t)(struct rte_mbuf *m); struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m); -extern "C" int vmxnet3_xmit_set_callback(rte_mbuf_convert_to_one_seg_t cb); +extern "C" void i40e_set_trex_mode(int mode); #define RTE_TEST_TX_DESC_DEFAULT 512 #define RTE_TEST_RX_DESC_DROP 0 @@ -127,6 +127,11 @@ public: virtual TrexPlatformApi::driver_speed_e get_driver_speed(uint8_t port_id) = 0; + /* by default NIC driver adds CRC */ + virtual bool has_crc_added() { + return true; + } + virtual int get_min_sample_rate(void)=0; virtual void update_configuration(port_cfg_t * cfg)=0; virtual void update_global_config_fdir(port_cfg_t * cfg)=0; @@ -135,7 +140,7 @@ public: return(false); } virtual int configure_rx_filter_rules(CPhyEthIF * _if)=0; - + virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id) {return -1;}; virtual bool is_hardware_support_drop_queue(){ return(false); } @@ -146,6 +151,10 @@ public: virtual int wait_for_stable_link()=0; virtual void wait_after_link_up(){}; virtual bool flow_control_disable_supported(){return true;} + virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *stats, uint32_t *prev_stats, int index) {return -1;} + virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) { return -1;} + virtual int get_stat_counters_num() {return 0;} + virtual int get_rx_stat_capabilities() {return 0;} }; @@ -176,7 +185,9 @@ public: virtual int configure_drop_queue(CPhyEthIF * _if); virtual int configure_rx_filter_rules(CPhyEthIF * _if); - + int configure_rx_filter_rules_statefull(CPhyEthIF * _if); + int configure_rx_filter_rules_stateless(CPhyEthIF * _if); + virtual bool is_hardware_support_drop_queue(){ return(true); } @@ -201,6 +212,10 @@ public: return TrexPlatformApi::SPEED_1G; } + virtual bool has_crc_added() { + return false; + } + static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1GVm() ); } @@ -272,6 +287,10 @@ public: class CTRexExtendedDriverBase40G : public CTRexExtendedDriverBase10G { public: CTRexExtendedDriverBase40G(){ + // Since we support only 128 counters per if, it is OK to configure here 4 statically. + // If we want to support more counters in case in case of card having less interfaces, we + // Will have to identify the number of interfaces dynamically. + m_if_per_card = 4; } TrexPlatformApi::driver_speed_e get_driver_speed(uint8_t port_id) { @@ -295,7 +314,7 @@ public: virtual void update_configuration(port_cfg_t * cfg); virtual int configure_rx_filter_rules(CPhyEthIF * _if); - + virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id); virtual bool is_hardware_filter_is_supported(){ return (true); } @@ -305,13 +324,18 @@ public: } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); + int get_rx_stats(CPhyEthIF * _if, uint32_t *stats, uint32_t *prev_stats, int index); + int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd); + int get_stat_counters_num() {return TREX_FDIR_STAT_SIZE;} + int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID;} virtual int wait_for_stable_link(); // disabling flow control on 40G using DPDK API causes the interface to malfunction bool flow_control_disable_supported(){return false;} +private: + void add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl, uint16_t ip_id, int queue, uint16_t stat_idx); + virtual int configure_rx_filter_rules_statfull(CPhyEthIF * _if); private: - void add_rules(CPhyEthIF * _if, - uint16_t type, - uint8_t ttl); + uint8_t m_if_per_card; }; class CTRexExtendedDriverBaseVIC : public CTRexExtendedDriverBase40G { @@ -330,7 +354,6 @@ public: bool flow_control_disable_supported(){return false;} virtual void update_configuration(port_cfg_t * cfg); - }; @@ -1168,20 +1191,18 @@ typedef struct cnt_name_ { #define MY_REG(a) {a,(char *)#a} void CPhyEthIFStats::Clear(){ - - ipackets =0; - ibytes =0 ; - - f_ipackets=0; - f_ibytes=0; - - opackets=0; - obytes=0; - - ierrors=0; - oerrors=0; - imcasts=0; - rx_nombuf=0; + ipackets = 0; + ibytes = 0; + f_ipackets = 0; + f_ibytes = 0; + opackets = 0; + obytes = 0; + ierrors = 0; + oerrors = 0; + imcasts = 0; + rx_nombuf = 0; + memset(m_rx_per_flow, 0, sizeof(m_rx_per_flow)); + m_fdir_stats_first_time = true; } @@ -1280,18 +1301,21 @@ void CPhyEthIF::dump_stats_extended(FILE *fd){ MY_REG(IXGBE_FDIRMISS ) }; - fprintf (fd," externded counter \n"); + fprintf (fd," extended counters \n"); int i; for (i=0; i<sizeof(reg)/sizeof(reg[0]); i++) { cnt_name_t *lp=®[i]; uint32_t c=pci_reg_read(lp->offset); - if (c) { + // xl710 bug. Counter values are -559038737 when they should be 0 + if (c && c != -559038737 ) { fprintf (fd," %s : %d \n",lp->name,c); } } } - +int CPhyEthIF::get_rx_stat_capabilities() { + return get_ex_drv()->get_rx_stat_capabilities(); +} void CPhyEthIF::configure(uint16_t nb_rx_queue, uint16_t nb_tx_queue, @@ -1542,16 +1566,55 @@ void CPhyEthIF::get_stats_1g(CPhyEthIFStats *stats){ } -void CPhyEthIF::get_stats(CPhyEthIFStats *stats){ +int CPhyEthIF::dump_fdir_global_stats(FILE *fd) { + return get_ex_drv()->dump_fdir_global_stats(this, fd); +} - get_ex_drv()->get_extended_stats(this,stats); +// get/reset flow director counters +// return 0 if OK. -1 if operation not supported. +// stats - If not NULL, returning counter numbers in it. +// index - If non negative, get only counter with this index +// reset - If true, reset counter value after reading +int CPhyEthIF::get_rx_stats(uint64_t *stats, int index, bool reset) { + uint32_t diff_stats[TREX_FDIR_STAT_SIZE]; + int start, len; - m_last_tx_rate = m_bw_tx.add(stats->obytes); - m_last_rx_rate = m_bw_rx.add(stats->ibytes); - m_last_tx_pps = m_pps_tx.add(stats->opackets); - m_last_rx_pps = m_pps_rx.add(stats->ipackets); -} + if (index >= 0) { + start = index; + len = 1; + } else { + start = 0; + len = TREX_FDIR_STAT_SIZE; + } + + if (get_ex_drv()->get_rx_stats(this, diff_stats, m_stats.m_fdir_prev_stats, index) < 0) { + return -1; + } + // First time, just syncing the counters + if (m_stats.m_fdir_stats_first_time) { + m_stats.m_fdir_stats_first_time = false; + if (stats) { + memset(stats, 0, sizeof(uint64_t) * TREX_FDIR_STAT_SIZE); + } + return 0; + } + + for (int i = start; i < (start + len); i++) { + if ( reset ) { + // return value so far, and reset + stats[i] = m_stats.m_rx_per_flow[i] + diff_stats[i]; + m_stats.m_rx_per_flow[i] = 0; + } else { + m_stats.m_rx_per_flow[i] += diff_stats[i]; + if (stats != NULL) { + stats[i] = m_stats.m_rx_per_flow[i]; + } + } + } + + return 0; +} void dump_hw_state(FILE *fd,struct ixgbe_hw_stats *hs ){ @@ -1642,7 +1705,12 @@ void dump_hw_state(FILE *fd,struct ixgbe_hw_stats *hs ){ void CPhyEthIF::update_counters(){ - get_stats(&m_stats); + get_ex_drv()->get_extended_stats(this, &m_stats); + + m_last_tx_rate = m_bw_tx.add(m_stats.obytes); + m_last_rx_rate = m_bw_rx.add(m_stats.ibytes); + m_last_tx_pps = m_pps_tx.add(m_stats.opackets); + m_last_rx_pps = m_pps_rx.add(m_stats.ipackets); } void CPhyEthIF::dump_stats(FILE *fd){ @@ -2579,7 +2647,7 @@ private: void check_for_dp_messages(); public: - int start_send_master(); + int start_master_statefull(); int start_master_stateless(); int run_in_core(virtual_thread_id_t virt_core_id); int stop_core(virtual_thread_id_t virt_core_id); @@ -3530,9 +3598,10 @@ CGlobalTRex::publish_async_data() { m_mg.dump_json_v2(json ); m_zmq_publisher.publish_json(json); - /* stateless info - nothing for now */ - //m_trex_stateless->generate_publish_snapshot(json); - //m_zmq_publisher.publish_json(json); + if (get_is_stateless()) { + if (m_trex_stateless->m_rx_flow_stat.dump_json(json)) + m_zmq_publisher.publish_json(json); + } } void @@ -3795,10 +3864,7 @@ int CGlobalTRex::start_master_stateless(){ return (0); } - - - -int CGlobalTRex::start_send_master(){ +int CGlobalTRex::start_master_statefull() { int i; for (i=0; i<BP_MAX_CORES; i++) { m_signal[i]=0; @@ -4235,7 +4301,8 @@ int main_test(int argc , char * argv[]){ return (-1); } - + // We init i40e fdir registers differently in case of stateless. Must set this before rte_eal_init which initiates the registers + i40e_set_trex_mode(get_is_stateless() ? 1:0 ); ret = rte_eal_init(global_dpdk_args_num, (char **)global_dpdk_args); if (ret < 0){ @@ -4279,7 +4346,7 @@ int main_test(int argc , char * argv[]){ g_trex.start_master_stateless(); }else{ - g_trex.start_send_master(); + g_trex.start_master_statefull(); } if (CGlobalInfo::m_options.m_debug_pkt_proto != 0) { @@ -4369,8 +4436,18 @@ int CTRexExtendedDriverBase1G::configure_drop_queue(CPhyEthIF * _if) { return 0; } + int CTRexExtendedDriverBase1G::configure_rx_filter_rules(CPhyEthIF * _if){ + if ( get_is_stateless() ) { + return configure_rx_filter_rules_stateless(_if); + } else { + return configure_rx_filter_rules_statefull(_if); + } + return 0; +} + +int CTRexExtendedDriverBase1G::configure_rx_filter_rules_statefull(CPhyEthIF * _if) { uint16_t hops = get_rx_check_hops(); uint16_t v4_hops = (hops << 8)&0xff00; uint8_t protocol; @@ -4462,6 +4539,54 @@ int CTRexExtendedDriverBase1G::configure_rx_filter_rules(CPhyEthIF * _if){ return (0); } +// Sadly, DPDK has no support for i350 filters, so we need to implement by writing to registers. +int CTRexExtendedDriverBase1G::configure_rx_filter_rules_stateless(CPhyEthIF * _if) { + /* enable filter to pass packet to rx queue 1 */ + _if->pci_reg_write( E1000_IMIR(0), 0x00020000); + _if->pci_reg_write( E1000_IMIREXT(0), 0x00081000); + + uint8_t len = 24; + uint32_t mask = 0x1 | 0x2; // first two rules + int rule_id; + + // clear rules 0, 1 registers + for (rule_id = 0 ; rule_id < 2; rule_id++) { + for (int i=0; i<0xff; i+=4) { + _if->pci_reg_write( (E1000_FHFT(rule_id)+i) , 0); + } + } + + rule_id = 0; + // filter for byte 18 of packet (lsb of IP ID) should equal ff + _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)) , 0x00ff0000); + _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x04); /* MASK */ + // + bytes 12 + 13 (ether type) should indicate IP. + _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x00000008); + _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ + // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 + _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); + + // same like 0, but with vlan. type should be vlan. Inside vlan, should be IP with lsb of IP ID equals 0xff + rule_id = 1; + // filter for byte 22 of packet (msb of IP ID) should equal ff + _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 4) , 0x00ff0000); + _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x40 | 0x03); /* MASK */ + // + bytes 12 + 13 (ether type) should indicate VLAN. + _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x00000081); + _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ + // + bytes 16 + 17 (vlan type) should indicate IP. + _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) ) , 0x00000080); + // Was written together with IP ID filter + // _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x03); /* MASK */ + // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 + _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); + + /* enable rules */ + _if->pci_reg_write(E1000_WUFC, (mask << 16) | (1 << 14) ); + + return (0); +} + void CTRexExtendedDriverBase1G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ @@ -4632,15 +4757,10 @@ int CTRexExtendedDriverBase10G::wait_for_stable_link(){ } //////////////////////////////////////////////////////////////////////////////// - - void CTRexExtendedDriverBase40G::clear_extended_stats(CPhyEthIF * _if){ - rte_eth_stats_reset(_if->get_port_id()); - } - void CTRexExtendedDriverBaseVIC::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; @@ -4655,15 +4775,14 @@ void CTRexExtendedDriverBase40G::update_configuration(port_cfg_t * cfg){ cfg->update_global_config_fdir_40g(); } - /* Add rule to send packets with protocol 'type', and ttl 'ttl' to rx queue 1 */ -void CTRexExtendedDriverBase40G::add_rules(CPhyEthIF * _if, - uint16_t type, - uint8_t ttl){ - uint8_t port_id = _if->get_port_id(); +// ttl is used in statefull mode, and ip_id in stateless. We configure the driver registers so that only one of them applies. +// So, the rule will apply if packet has either the correct ttl or IP ID, depending if we are in statfull or stateless. +void CTRexExtendedDriverBase40G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl, uint16_t ip_id, int queue, uint16_t stat_idx) { int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR); + static int filter_soft_id = 0; - if ( ret !=0 ){ + if ( ret != 0 ){ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_supported " "err=%d, port=%u \n", ret, port_id); @@ -4673,21 +4792,29 @@ void CTRexExtendedDriverBase40G::add_rules(CPhyEthIF * _if, memset(&filter,0,sizeof(struct rte_eth_fdir_filter)); - filter.action.rx_queue =1; +#if 0 + printf("40g::%s rules: port:%d, type:%d ttl:%d, ip_id:%x, q:%d hw index:%d\n", (op == RTE_ETH_FILTER_ADD) ? "add" : "del" + , port_id, type, ttl, ip_id, queue, stat_idx); +#endif + + filter.action.rx_queue = queue; filter.action.behavior =RTE_ETH_FDIR_ACCEPT; filter.action.report_status =RTE_ETH_FDIR_NO_REPORT_STATUS; - filter.soft_id=0; - + filter.action.stat_count_index = stat_idx; + filter.soft_id = filter_soft_id++; filter.input.flow_type = type; + switch (type) { case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER: filter.input.flow.ip4_flow.ttl=ttl; + filter.input.flow.ip4_flow.ip_id = ip_id; filter.input.flow.ip4_flow.l4_protocol = IPPROTO_ICMP; // In this case we want filter for icmp packets break; case RTE_ETH_FLOW_NONFRAG_IPV4_UDP: case RTE_ETH_FLOW_NONFRAG_IPV4_TCP: case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP: filter.input.flow.ip4_flow.ttl=ttl; + filter.input.flow.ip4_flow.ip_id = ip_id; break; case RTE_ETH_FLOW_NONFRAG_IPV6_UDP: case RTE_ETH_FLOW_NONFRAG_IPV6_TCP: @@ -4699,9 +4826,8 @@ void CTRexExtendedDriverBase40G::add_rules(CPhyEthIF * _if, break; } - /* We want to place latency packets in queue 1 */ ret=rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, - RTE_ETH_FILTER_ADD, (void*)&filter); + op, (void*)&filter); if ( ret !=0 ){ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_ctrl" @@ -4710,25 +4836,107 @@ void CTRexExtendedDriverBase40G::add_rules(CPhyEthIF * _if, } } +// type - rule type. Currently we only support rules in IP ID. +// proto - Packet protocol: UDP or TCP +// id - Counter id in HW. We assume it is in the range 0..TREX_FDIR_STAT_SIZE +int CTRexExtendedDriverBase40G::add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint8_t type, uint16_t proto, uint16_t id) { + uint32_t rule_id = (port_id % m_if_per_card) * TREX_FDIR_STAT_SIZE + id; + uint16_t rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; + + switch(proto) { + case IPPROTO_TCP: + rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_TCP; + break; + case IPPROTO_UDP: + rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_UDP; + break; + default: + rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; + break; + } + add_del_rules(op, port_id, rte_type, 0, IP_ID_RESERVE_BASE + id, MAIN_DPDK_DATA_Q, rule_id); + return 0; +} -int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if){ +int CTRexExtendedDriverBase40G::configure_rx_filter_rules_statfull(CPhyEthIF * _if) { + uint32_t port_id = _if->get_port_id(); uint16_t hops = get_rx_check_hops(); int i; - for (i=0; i<2; i++) { - uint8_t ttl=0xff-i-hops; - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_UDP, ttl); - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_TCP, ttl); - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV6_UDP, ttl); - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV6_TCP, ttl); + + for (i = 0; i < 2; i++) { + uint8_t ttl = TTL_RESERVE_DUPLICATE - i - hops; + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, ttl, 0, MAIN_DPDK_RX_Q, 0); + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, ttl, 0, MAIN_DPDK_RX_Q, 0); + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, ttl, 0, MAIN_DPDK_RX_Q, 0); + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, ttl, 0, MAIN_DPDK_RX_Q, 0); } - /* Configure queue for latency packets */ - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 255); - add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, 255); + /* Configure rules for latency measurement packets */ + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, TTL_RESERVE_DUPLICATE - hops, 0, MAIN_DPDK_RX_Q, 0); + add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, TTL_RESERVE_DUPLICATE - hops, 0, MAIN_DPDK_RX_Q, 0); - return (0); + return 0; +} + +int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) { + if (get_is_stateless()) { + return 0; // Rules are configured dynamically in stateless + } else { + return configure_rx_filter_rules_statfull(_if); + } +} + +// instead of adding this to rte_ethdev.h +extern "C" int rte_eth_fdir_stats_get(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len); +int CTRexExtendedDriverBase40G::get_rx_stats(CPhyEthIF * _if, uint32_t *stats, uint32_t *prev_stats, int index) { + uint32_t hw_stats[TREX_FDIR_STAT_SIZE]; + uint32_t port_id = _if->get_port_id(); + uint32_t len, start, loop_start; + + if (index >= 0) { + len = 1; + start = (port_id % m_if_per_card) * TREX_FDIR_STAT_SIZE + index; + loop_start = index; + } else { + start = (port_id % m_if_per_card) * TREX_FDIR_STAT_SIZE; + len = TREX_FDIR_STAT_SIZE; + loop_start = 0; + } + + rte_eth_fdir_stats_get(port_id, hw_stats, start, len); + for (int i = loop_start; i < loop_start + len; i++) { + if (hw_stats[i] >= prev_stats[i]) { + stats[i] = (uint64_t)(hw_stats[i] - prev_stats[i]); + } else { + // Wrap around + stats[i] = (uint64_t)((hw_stats[i] + ((uint64_t)1 << 32)) - prev_stats[i]); + } + prev_stats[i] = hw_stats[i]; + } + + return 0; } +// if fd != NULL, dump fdir stats of _if +// return num of filters +int CTRexExtendedDriverBase40G::dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) +{ + uint32_t port_id = _if->get_port_id(); + struct rte_eth_fdir_stats stat; + int ret; + + ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_STATS, (void*)&stat); + if (ret == 0) { + if (fd) + fprintf(fd, "Num filters on guarant poll:%d, best effort poll:%d\n", stat.guarant_cnt, stat.best_cnt); + return (stat.guarant_cnt + stat.best_cnt); + } else { + if (fd) + fprintf(fd, "Failed reading fdir statistics\n"); + return -1; + } +} + void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ struct rte_eth_stats stats1; @@ -4872,12 +5080,15 @@ struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m){ return(r); } - /*********************************************************** * platfrom API object * TODO: REMOVE THIS TO A SEPERATE FILE * **********************************************************/ +void TrexDpdkPlatformApi::get_port_num(uint8_t &port_num) const { + port_num = g_trex.m_max_ports; +} + void TrexDpdkPlatformApi::get_global_stats(TrexPlatformGlobalStats &stats) const { CGlobalStats trex_stats; @@ -4927,10 +5138,12 @@ TrexDpdkPlatformApi::port_id_to_cores(uint8_t port_id, std::vector<std::pair<uin void TrexDpdkPlatformApi::get_interface_info(uint8_t port_id, std::string &driver_name, - driver_speed_e &speed) const { + driver_speed_e &speed, + bool &has_crc) const { driver_name = CTRexExtendedDriverDb::Ins()->get_driver_name(); - speed = CTRexExtendedDriverDb::Ins()->get_drv()->get_driver_speed(port_id); + speed = CTRexExtendedDriverDb::Ins()->get_drv()->get_driver_speed(port_id); + has_crc = CTRexExtendedDriverDb::Ins()->get_drv()->has_crc_added(); } void @@ -4939,3 +5152,22 @@ TrexDpdkPlatformApi::publish_async_data_now(uint32_t key) const { g_trex.publish_async_barrier(key); } +void +TrexDpdkPlatformApi::get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const { + num_counters = CTRexExtendedDriverDb::Ins()->get_drv()->get_stat_counters_num(); + capabilities = CTRexExtendedDriverDb::Ins()->get_drv()->get_rx_stat_capabilities(); +} + +int TrexDpdkPlatformApi::get_rx_stats(uint8 port_id, uint64_t *stats, int index, bool reset) const { + return g_trex.m_ports[port_id].get_rx_stats(stats, index, reset); +} + +int TrexDpdkPlatformApi::add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const { + return CTRexExtendedDriverDb::Ins()->get_drv() + ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_ADD, type, proto, id); +} + +int TrexDpdkPlatformApi::del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const { + return CTRexExtendedDriverDb::Ins()->get_drv() + ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_DELETE, type, proto, id); +} diff --git a/src/main_dpdk.h b/src/main_dpdk.h index e2c0cdb2..7357c0f4 100644 --- a/src/main_dpdk.h +++ b/src/main_dpdk.h @@ -1,27 +1,33 @@ /* -Copyright (c) 2015-2016 Cisco Systems, Inc. + Copyright (c) 2015-2016 Cisco Systems, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #ifndef MAIN_DPDK_H #define MAIN_DPDK_H +#include <rte_ethdev.h> #include "bp_sim.h" +enum { + MAIN_DPDK_DATA_Q = 0, + MAIN_DPDK_RX_Q = 1, +}; + class CPhyEthIFStats { -public: + public: uint64_t ipackets; /**< Total number of successfully received packets. */ uint64_t ibytes; /**< Total number of successfully received bytes. */ uint64_t f_ipackets; /**< Total number of successfully received packets - filter SCTP*/ @@ -32,18 +38,22 @@ public: uint64_t oerrors; /**< Total number of failed transmitted packets. */ uint64_t imcasts; /**< Total number of multicast received packets. */ uint64_t rx_nombuf; /**< Total number of RX mbuf allocation failures. */ - -public: + uint64_t m_rx_per_flow [TREX_FDIR_STAT_SIZE]; // Per flow RX statistics + // Previous fdir stats values read from HW. Since on xl710 this is 32 bit, we save old value, to handle wrap around. + uint32_t m_fdir_prev_stats [TREX_FDIR_STAT_SIZE]; + bool m_fdir_stats_first_time; + public: void Clear(); void Dump(FILE *fd); void DumpAll(FILE *fd); }; class CPhyEthIF { -public: + public: CPhyEthIF (){ m_port_id=0; m_rx_queue=0; + m_stats.m_fdir_stats_first_time = true; } bool Create(uint8_t portid){ m_port_id = portid; @@ -59,18 +69,20 @@ public: } void configure(uint16_t nb_rx_queue, - uint16_t nb_tx_queue, - const struct rte_eth_conf *eth_conf); + uint16_t nb_tx_queue, + const struct rte_eth_conf *eth_conf); void macaddr_get(struct ether_addr *mac_addr); void get_stats(CPhyEthIFStats *stats); + int dump_fdir_global_stats(FILE *fd); + int get_rx_stats(uint64_t *stats, int index, bool reset); void get_stats_1g(CPhyEthIFStats *stats); void rx_queue_setup(uint16_t rx_queue_id, - uint16_t nb_rx_desc, + uint16_t nb_rx_desc, unsigned int socket_id, const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool); void tx_queue_setup(uint16_t tx_queue_id, - uint16_t nb_tx_desc, + uint16_t nb_tx_desc, unsigned int socket_id, const struct rte_eth_txconf *tx_conf); void configure_rx_drop_queue(); @@ -90,7 +102,7 @@ public: void update_counters(); void stats_clear(); uint8_t get_port_id(){ - return (m_port_id); + return (m_port_id); } float get_last_tx_rate(){ return (m_last_tx_rate); @@ -105,11 +117,11 @@ public: return (m_last_rx_pps); } CPhyEthIFStats & get_stats(){ - return ( m_stats ); + return ( m_stats ); } void flush_rx_queue(void); - -public: + int add_rx_flow_stat_rule(uint8_t type, uint16_t proto, uint16_t id); + int del_rx_flow_stat_rule(uint8_t type, uint16_t proto, uint16_t id); inline uint16_t tx_burst(uint16_t queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts) { return rte_eth_tx_burst(m_port_id, queue_id, tx_pkts, nb_pkts); } @@ -117,26 +129,27 @@ public: return rte_eth_rx_burst(m_port_id, queue_id, rx_pkts, nb_pkts); } inline uint32_t pci_reg_read(uint32_t reg_off) { - void *reg_addr; - uint32_t reg_v; - reg_addr = (void *)((char *)m_dev_info.pci_dev->mem_resource[0].addr + - reg_off); + void *reg_addr; + uint32_t reg_v; + reg_addr = (void *)((char *)m_dev_info.pci_dev->mem_resource[0].addr + + reg_off); reg_v = *((volatile uint32_t *)reg_addr); return rte_le_to_cpu_32(reg_v); } - inline void pci_reg_write(uint32_t reg_off, + inline void pci_reg_write(uint32_t reg_off, uint32_t reg_v) { - void *reg_addr; - - reg_addr = (void *)((char *)m_dev_info.pci_dev->mem_resource[0].addr + - reg_off); - *((volatile uint32_t *)reg_addr) = rte_cpu_to_le_32(reg_v); + void *reg_addr; + + reg_addr = (void *)((char *)m_dev_info.pci_dev->mem_resource[0].addr + + reg_off); + *((volatile uint32_t *)reg_addr) = rte_cpu_to_le_32(reg_v); } void dump_stats_extended(FILE *fd); uint8_t get_rte_port_id(void) { - return m_port_id; + return m_port_id; } -private: + int get_rx_stat_capabilities(); + private: uint8_t m_port_id; uint8_t m_rx_queue; struct rte_eth_link m_link; @@ -151,8 +164,14 @@ private: float m_last_rx_rate; float m_last_tx_pps; float m_last_rx_pps; -public: - struct rte_eth_dev_info m_dev_info; + public: + struct rte_eth_dev_info m_dev_info; +}; + +// Because it is difficult to move CGlobalTRex into this h file, defining interface class to it +class CGlobalTRexInterface { + public: + CPhyEthIF *get_ports(uint8_t &port_num); }; #endif diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 93a680c9..d2cb4ce6 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -103,11 +103,11 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { /* parse RX info */ const Json::Value &rx = parse_object(section, "rx_stats", result); - stream->m_rx_check.m_enable = parse_bool(rx, "enabled", result); + stream->m_rx_check.m_enabled = parse_bool(rx, "enabled", result); /* if it is enabled - we need more fields */ - if (stream->m_rx_check.m_enable) { - stream->m_rx_check.m_stream_id = parse_int(rx, "stream_id", result); + if (stream->m_rx_check.m_enabled) { + stream->m_rx_check.m_user_id = parse_int(rx, "stream_id", result); stream->m_rx_check.m_seq_enabled = parse_bool(rx, "seq_enabled", result); stream->m_rx_check.m_latency = parse_bool(rx, "latency_enabled", result); } @@ -590,8 +590,6 @@ TrexRpcCmdGetAllStreams::_run(const Json::Value ¶ms, Json::Value &result) { uint8_t port_id = parse_port(params, result); TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id); - bool get_pkt = parse_bool(params, "get_pkt", result); - std::vector <TrexStream *> streams; port->get_object_list(streams); @@ -600,11 +598,6 @@ TrexRpcCmdGetAllStreams::_run(const Json::Value ¶ms, Json::Value &result) { Json::Value j = stream->get_stream_json(); - /* should we include the packet as well ? */ - if (!get_pkt) { - j.removeMember("packet"); - } - std::stringstream ss; ss << stream->m_stream_id; diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index d90d880e..9545e585 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -107,7 +107,7 @@ void parse_vm_instr_write_mask_flow_var(const Json::Value &inst, std::unique_ptr TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStreamList, "get_stream_list", 1, false); -TREX_RPC_CMD_DEFINE(TrexRpcCmdGetAllStreams, "get_all_streams", 2, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetAllStreams, "get_all_streams", 1, false); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStream, "get_stream", 3, false); diff --git a/src/sim/trex_sim.h b/src/sim/trex_sim.h index 21498978..6e842eb7 100644 --- a/src/sim/trex_sim.h +++ b/src/sim/trex_sim.h @@ -54,13 +54,18 @@ public: virtual void get_global_stats(TrexPlatformGlobalStats &stats) const { } - virtual void get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const { + virtual void get_interface_info(uint8_t interface_id, + std::string &driver_name, + driver_speed_e &speed, + bool &has_crc) const { driver_name = "TEST"; speed = TrexPlatformApi::SPEED_10G; + has_crc = true; } virtual void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const { } + virtual void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const {num_counters=128; capabilities=0; } virtual void port_id_to_cores(uint8_t port_id, std::vector<std::pair<uint8_t, uint8_t>> &cores_id_list) const { for (int i = 0; i < m_dp_core_count; i++) { @@ -71,6 +76,10 @@ public: virtual void publish_async_data_now(uint32_t key) const { } + virtual int get_rx_stats(uint8_t port_id, uint64_t *stats, int index, bool reset) const {return 0;} + virtual void get_port_num(uint8_t &port_num) const {port_num = 2;}; + virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;} + virtual int del_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;} private: int m_dp_core_count; diff --git a/src/sim/trex_sim_stateless.cpp b/src/sim/trex_sim_stateless.cpp index a8316034..30d60b15 100644 --- a/src/sim/trex_sim_stateless.cpp +++ b/src/sim/trex_sim_stateless.cpp @@ -65,7 +65,6 @@ public: } }; - /** * handler for DP to CP messages * diff --git a/src/stateless/cp/trex_stateless.h b/src/stateless/cp/trex_stateless.h index 59be9241..cc47da6b 100644 --- a/src/stateless/cp/trex_stateless.h +++ b/src/stateless/cp/trex_stateless.h @@ -32,6 +32,7 @@ limitations under the License. #include <trex_rpc_server_api.h> #include <publisher/trex_publisher.h> +#include <flow_stat.h> #include <internal_api/trex_platform_api.h> /** @@ -165,6 +166,8 @@ public: return m_rpc_server; } + CFlowStatRuleMgr m_rx_flow_stat; + protected: /* no copy or assignment */ diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index 99b6565c..88c38112 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -59,7 +59,7 @@ TrexStatelessPort::TrexStatelessPort(uint8_t port_id, const TrexPlatformApi *api m_port_state = PORT_STATE_IDLE; /* get the platform specific data */ - api->get_interface_info(port_id, m_driver_name, m_speed); + api->get_interface_info(port_id, m_driver_name, m_speed, m_has_crc); /* get the DP cores belonging to this port */ api->port_id_to_cores(m_port_id, core_pair_list); @@ -179,7 +179,6 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, m_last_duration = duration; change_state(PORT_STATE_TX); - /* update the DP - messages will be freed by the DP */ int index = 0; for (auto core_id : m_cores_id_list) { @@ -231,12 +230,32 @@ TrexStatelessPort::stop_traffic(void) { send_message_to_all_dp(stop_msg); + /* continue to general actions */ + common_port_stop_actions(false); + +} + +/** + * when a port stops, perform various actions + * + */ +void +TrexStatelessPort::common_port_stop_actions(bool event_triggered) { + change_state(PORT_STATE_STREAMS); Json::Value data; data["port_id"] = m_port_id; - get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STOPPED, data); + if (event_triggered) { + get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_STOPPED, data); + } else { + get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_FINISHED_TX, data); + } + + for (auto entry : m_stream_table) { + get_stateless_obj()->m_rx_flow_stat.stop_stream(entry.second); + } } void @@ -428,12 +447,7 @@ TrexStatelessPort::on_dp_event_occured(TrexDpPortEvent::event_e event_type) { switch (event_type) { case TrexDpPortEvent::EVENT_STOP: - /* set a stop event */ - change_state(PORT_STATE_STREAMS); - /* send a ZMQ event */ - - data["port_id"] = m_port_id; - get_stateless_obj()->get_publisher()->publish_event(TrexPublisher::EVENT_PORT_FINISHED_TX, data); + common_port_stop_actions(true); break; default: @@ -651,6 +665,47 @@ TrexStatelessPort::get_port_effective_rate(double &pps, } +void +TrexStatelessPort::add_stream(TrexStream *stream) { + + verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS); + + m_stream_table.add_stream(stream); + delete_streams_graph(); + + get_stateless_obj()->m_rx_flow_stat.add_stream(stream); + + change_state(PORT_STATE_STREAMS); +} + +void +TrexStatelessPort::remove_stream(TrexStream *stream) { + + verify_state(PORT_STATE_STREAMS); + + get_stateless_obj()->m_rx_flow_stat.del_stream(stream); + + m_stream_table.remove_stream(stream); + delete_streams_graph(); + + if (m_stream_table.size() == 0) { + change_state(PORT_STATE_IDLE); + } +} + +void +TrexStatelessPort::remove_and_delete_all_streams() { + verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS); + + vector<TrexStream *> streams; + get_object_list(streams); + + for (auto stream : streams) { + remove_stream(stream); + delete stream; + } +} + /************* Trex Port Owner **************/ TrexPortOwner::TrexPortOwner() { diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h index 49e69757..434181c4 100644 --- a/src/stateless/cp/trex_stateless_port.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -236,34 +236,9 @@ public: * */ - void add_stream(TrexStream *stream) { - verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS); - - m_stream_table.add_stream(stream); - delete_streams_graph(); - - change_state(PORT_STATE_STREAMS); - } - - void remove_stream(TrexStream *stream) { - verify_state(PORT_STATE_STREAMS); - - m_stream_table.remove_stream(stream); - delete_streams_graph(); - - if (m_stream_table.size() == 0) { - change_state(PORT_STATE_IDLE); - } - } - - void remove_and_delete_all_streams() { - verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS); - - m_stream_table.remove_and_delete_all_streams(); - delete_streams_graph(); - - change_state(PORT_STATE_IDLE); - } + void add_stream(TrexStream *stream); + void remove_stream(TrexStream *stream); + void remove_and_delete_all_streams(); TrexStream * get_stream_by_id(uint32_t stream_id) { return m_stream_table.get_stream_by_id(stream_id); @@ -308,6 +283,18 @@ public: */ uint64_t get_port_speed_bps() const; + /** + * return true if port adds CRC to a packet (not occurs for + * VNICs) + * + * @author imarom (24-Feb-16) + * + * @return bool + */ + bool has_crc_added() const { + return m_has_crc; + } + TrexPortOwner & get_owner() { return m_owner; } @@ -357,6 +344,12 @@ private: /** + * when a port stops, perform various actions + * + */ + void common_port_stop_actions(bool event_triggered); + + /** * calculate effective M per core * */ @@ -382,6 +375,7 @@ private: uint8_t m_port_id; port_state_e m_port_state; std::string m_driver_name; + bool m_has_crc; TrexPlatformApi::driver_speed_e m_speed; diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index f1c93a11..9c7898a8 100644 --- a/src/stateless/cp/trex_stream.cpp +++ b/src/stateless/cp/trex_stream.cpp @@ -130,7 +130,7 @@ TrexStream::TrexStream(uint8_t type, m_pkt.len = 0; m_expected_pkt_len = 0; - m_rx_check.m_enable = false; + m_rx_check.m_enabled = false; m_burst_total_pkts=0; @@ -191,15 +191,6 @@ void TrexStreamTable::remove_stream(TrexStream *stream) { } -void TrexStreamTable::remove_and_delete_all_streams() { - - for (auto stream : m_stream_table) { - delete stream.second; - } - - m_stream_table.clear(); -} - TrexStream * TrexStreamTable::get_stream_by_id(uint32_t stream_id) { auto search = m_stream_table.find(stream_id); @@ -254,5 +245,13 @@ TrexStreamRate::get_line_speed_bps() { double TrexStreamRate::get_pkt_size() { - return m_stream.get_pkt_size(); + TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(m_stream.m_port_id); + + double pkt_size = m_stream.get_pkt_size(); + + if (port->has_crc_added()) { + pkt_size += 4; + } + + return pkt_size; } diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index cc05c198..36f9d407 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -244,29 +244,29 @@ private: double get_pkt_size(); void calculate_from_pps() { - m_bps_L1 = m_pps * (get_pkt_size() + 24) * 8; - m_bps_L2 = m_pps * (get_pkt_size() + 4) * 8; + m_bps_L1 = m_pps * (get_pkt_size() + 20) * 8; + m_bps_L2 = m_pps * get_pkt_size() * 8; m_percentage = (m_bps_L1 / get_line_speed_bps()) * 100.0; } void calculate_from_bps_L1() { - m_bps_L2 = m_bps_L1 * ( (get_pkt_size() + 4.0) / (get_pkt_size() + 24.0) ); - m_pps = m_bps_L2 / (8 * (get_pkt_size() + 4)); + m_bps_L2 = m_bps_L1 * ( get_pkt_size() / (get_pkt_size() + 20.0) ); + m_pps = m_bps_L2 / (8 * get_pkt_size()); m_percentage = (m_bps_L1 / get_line_speed_bps()) * 100.0; } void calculate_from_bps_L2() { - m_bps_L1 = m_bps_L2 * ( (get_pkt_size() + 24.0) / (get_pkt_size() + 4.0)); - m_pps = m_bps_L2 / (8 * (get_pkt_size() + 4)); + m_bps_L1 = m_bps_L2 * ( (get_pkt_size() + 20.0) / get_pkt_size()); + m_pps = m_bps_L2 / (8 * get_pkt_size()); m_percentage = (m_bps_L1 / get_line_speed_bps()) * 100.0; } void calculate_from_percentage() { m_bps_L1 = (m_percentage / 100.0) * get_line_speed_bps(); - m_bps_L2 = m_bps_L1 * ( (get_pkt_size() + 4.0) / (get_pkt_size() + 24.0) ); - m_pps = m_bps_L2 / (8 * (get_pkt_size() + 4)); + m_bps_L2 = m_bps_L1 * ( get_pkt_size() / (get_pkt_size() + 20.0) ); + m_pps = m_bps_L2 / (8 * get_pkt_size()); } @@ -394,11 +394,19 @@ public: } /* create new stream */ - TrexStream * clone() const { + TrexStream * clone(bool full = false) const { /* not all fields will be cloned */ TrexStream *dp = new TrexStream(m_type,m_port_id,m_stream_id); + + /* on full clone we copy also VM */ + if (full) { + m_vm.clone(dp->m_vm); + + } + + /* copy VM DP product */ if (m_vm_dp) { dp->m_vm_dp = m_vm_dp->clone(); } else { @@ -500,10 +508,10 @@ public: /* RX check */ struct { - bool m_enable; + bool m_enabled; bool m_seq_enabled; bool m_latency; - uint32_t m_stream_id; + uint32_t m_user_id; } m_rx_check; @@ -559,12 +567,7 @@ public: */ void remove_stream(TrexStream *stream); - /** - * remove all streams on the table - * memory will be deleted - */ - void remove_and_delete_all_streams(); - + /** * fetch a stream if exists * o.w NULL diff --git a/src/stateless/cp/trex_stream_vm.cpp b/src/stateless/cp/trex_stream_vm.cpp index d8395ba4..7a1dc122 100644 --- a/src/stateless/cp/trex_stream_vm.cpp +++ b/src/stateless/cp/trex_stream_vm.cpp @@ -887,14 +887,14 @@ StreamVm::set_split_instruction(StreamVmInstructionVar *instr) { } /** - * copy instructions from this VM to 'other' + * clone VM from this VM to 'other' * * @author imarom (22-Dec-15) * * @param other */ void -StreamVm::copy_instructions(StreamVm &other) const { +StreamVm::clone(StreamVm &other) const { /* clear previous if any exists */ for (auto instr : other.m_inst_list) { delete instr; @@ -904,6 +904,7 @@ StreamVm::copy_instructions(StreamVm &other) const { for (auto instr : m_inst_list) { StreamVmInstruction *new_instr = instr->clone(); + other.m_inst_list.push_back(new_instr); /* for the split instruction - find the right one */ @@ -914,6 +915,7 @@ StreamVm::copy_instructions(StreamVm &other) const { } } + other.m_is_random_var = m_is_random_var; } /** @@ -974,7 +976,7 @@ StreamVm::calc_expected_pkt_size(uint16_t regular_pkt_size) const { StreamVm dummy; - this->copy_instructions(dummy); + this->clone(dummy); dummy.compile(regular_pkt_size); assert(dummy.m_expected_pkt_size != 0); diff --git a/src/stateless/cp/trex_stream_vm.h b/src/stateless/cp/trex_stream_vm.h index f5d58edd..c1db090d 100644 --- a/src/stateless/cp/trex_stream_vm.h +++ b/src/stateless/cp/trex_stream_vm.h @@ -1456,7 +1456,7 @@ public: * clone VM instructions * */ - void copy_instructions(StreamVm &other) const; + void clone(StreamVm &other) const; bool is_vm_empty() const { diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index aca74498..7c91754c 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -475,16 +475,18 @@ TrexStreamsCompiler::compile_stream(TrexStream *stream, new_next_id = nodes.get(stream->m_next_stream_id)->m_compressed_stream_id; } + TrexStream *fixed_rx_flow_stat_stream = stream->clone(true); + get_stateless_obj()->m_rx_flow_stat.start_stream(fixed_rx_flow_stat_stream); /* can this stream be split to many cores ? */ if (!stream->is_splitable(dp_core_count)) { - compile_stream_on_single_core(stream, + compile_stream_on_single_core(fixed_rx_flow_stat_stream, factor, objs[0], new_id, new_next_id); } else { - compile_stream_on_all_cores(stream, + compile_stream_on_all_cores(fixed_rx_flow_stat_stream, factor, dp_core_count, objs, @@ -492,7 +494,7 @@ TrexStreamsCompiler::compile_stream(TrexStream *stream, new_next_id); } - + delete fixed_rx_flow_stat_stream; } /** diff --git a/src/stateless/cp/trex_vm_splitter.cpp b/src/stateless/cp/trex_vm_splitter.cpp index 5e6d4fbd..963b4525 100644 --- a/src/stateless/cp/trex_vm_splitter.cpp +++ b/src/stateless/cp/trex_vm_splitter.cpp @@ -190,7 +190,7 @@ void TrexVmSplitter::duplicate_vm() { /* for each core - duplicate the instructions */ for (TrexStream *core_stream : *m_core_streams) { - m_stream->m_vm.copy_instructions(core_stream->m_vm); + m_stream->m_vm.clone(core_stream->m_vm); } } diff --git a/src/trex_defs.h b/src/trex_defs.h new file mode 100644 index 00000000..bb8510fa --- /dev/null +++ b/src/trex_defs.h @@ -0,0 +1,31 @@ +/* +Copyright (c) 2015-2016 Cisco Systems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef __TREX_DEFS_H__ +#define __TREX_DEFS_H__ + +#define TREX_MAX_PORTS 12 +#define TREX_FDIR_STAT_SIZE 128 + +#ifndef UINT8_MAX + #define UINT8_MAX 255 +#endif + +#ifndef UINT16_MAX + #define UINT16_MAX 0xFFFF +#endif + + +#endif |