diff options
author | Ido Barnea <ibarnea@cisco.com> | 2016-02-08 11:25:07 +0200 |
---|---|---|
committer | Ido Barnea <ibarnea@cisco.com> | 2016-02-24 14:21:29 +0200 |
commit | f0ab9eba97221e491cf7b3dd846eb8c23d920ec2 (patch) | |
tree | caf0b2f4f9cc0874a53bb3af813d5bae8d644914 /src | |
parent | 252b8ab3f41a18af8561cece71cf07bc9872f39f (diff) |
Rx stat per flow. Low level working for xl710, and partly for i350.
added full clone (with CP VM) to stream
Diffstat (limited to 'src')
27 files changed, 1706 insertions, 278 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 67288b19..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, @@ -116,6 +122,11 @@ public: 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() {} }; @@ -139,7 +150,11 @@ public: 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; }; /** @@ -164,6 +179,12 @@ public: 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 608a05b3..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 @@ -140,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); } @@ -151,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;} }; @@ -181,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); } @@ -281,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) { @@ -304,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); } @@ -314,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 { @@ -339,7 +354,6 @@ public: bool flow_control_disable_supported(){return false;} virtual void update_configuration(port_cfg_t * cfg); - }; @@ -1177,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; } @@ -1289,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, @@ -1551,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 ){ @@ -1651,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){ @@ -2588,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); @@ -3539,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 @@ -3804,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; @@ -4244,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){ @@ -4288,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) { @@ -4378,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; @@ -4471,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){ @@ -4641,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; @@ -4664,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); @@ -4682,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: @@ -4708,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" @@ -4719,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; @@ -4881,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; @@ -4950,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 d08c79b5..984c3f8e 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); } diff --git a/src/sim/trex_sim.h b/src/sim/trex_sim.h index 48e038db..6e842eb7 100644 --- a/src/sim/trex_sim.h +++ b/src/sim/trex_sim.h @@ -65,6 +65,7 @@ public: 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++) { @@ -75,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 6ac93577..88c38112 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -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 df52e751..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); @@ -369,6 +344,12 @@ private: /** + * when a port stops, perform various actions + * + */ + void common_port_stop_actions(bool event_triggered); + + /** * calculate effective M per core * */ diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index 7ea90895..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); diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index 088478bb..af4d4a73 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -394,11 +394,18 @@ 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.copy_instructions(dp->m_vm); + } + + /* copy VM DP product */ if (m_vm_dp) { dp->m_vm_dp = m_vm_dp->clone(); } else { @@ -500,10 +507,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 +566,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_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/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 |