summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xlinux/ws_main.py6
-rwxr-xr-xlinux_dpdk/ws_main.py2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py19
-rw-r--r--scripts/stl/rx_stats.py19
-rwxr-xr-xsrc/bp_sim.h3
-rwxr-xr-xsrc/common/Network/Packet/IPHeader.h2
-rwxr-xr-xsrc/common/Network/Packet/IPHeader.inl6
-rw-r--r--src/debug.cpp285
-rw-r--r--src/debug.h2
-rw-r--r--src/dpdk22/drivers/net/i40e/i40e_ethdev.c34
-rw-r--r--src/dpdk22/drivers/net/i40e/i40e_fdir.c9
-rw-r--r--src/dpdk22/lib/librte_ether/rte_eth_ctrl.h6
-rw-r--r--src/dpdk22/lib/librte_ether/rte_ethdev.c17
-rw-r--r--src/flow_stat.cpp619
-rw-r--r--src/flow_stat.h139
-rw-r--r--src/flow_stat_parser.cpp137
-rw-r--r--src/flow_stat_parser.h37
-rw-r--r--src/gtest/trex_stateless_gtest.cpp16
-rw-r--r--src/internal_api/trex_platform_api.h25
-rw-r--r--[-rwxr-xr-x]src/main_dpdk.cpp363
-rw-r--r--src/main_dpdk.h91
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_stream.cpp6
-rw-r--r--src/sim/trex_sim.h5
-rw-r--r--src/sim/trex_sim_stateless.cpp1
-rw-r--r--src/stateless/cp/trex_stateless.h3
-rw-r--r--src/stateless/cp/trex_stateless_port.cpp71
-rw-r--r--src/stateless/cp/trex_stateless_port.h37
-rw-r--r--src/stateless/cp/trex_stream.cpp11
-rw-r--r--src/stateless/cp/trex_stream.h20
-rw-r--r--src/stateless/cp/trex_streams_compiler.cpp8
-rw-r--r--src/trex_defs.h31
31 files changed, 1747 insertions, 283 deletions
diff --git a/linux/ws_main.py b/linux/ws_main.py
index ce7140b9..9e4ac4d4 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -118,8 +118,10 @@ main_src = SrcGroup(dir='src',
'utl_cpuu.cpp',
'msg_manager.cpp',
'publisher/trex_publisher.cpp',
- 'latency.cpp',
-
+ 'latency.cpp',
+ 'flow_stat.cpp',
+ 'flow_stat_parser.cpp',
+
'pal/linux/pal_utl.cpp',
'pal/linux/mbuf.cpp',
'sim/trex_sim_stateless.cpp',
diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py
index b533004a..0d96cfae 100755
--- a/linux_dpdk/ws_main.py
+++ b/linux_dpdk/ws_main.py
@@ -99,6 +99,8 @@ main_src = SrcGroup(dir='src',
'global_io_mode.cpp',
'main_dpdk.cpp',
'debug.cpp',
+ 'flow_stat.cpp',
+ 'flow_stat_parser.cpp',
'bp_sim.cpp',
'latency.cpp',
'platform_cfg.cpp',
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
index 90dd9657..adbb88a6 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
@@ -121,7 +121,21 @@ STLStreamDstMAC_CFG_FILE=0
STLStreamDstMAC_PKT =1
STLStreamDstMAC_ARP =2
+# RX stats class
+class STLRxStats(object):
+ def __init__ (self, user_id):
+ self.fields = {}
+ self.fields['stream_id'] = user_id
+ self.fields['enabled'] = True
+ self.fields['seq_enabled'] = False
+ self.fields['latency_enabled'] = False
+ def to_json (self):
+ return dict(self.fields)
+
+ @staticmethod
+ def defaults ():
+ return {'enabled' : False}
class STLStream(object):
@@ -216,10 +230,9 @@ class STLStream(object):
self.packet_desc = None
if not rx_stats:
- self.fields['rx_stats'] = {}
- self.fields['rx_stats']['enabled'] = False
+ self.fields['rx_stats'] = STLRxStats.defaults()
else:
- self.fields['rx_stats'] = rx_stats
+ self.fields['rx_stats'] = rx_stats.to_json()
def __str__ (self):
diff --git a/scripts/stl/rx_stats.py b/scripts/stl/rx_stats.py
new file mode 100644
index 00000000..892fe1a0
--- /dev/null
+++ b/scripts/stl/rx_stats.py
@@ -0,0 +1,19 @@
+from trex_stl_lib.api import *
+
+# stream from pcap file. continues pps 10 in sec
+
+class STLS1(object):
+
+ def get_streams (self, direction = 0):
+ return [STLStream(packet = STLPktBuilder(pkt ="stl/yaml/udp_64B_no_crc.pcap"), # path relative to pwd
+ mode = STLTXCont(pps=10),
+ rx_stats = STLRxStats(user_id = 7))
+ ]
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
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=&reg[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 &params, 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