Rx stat per flow. Low level working for xl710, and partly for i350.
authorIdo Barnea <[email protected]>
Mon, 8 Feb 2016 09:25:07 +0000 (11:25 +0200)
committerIdo Barnea <[email protected]>
Wed, 24 Feb 2016 12:21:29 +0000 (14:21 +0200)
added full clone (with CP VM) to stream

31 files changed:
linux/ws_main.py
linux_dpdk/ws_main.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
scripts/stl/rx_stats.py [new file with mode: 0644]
src/bp_sim.h
src/common/Network/Packet/IPHeader.h
src/common/Network/Packet/IPHeader.inl
src/debug.cpp
src/debug.h
src/dpdk22/drivers/net/i40e/i40e_ethdev.c
src/dpdk22/drivers/net/i40e/i40e_fdir.c
src/dpdk22/lib/librte_ether/rte_eth_ctrl.h
src/dpdk22/lib/librte_ether/rte_ethdev.c
src/flow_stat.cpp [new file with mode: 0644]
src/flow_stat.h [new file with mode: 0644]
src/flow_stat_parser.cpp [new file with mode: 0644]
src/flow_stat_parser.h [new file with mode: 0644]
src/gtest/trex_stateless_gtest.cpp
src/internal_api/trex_platform_api.h
src/main_dpdk.cpp [changed mode: 0755->0644]
src/main_dpdk.h
src/rpc-server/commands/trex_rpc_cmd_stream.cpp
src/sim/trex_sim.h
src/sim/trex_sim_stateless.cpp
src/stateless/cp/trex_stateless.h
src/stateless/cp/trex_stateless_port.cpp
src/stateless/cp/trex_stateless_port.h
src/stateless/cp/trex_stream.cpp
src/stateless/cp/trex_stream.h
src/stateless/cp/trex_streams_compiler.cpp
src/trex_defs.h [new file with mode: 0644]

index ce7140b..9e4ac4d 100755 (executable)
@@ -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',
index b533004..0d96cfa 100755 (executable)
@@ -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',
index 90dd965..adbb88a 100644 (file)
@@ -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 (file)
index 0000000..892fe1a
--- /dev/null
@@ -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()
+
+
+
index a51a520..c9550dc 100755 (executable)
@@ -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 {
index 5dfd03d..b9ef8a2 100755 (executable)
@@ -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);
 
index e7b87f0..26ea551 100755 (executable)
@@ -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;
index ed4900f..0ca3454 100644 (file)
   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>
 #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);
 }
index fe37c18..4fc23d9 100644 (file)
@@ -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);
index 0a1e9ef..510a98c 100644 (file)
@@ -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)
index 194f862..f644ef3 100644 (file)
@@ -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);
 
index dc26439..419ca90 100644 (file)
@@ -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;
 };
 
 /**
index 43ec026..383ad12 100644 (file)
@@ -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 (file)
index 0000000..298bcb5
--- /dev/null
@@ -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 (file)
index 0000000..444daab
--- /dev/null
@@ -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 (file)
index 0000000..52824f7
--- /dev/null
@@ -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 (file)
index 0000000..606a1be
--- /dev/null
@@ -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;
+};
index 576f7d6..e946f5d 100644 (file)
@@ -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();
+}
index 67288b1..831fd77 100644 (file)
@@ -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__ */
old mode 100755 (executable)
new mode 100644 (file)
index 608a05b..8cc94e7
@@ -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);
+}
index e2c0cdb..7357c0f 100644 (file)
@@ -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
index d08c79b..984c3f8 100644 (file)
@@ -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);
     }
index 48e038d..6e842eb 100644 (file)
@@ -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;
index a831603..30d60b1 100644 (file)
@@ -65,7 +65,6 @@ public:
     }
 };
 
-
 /**
  * handler for DP to CP messages
  * 
index 59be924..cc47da6 100644 (file)
@@ -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 */
index 6ac9357..88c3811 100644 (file)
@@ -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() {
index df52e75..434181c 100644 (file)
@@ -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);
@@ -368,6 +343,12 @@ private:
     void on_dp_event_occured(TrexDpPortEvent::event_e event_type);
 
 
+    /**
+     * when a port stops, perform various actions
+     * 
+     */
+    void common_port_stop_actions(bool event_triggered);
+
     /**
      * calculate effective M per core
      * 
index 7ea9089..9c7898a 100644 (file)
@@ -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);
 
index 088478b..af4d4a7 100644 (file)
@@ -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 
index aca7449..7c91754 100644 (file)
@@ -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 (file)
index 0000000..bb8510f
--- /dev/null
@@ -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