summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scripts/automation/regression/functional_tests/stl_basic_tests.py58
-rw-r--r--scripts/automation/regression/setups/kiwi02/benchmark.yaml5
-rw-r--r--scripts/automation/regression/setups/trex-dan/benchmark.yaml2
-rw-r--r--scripts/automation/regression/stateless_tests/stl_client_test.py26
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py141
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py9
-rw-r--r--scripts/exp/imix.pcapbin39396 -> 39396 bytes
-rw-r--r--scripts/exp/imix_3pkt.pcapbin8692 -> 8162 bytes
-rw-r--r--scripts/exp/imix_3pkt_vm.pcapbin6572 -> 4588 bytes
-rw-r--r--scripts/exp/pcap_with_vm.pcapbin2284 -> 2284 bytes
-rw-r--r--scripts/exp/udp_1pkt_range_clients_split.pcapbin7624 -> 7624 bytes
-rw-r--r--scripts/exp/udp_1pkt_simple_test.pcapbin1474 -> 1474 bytes
-rw-r--r--scripts/exp/udp_1pkt_simple_test2.pcapbin1474 -> 1474 bytes
-rw-r--r--scripts/exp/udp_1pkt_tuple_gen_split.pcapbin7624 -> 7624 bytes
-rw-r--r--scripts/stl/imix.py4
-rw-r--r--scripts/stl/pcap_with_vm.py2
-rw-r--r--scripts/stl/tests/many_streams.py50
-rw-r--r--scripts/stl/tests/multi_burst.py17
-rw-r--r--scripts/stl/tests/single_burst.py17
-rw-r--r--scripts/stl/tests/single_cont.py17
-rw-r--r--scripts/stl/udp_1pkt_simple_test.py2
-rw-r--r--scripts/stl/udp_1pkt_simple_test2.py2
-rwxr-xr-xsrc/bp_sim.cpp6
-rwxr-xr-xsrc/bp_sim.h7
-rw-r--r--src/flow_stat.cpp3
-rw-r--r--src/flow_stat_parser.cpp86
-rw-r--r--src/flow_stat_parser.h1
-rw-r--r--src/internal_api/trex_platform_api.h1
-rw-r--r--src/main_dpdk.cpp10
-rwxr-xr-xsrc/os_time.h5
-rw-r--r--src/sim/trex_sim.h6
-rw-r--r--src/sim/trex_sim_stateless.cpp59
-rw-r--r--src/stateless/cp/trex_exception.h64
-rw-r--r--src/stateless/cp/trex_stream.cpp14
-rw-r--r--src/stateless/cp/trex_stream.h45
-rw-r--r--src/stateless/cp/trex_streams_compiler.cpp151
-rw-r--r--src/stateless/cp/trex_streams_compiler.h22
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.cpp3
-rw-r--r--src/stateless/dp/trex_stream_node.h22
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.cpp1
40 files changed, 733 insertions, 125 deletions
diff --git a/scripts/automation/regression/functional_tests/stl_basic_tests.py b/scripts/automation/regression/functional_tests/stl_basic_tests.py
index 2bf97307..ecb7b465 100644
--- a/scripts/automation/regression/functional_tests/stl_basic_tests.py
+++ b/scripts/automation/regression/functional_tests/stl_basic_tests.py
@@ -9,8 +9,16 @@ from nose.plugins.attrib import attr
from trex import CTRexScenario
from trex_stl_lib import trex_stl_sim
from trex_stl_lib.trex_stl_streams import STLProfile
-from trex_stl_lib.trex_stl_packet_builder_scapy import RawPcapReader, RawPcapWriter
+from trex_stl_lib.trex_stl_packet_builder_scapy import RawPcapReader, RawPcapWriter, Ether
+from trex_stl_lib.utils.text_opts import *
+
import sys
+
+if sys.version_info > (3,0):
+ from io import StringIO
+else:
+ from cStringIO import StringIO
+
import os
import subprocess
import shlex
@@ -64,9 +72,18 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
raise Exception("cannot find '{0}'".format(name))
- def compare_caps (self, cap1, cap2, max_diff_sec = 0.01):
- pkts1 = list(RawPcapReader(cap1))
- pkts2 = list(RawPcapReader(cap2))
+ def scapy_pkt_show_to_str (self, scapy_pkt):
+ capture = StringIO()
+ save_stdout = sys.stdout
+ sys.stdout = capture
+ scapy_pkt.show()
+ sys.stdout = save_stdout
+ return capture.getvalue()
+
+
+ def compare_caps (self, output, golden, max_diff_sec = 0.01):
+ pkts1 = list(RawPcapReader(output))
+ pkts2 = list(RawPcapReader(golden))
assert_equal(len(pkts1), len(pkts2))
@@ -75,11 +92,29 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
ts2 = float(pkt2[1][0]) + (float(pkt2[1][1]) / 1e6)
if abs(ts1-ts2) > 0.000005: # 5 nsec
- raise AssertionError("TS error: cap files '{0}', '{1}' differ in cap #{2} - '{3}' vs. '{4}'".format(cap1, cap2, i, ts1, ts2))
+ raise AssertionError("TS error: cap files '{0}', '{1}' differ in cap #{2} - '{3}' vs. '{4}'".format(output, golden, i, ts1, ts2))
if pkt1[0] != pkt2[0]:
- raise AssertionError("RAW error: cap files '{0}', '{1}' differ in cap #{2}".format(cap1, cap2, i))
+ errmsg = "RAW error: output file '{0}', differs from golden '{1}' in cap #{2}".format(output, golden, i)
+ print(errmsg)
+
+ print(format_text("\ndifferent fields for packet #{0}:".format(i), 'underline'))
+ scapy_pkt1_info = self.scapy_pkt_show_to_str(Ether(pkt1[0])).split('\n')
+ scapy_pkt2_info = self.scapy_pkt_show_to_str(Ether(pkt2[0])).split('\n')
+
+ print(format_text("\nGot:\n", 'bold', 'underline'))
+ for line, ref in zip(scapy_pkt1_info, scapy_pkt2_info):
+ if line != ref:
+ print(format_text(line, 'bold'))
+
+ print(format_text("\nExpected:\n", 'bold', 'underline'))
+ for line, ref in zip(scapy_pkt2_info, scapy_pkt1_info):
+ if line != ref:
+ print(format_text(line, 'bold'))
+
+ print("\n")
+ raise AssertionError(errmsg)
def run_sim (self, yaml, output, options = "", silent = False, obj = None):
@@ -262,3 +297,14 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
+ def test_multicore_scheduling (self):
+ mc_tests = ['stl/tests/single_cont.py',
+ 'stl/tests/single_burst.py',
+ 'stl/tests/multi_burst.py',
+ 'stl/tests/many_streams.py',
+ ]
+
+ for mc_test in mc_tests:
+ rc = self.run_sim(mc_test, output = None, options = '--test_multi_core --limit=3840 -m 27kpps', silent = True)
+ assert_equal(rc, True)
+
diff --git a/scripts/automation/regression/setups/kiwi02/benchmark.yaml b/scripts/automation/regression/setups/kiwi02/benchmark.yaml
index 2d47f8f8..60febc8f 100644
--- a/scripts/automation/regression/setups/kiwi02/benchmark.yaml
+++ b/scripts/automation/regression/setups/kiwi02/benchmark.yaml
@@ -122,26 +122,31 @@ test_rx_check_sfr:
multiplier : 25
cores : 4
rx_sample_rate : 32
+ error_tolerance : 0.01
test_rx_check_http:
multiplier : 40000
cores : 2
rx_sample_rate : 32
+ error_tolerance : 0.01
test_rx_check_sfr_ipv6:
multiplier : 25
cores : 4
rx_sample_rate : 32
+ error_tolerance : 0.01
test_rx_check_http_ipv6:
multiplier : 40000
cores : 2
rx_sample_rate : 32
+ error_tolerance : 0.01
test_rx_check_http_negative:
multiplier : 40000
cores : 2
rx_sample_rate : 32
+ error_tolerance : 0.01
test_jumbo:
multiplier : 55
diff --git a/scripts/automation/regression/setups/trex-dan/benchmark.yaml b/scripts/automation/regression/setups/trex-dan/benchmark.yaml
index 4b47bd8e..a31d070c 100644
--- a/scripts/automation/regression/setups/trex-dan/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex-dan/benchmark.yaml
@@ -4,7 +4,7 @@
test_nbar_simple :
multiplier : 1.5
- cores : 1
+ cores : 2
exp_gbps : 0.5
cpu_to_core_ratio : 20800000
cpu2core_custom_dev: YES
diff --git a/scripts/automation/regression/stateless_tests/stl_client_test.py b/scripts/automation/regression/stateless_tests/stl_client_test.py
index 01a90250..3ef4713f 100644
--- a/scripts/automation/regression/stateless_tests/stl_client_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_client_test.py
@@ -240,22 +240,29 @@ class STLClient_Test(CStlGeneral_Test):
def test_all_profiles (self):
- # need promiscious for this one...
- if self.is_virt_nics or not self.is_loopback:
- self.skip('skipping profile tests for virtual NICs')
- return
try:
- self.c.set_port_attr(ports = [self.tx_port, self.rx_port], promiscuous = True)
-
+
for profile in self.profiles:
+
print("now testing profile {0}...\n").format(profile)
p1 = STLProfile.load(profile, port_id = self.tx_port)
p2 = STLProfile.load(profile, port_id = self.rx_port)
+ # if profile contains custom MAC addrs we need promiscuous mode
+ # but virtual NICs does not support promiscuous mode
+ self.c.set_port_attr(ports = [self.tx_port, self.rx_port], promiscuous = False)
+
+ if p1.has_custom_mac_addr():
+ if not self.is_virt_nics:
+ self.c.set_port_attr(ports = [self.tx_port, self.rx_port], promiscuous = True)
+ else:
+ print("\n*** profile needs promiscuous mode but running on virtual NICs - skipping... ***\n")
+ continue
+
if p1.has_flow_stats():
- print("profile needs RX caps - skipping...")
+ print("\n*** profile needs RX caps - skipping... ***\n")
continue
self.c.add_streams(p1, ports = self.tx_port)
@@ -280,9 +287,8 @@ class STLClient_Test(CStlGeneral_Test):
assert self.tx_port in stats, '{0} - no stats for TX port'.format(profile)
assert self.rx_port in stats, '{0} - no stats for RX port'.format(profile)
- assert stats[self.tx_port]['opackets'] == stats[self.rx_port]['ipackets'], '{0} - number of TX packets differ from RX packets'.format(profile)
-
- assert stats[self.rx_port]['opackets'] == stats[self.tx_port]['ipackets'], '{0} - number of TX packets differ from RX packets'.format(profile)
+ self.verify(stats[self.tx_port]['opackets'], stats[self.rx_port]['ipackets'])
+ self.verify(stats[self.rx_port]['opackets'], stats[self.tx_port]['ipackets'])
self.c.remove_all_streams(ports = [self.tx_port, self.rx_port])
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py
index 1d89a599..11e80b9a 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py
@@ -22,6 +22,7 @@ from .trex_stl_streams import *
from .utils import parsing_opts
from .trex_stl_client import STLClient
from .utils import pcap
+from trex_stl_lib.trex_stl_packet_builder_scapy import RawPcapReader, RawPcapWriter, hexdump
from yaml import YAMLError
@@ -291,13 +292,13 @@ class STLSim(object):
return
- print("Mering cores output to a single pcap file...\n")
+ if not self.silent:
+ print("Mering cores output to a single pcap file...\n")
inputs = ["{0}-{1}".format(self.outfile, index) for index in range(0, self.dp_core_count)]
pcap.merge_cap_files(inputs, self.outfile, delete_src = True)
-
def is_valid_file(filename):
if not os.path.isfile(filename):
raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename)
@@ -421,6 +422,11 @@ def setParserOptions():
action = "store_true",
default = False)
+ group.add_argument("--test_multi_core",
+ help = "runs the profile with c=1-8",
+ action = "store_true",
+ default = False)
+
return parser
@@ -435,6 +441,110 @@ def validate_args (parser, options):
parser.error("limit cannot be lower than number of DP cores")
+# a more flexible check
+def compare_caps (cap1, cap2, max_diff_sec = (5 * 1e-6)):
+ pkts1 = list(RawPcapReader(cap1))
+ pkts2 = list(RawPcapReader(cap2))
+
+ if len(pkts1) != len(pkts2):
+ print('{0} contains {1} packets vs. {2} contains {3} packets'.format(cap1, len(pkts1), cap2, len(pkts2)))
+ return False
+
+ # to be less strict we define equality if all packets from cap1 exists and in cap2
+ # and vice versa
+ # 'exists' means the same packet with abs(TS1-TS2) < 5nsec
+ # its O(n^2) but who cares, right ?
+ for i, pkt1 in enumerate(pkts1):
+ ts1 = float(pkt1[1][0]) + (float(pkt1[1][1]) / 1e6)
+ found = None
+ for j, pkt2 in enumerate(pkts2):
+ ts2 = float(pkt2[1][0]) + (float(pkt2[1][1]) / 1e6)
+
+ if abs(ts1-ts2) > max_diff_sec:
+ break
+
+ if pkt1[0] == pkt2[0]:
+ found = j
+ break
+
+
+ if found is None:
+ print(format_text("cannot find packet #{0} from {1} in {2}\n".format(i, cap1, cap2), 'bold'))
+ return False
+ else:
+ del pkts2[found]
+
+ return True
+
+
+
+
+# a more strict comparsion 1 <--> 1
+def compare_caps_strict (cap1, cap2, max_diff_sec = (5 * 1e-6)):
+ pkts1 = list(RawPcapReader(cap1))
+ pkts2 = list(RawPcapReader(cap2))
+
+ if len(pkts1) != len(pkts2):
+ print('{0} contains {1} packets vs. {1} contains {2} packets'.format(cap1, len(pkts1), cap2, len(pkts2)))
+ return False
+
+ # a strict check
+ for pkt1, pkt2, i in zip(pkts1, pkts2, range(1, len(pkts1))):
+ ts1 = float(pkt1[1][0]) + (float(pkt1[1][1]) / 1e6)
+ ts2 = float(pkt2[1][0]) + (float(pkt2[1][1]) / 1e6)
+
+ if abs(ts1-ts2) > 0.000005: # 5 nsec
+ print(format_text("TS error: cap files '{0}', '{1}' differ in cap #{2} - '{3}' vs. '{4}'\n".format(cap1, cap2, i, ts1, ts2), 'bold'))
+ return False
+
+ if pkt1[0] != pkt2[0]:
+ print(format_text("RAW error: cap files '{0}', '{1}' differ in cap #{2}\n".format(cap1, cap2, i), 'bold'))
+ print(hexdump(pkt1[0]))
+ print("")
+ print(hexdump(pkt2[0]))
+ print("")
+ return False
+
+ return True
+
+#
+def test_multi_core (r, options):
+
+ for core_count in [1, 2, 4, 6, 8]:
+ r.run(input_list = options.input_file,
+ outfile = '{0}.cap'.format(core_count),
+ dp_core_count = core_count,
+ is_debug = (not options.release),
+ pkt_limit = options.limit,
+ mult = options.mult,
+ duration = options.duration,
+ mode = 'none',
+ silent = True,
+ tunables = options.tunables)
+
+ print("")
+
+ print(format_text("comparing 2 cores to 1 core:\n", 'underline'))
+ rc = compare_caps('1.cap', '2.cap')
+ if rc:
+ print("[Passed]\n")
+
+ print(format_text("comparing 4 cores to 1 core:\n", 'underline'))
+ rc = compare_caps('1.cap', '4.cap')
+ if rc:
+ print("[Passed]\n")
+
+ print(format_text("comparing 6 cores to 1 core:\n", 'underline'))
+ rc = compare_caps('1.cap', '6.cap')
+ if rc:
+ print("[Passed]\n")
+
+ print(format_text("comparing 8 cores to 1 core:\n", 'underline'))
+ rc = compare_caps('1.cap', '8.cap')
+ if rc:
+ print("[Passed]\n")
+
+
def main (args = None):
parser = setParserOptions()
options = parser.parse_args(args = args)
@@ -455,23 +565,28 @@ def main (args = None):
mode = 'native'
elif options.pkt:
mode = 'pkt'
+ elif options.test_multi_core:
+ mode = 'test_multi_core'
else:
mode = 'none'
try:
r = STLSim(bp_sim_path = options.bp_sim_path, port_id = options.port_id)
- r.run(input_list = options.input_file,
- outfile = options.output_file,
- dp_core_count = options.dp_core_count,
- dp_core_index = options.dp_core_index,
- is_debug = (not options.release),
- pkt_limit = options.limit,
- mult = options.mult,
- duration = options.duration,
- mode = mode,
- silent = options.silent,
- tunables = options.tunables)
+ if mode == 'test_multi_core':
+ test_multi_core(r, options)
+ else:
+ r.run(input_list = options.input_file,
+ outfile = options.output_file,
+ dp_core_count = options.dp_core_count,
+ dp_core_index = options.dp_core_index,
+ is_debug = (not options.release),
+ pkt_limit = options.limit,
+ mult = options.mult,
+ duration = options.duration,
+ mode = mode,
+ silent = options.silent,
+ tunables = options.tunables)
except KeyboardInterrupt as e:
print("\n\n*** Caught Ctrl + C... Exiting...\n\n")
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
index 3ce876ad..165942d8 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
@@ -361,6 +361,8 @@ class STLStream(object):
int_mac_dst_override_mode = int(mac_dst_override_mode);
+ self.is_default_mac = not (int_mac_src_override_by_pkt or int_mac_dst_override_mode)
+
self.fields['flags'] = (int_mac_src_override_by_pkt&1) + ((int_mac_dst_override_mode&3)<<1)
self.fields['action_count'] = action_count
@@ -421,6 +423,10 @@ class STLStream(object):
return self.id
+ def has_custom_mac_addr (self):
+ """ Return True if src or dst MAC were set as custom """
+ return not self.is_default_mac
+
def get_name (self):
""" Get the stream name """
return self.name
@@ -835,6 +841,9 @@ class STLProfile(object):
def is_pauseable (self):
return all([x.get_mode() == "Continuous" for x in self.get_streams()])
+ def has_custom_mac_addr (self):
+ return any([x.has_custom_mac_addr() for x in self.get_streams()])
+
def has_flow_stats (self):
return any([x.has_flow_stats() for x in self.get_streams()])
diff --git a/scripts/exp/imix.pcap b/scripts/exp/imix.pcap
index aec8dac8..dff21f25 100644
--- a/scripts/exp/imix.pcap
+++ b/scripts/exp/imix.pcap
Binary files differ
diff --git a/scripts/exp/imix_3pkt.pcap b/scripts/exp/imix_3pkt.pcap
index 29e84d63..065a23fa 100644
--- a/scripts/exp/imix_3pkt.pcap
+++ b/scripts/exp/imix_3pkt.pcap
Binary files differ
diff --git a/scripts/exp/imix_3pkt_vm.pcap b/scripts/exp/imix_3pkt_vm.pcap
index 5af466d9..4a97280d 100644
--- a/scripts/exp/imix_3pkt_vm.pcap
+++ b/scripts/exp/imix_3pkt_vm.pcap
Binary files differ
diff --git a/scripts/exp/pcap_with_vm.pcap b/scripts/exp/pcap_with_vm.pcap
index b9476261..afc4bbef 100644
--- a/scripts/exp/pcap_with_vm.pcap
+++ b/scripts/exp/pcap_with_vm.pcap
Binary files differ
diff --git a/scripts/exp/udp_1pkt_range_clients_split.pcap b/scripts/exp/udp_1pkt_range_clients_split.pcap
index fc5572a8..fb5037cc 100644
--- a/scripts/exp/udp_1pkt_range_clients_split.pcap
+++ b/scripts/exp/udp_1pkt_range_clients_split.pcap
Binary files differ
diff --git a/scripts/exp/udp_1pkt_simple_test.pcap b/scripts/exp/udp_1pkt_simple_test.pcap
index 2eeec462..a1d3a2e0 100644
--- a/scripts/exp/udp_1pkt_simple_test.pcap
+++ b/scripts/exp/udp_1pkt_simple_test.pcap
Binary files differ
diff --git a/scripts/exp/udp_1pkt_simple_test2.pcap b/scripts/exp/udp_1pkt_simple_test2.pcap
index 002d77dc..2cf16a8f 100644
--- a/scripts/exp/udp_1pkt_simple_test2.pcap
+++ b/scripts/exp/udp_1pkt_simple_test2.pcap
Binary files differ
diff --git a/scripts/exp/udp_1pkt_tuple_gen_split.pcap b/scripts/exp/udp_1pkt_tuple_gen_split.pcap
index 08377c6d..873ab47f 100644
--- a/scripts/exp/udp_1pkt_tuple_gen_split.pcap
+++ b/scripts/exp/udp_1pkt_tuple_gen_split.pcap
Binary files differ
diff --git a/scripts/stl/imix.py b/scripts/stl/imix.py
index 65e35108..82edbfa5 100644
--- a/scripts/stl/imix.py
+++ b/scripts/stl/imix.py
@@ -8,8 +8,8 @@ class STLImix(object):
def __init__ (self):
# default IP range
- self.ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"},
- 'dst': {'start': "8.0.0.1", 'end': "8.0.0.254"}}
+ self.ip_range = {'src': {'start': "16.0.0.1", 'end': "16.0.0.254"},
+ 'dst': {'start': "48.0.0.1", 'end': "48.0.0.254"}}
# default IMIX properties
self.imix_table = [ {'size': 60, 'pps': 28, 'isg':0 },
diff --git a/scripts/stl/pcap_with_vm.py b/scripts/stl/pcap_with_vm.py
index 7cf2906b..4e85bdf4 100644
--- a/scripts/stl/pcap_with_vm.py
+++ b/scripts/stl/pcap_with_vm.py
@@ -34,7 +34,7 @@ class STLPcap(object):
ipg_usec = 10.0,
loop_count = 5,
ip_src_range = None,
- ip_dst_range = {'start' : '10.0.0.1', 'end': '10.0.0.254'},
+ ip_dst_range = {'start' : '16.0.0.1', 'end': '16.0.0.254'},
**kwargs):
vm = self.create_vm(ip_src_range, ip_dst_range)
diff --git a/scripts/stl/tests/many_streams.py b/scripts/stl/tests/many_streams.py
new file mode 100644
index 00000000..a8713a26
--- /dev/null
+++ b/scripts/stl/tests/many_streams.py
@@ -0,0 +1,50 @@
+from trex_stl_lib.api import *
+
+class STLS1(object):
+
+ def get_streams (self, direction = 0, **kwargs):
+ s1 = STLStream(name = 's1',
+ packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXSingleBurst(pps = 100, total_pkts = 7),
+ next = 's2'
+
+ )
+ s2 = STLStream(name = 's2',
+ self_start = False,
+ packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.2")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXSingleBurst(pps = 317, total_pkts = 13),
+ next = 's3'
+ )
+
+
+ s3 = STLStream(name = 's3',
+ self_start = False,
+ packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.3")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXMultiBurst(pps = 57, pkts_per_burst = 3, count = 5, ibg = 12),
+ next = 's4'
+ )
+
+ s4 = STLStream(name = 's4',
+ self_start = False,
+ packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.3")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXSingleBurst(pps = 4, total_pkts = 22),
+ next = 's5'
+ )
+
+ s5 = STLStream(name = 's5',
+ self_start = False,
+ packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.3")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXSingleBurst(pps = 17, total_pkts = 27),
+ action_count = 17,
+ next = 's1'
+ )
+
+ return [ s1, s2, s3, s4, s5 ]
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
diff --git a/scripts/stl/tests/multi_burst.py b/scripts/stl/tests/multi_burst.py
new file mode 100644
index 00000000..68a239f5
--- /dev/null
+++ b/scripts/stl/tests/multi_burst.py
@@ -0,0 +1,17 @@
+from trex_stl_lib.api import *
+
+class STLS1(object):
+
+ def get_streams (self, direction = 0, **kwargs):
+ s1 = STLStream(packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXMultiBurst(pkts_per_burst = 9, count = 2, ibg = 13))
+
+ return [s1]
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
diff --git a/scripts/stl/tests/single_burst.py b/scripts/stl/tests/single_burst.py
new file mode 100644
index 00000000..c46ebf87
--- /dev/null
+++ b/scripts/stl/tests/single_burst.py
@@ -0,0 +1,17 @@
+from trex_stl_lib.api import *
+
+class STLS1(object):
+
+ def get_streams (self, direction = 0, **kwargs):
+ s1 = STLStream(packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXSingleBurst(total_pkts = 27))
+
+ return [s1]
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
diff --git a/scripts/stl/tests/single_cont.py b/scripts/stl/tests/single_cont.py
new file mode 100644
index 00000000..19563105
--- /dev/null
+++ b/scripts/stl/tests/single_cont.py
@@ -0,0 +1,17 @@
+from trex_stl_lib.api import *
+
+class STLS1(object):
+
+ def get_streams (self, direction = 0, **kwargs):
+ s1 = STLStream(packet = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/(10*'x')),
+ mode = STLTXCont(pps = 2000))
+
+ return [s1]
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
diff --git a/scripts/stl/udp_1pkt_simple_test.py b/scripts/stl/udp_1pkt_simple_test.py
index 3915412d..5f08af9d 100644
--- a/scripts/stl/udp_1pkt_simple_test.py
+++ b/scripts/stl/udp_1pkt_simple_test.py
@@ -15,7 +15,7 @@ class STLS1(object):
base_pkt_a = Ether()/IP(dst="48.0.0.1",options=IPOption(b'\x01\x01\x01\x00'))/UDP(dport=12,sport=1025)
vm1 = STLScVmRaw([
- STLVmFlowVar(name="src",min_value="10.0.0.1",max_value="10.0.0.10",size=4,op="inc"),
+ STLVmFlowVar(name="src",min_value="16.0.0.1",max_value="16.0.0.10",size=4,op="inc"),
STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src"),
# checksum
STLVmFixIpv4(offset = "IP")
diff --git a/scripts/stl/udp_1pkt_simple_test2.py b/scripts/stl/udp_1pkt_simple_test2.py
index 617d98b3..190e5439 100644
--- a/scripts/stl/udp_1pkt_simple_test2.py
+++ b/scripts/stl/udp_1pkt_simple_test2.py
@@ -15,7 +15,7 @@ class STLS1(object):
base_pkt_a = Ether()/IP()/IPv6()/IP(dst="48.0.0.1",options=IPOption(b'\x01\x01\x01\x00'))/UDP(dport=12,sport=1025)
vm1 = STLScVmRaw([
- STLVmFlowVar(name="src",min_value="10.0.0.1",max_value="10.0.0.10",size=4,op="inc"),
+ STLVmFlowVar(name="src",min_value="16.0.0.1",max_value="16.0.0.10",size=4,op="inc"),
STLVmWrFlowVar(fv_name="src",pkt_offset= "IP:1.src"),
# checksum
STLVmFixIpv4(offset = "IP:1")
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index 0e87fbeb..2732548c 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -3263,6 +3263,7 @@ int CNodeGenerator::open_file(std::string file_name,
m_v_if->set_review_mode(preview_mode);
m_v_if->open_file(file_name);
m_cnt = 0;
+ m_non_active = 0;
m_limit = 0;
return (0);
}
@@ -3277,10 +3278,13 @@ int CNodeGenerator::close_file(CFlowGenListPerThread * thread){
int CNodeGenerator::update_stl_stats(CGenNodeStateless *node_sl){
m_cnt++;
-
+ if (!node_sl->is_node_active()) {
+ m_non_active++;
+ }
#ifdef _DEBUG
if ( m_preview_mode.getVMode() >2 ){
fprintf(stdout," %4lu ,", (ulong)m_cnt);
+ fprintf(stdout," %4lu ,", (ulong)m_non_active);
node_sl->Dump(stdout);
}
#endif
diff --git a/src/bp_sim.h b/src/bp_sim.h
index 371015d4..1ec036c0 100755
--- a/src/bp_sim.h
+++ b/src/bp_sim.h
@@ -63,12 +63,6 @@ limitations under the License.
#undef NAT_TRACE_
-static inline double
-usec_to_sec(double usec) {
- return (usec / (1000 * 1000));
-}
-
-
#define FORCE_NO_INLINE __attribute__ ((noinline))
/* IP address, last 32-bits of IPv6 remaps IPv4 */
@@ -2033,6 +2027,7 @@ public:
CFlowGenListPerThread * m_parent;
CPreviewMode m_preview_mode;
uint64_t m_cnt;
+ uint64_t m_non_active;
uint64_t m_limit;
CTimeHistogram m_realtime_his;
};
diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp
index 13f8eb16..10b0c3ea 100644
--- a/src/flow_stat.cpp
+++ b/src/flow_stat.cpp
@@ -845,7 +845,8 @@ bool CFlowStatRuleMgr::dump_json(std::string & json, bool baseline) {
if (user_id_info->need_to_send_rx(port) || baseline) {
user_id_info->set_no_need_to_send_rx(port);
data_section[str_user_id]["rx_pkts"][str_port] = Json::Value::UInt64(user_id_info->get_rx_counter(port).get_pkts());
- data_section[str_user_id]["rx_bytes"][str_port] = Json::Value::UInt64(user_id_info->get_rx_counter(port).get_bytes());
+ if (m_capabilities & TrexPlatformApi::IF_STAT_RX_BYTES_COUNT)
+ data_section[str_user_id]["rx_bytes"][str_port] = Json::Value::UInt64(user_id_info->get_rx_counter(port).get_bytes());
send_empty = false;
}
if (user_id_info->need_to_send_tx(port) || baseline) {
diff --git a/src/flow_stat_parser.cpp b/src/flow_stat_parser.cpp
index 8cb41fb7..e83f8a51 100644
--- a/src/flow_stat_parser.cpp
+++ b/src/flow_stat_parser.cpp
@@ -19,8 +19,10 @@
limitations under the License.
*/
+#include <netinet/in.h>
#include <common/basic_utils.h>
#include <common/Network/Packet/IPHeader.h>
+#include <common/Network/Packet/TcpHeader.h>
#include <common/Network/Packet/IPv6Header.h>
#include <common/Network/Packet/EthernetHeader.h>
#include <flow_stat_parser.h>
@@ -97,8 +99,54 @@ int CFlowStatParser::get_l4_proto(uint8_t &proto) {
return 0;
}
+// calculate the payload len. Do not want to do this in parse(), since this is required only in
+// specific cases, while parse is used in many places (including on packet RX path, where we want to bo as fast as possible)
+int CFlowStatParser::get_payload_len(uint8_t *p, uint16_t len, uint16_t &payload_len) {
+ uint16_t l2_header_len;
+ uint16_t l3_header_len;
+ uint16_t l4_header_len;
+ uint8_t *p_l4 = NULL;
+ TCPHeader *p_tcp = NULL;
+ if (!m_ipv4) {
+ payload_len = 0;
+ return -1;
+ }
+
+ l2_header_len = ((uint8_t *)m_ipv4) - p;
+ l3_header_len = m_ipv4->getHeaderLength();
+ switch (m_ipv4->getProtocol()) {
+ case IPPROTO_UDP:
+ l4_header_len = 8;
+ break;
+ case IPPROTO_TCP:
+ p_l4 = ((uint8_t *)m_ipv4) + l3_header_len;
+ if ((p_l4 + TCP_HEADER_LEN) > (p + len)) {
+ //Not enough space for TCP header
+ payload_len = 0;
+ return -1;
+ }
+ p_tcp = (TCPHeader *)p_l4;
+ l4_header_len = p_tcp->getHeaderLength();
+ break;
+ case IPPROTO_ICMP:
+ l4_header_len = 8;
+ break;
+ default:
+ l4_header_len = 0;
+ }
+
+ if (len < l2_header_len + l3_header_len + l4_header_len) {
+ payload_len = 0;
+ return -1;
+ }
+
+ payload_len = len - l2_header_len - l3_header_len - l4_header_len;
+
+ return 0;
+}
+
static const uint16_t TEST_IP_ID = 0xabcd;
-static const uint8_t TEST_L4_PROTO = 0x11;
+static const uint8_t TEST_L4_PROTO = IPPROTO_UDP;
int CFlowStatParser::test() {
uint16_t ip_id = 0;
@@ -115,6 +163,13 @@ int CFlowStatParser::test() {
0xff, TEST_L4_PROTO, 0xbd,0x04,
0x10,0x0,0x0,0x1,
0x30,0x0,0x0,0x1,
+ // TCP heaader
+ 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
+ // some extra bytes
+ 0x1, 0x2, 0x3, 0x4
};
// good packet
@@ -130,9 +185,36 @@ int CFlowStatParser::test() {
assert(l4_proto == TEST_L4_PROTO);
assert(m_stat_supported == true);
+ // payload len test
+ uint16_t payload_len;
+ int ret;
+ ret = get_payload_len(test_pkt, sizeof(test_pkt), payload_len);
+ // UDP packet.
+ assert(ret == 0);
+ assert(payload_len == 16);
+ reset();
+ // ICMP packet
+ test_pkt[27] = IPPROTO_ICMP;
+ assert (parse(test_pkt, sizeof(test_pkt)) == 0);
+ ret = get_payload_len(test_pkt, sizeof(test_pkt), payload_len);
+ assert(ret == 0);
+ assert(payload_len == 16);
+ // TCP packet
+ test_pkt[27] = IPPROTO_TCP;
+ assert (parse(test_pkt, sizeof(test_pkt)) == 0);
+ ret = get_payload_len(test_pkt, sizeof(test_pkt), payload_len);
+ assert(ret == 0);
+ assert(payload_len == 4);
+ // Other protocol
+ test_pkt[27] = 0xaa;
+ assert (parse(test_pkt, sizeof(test_pkt)) == 0);
+ ret = get_payload_len(test_pkt, sizeof(test_pkt), payload_len);
+ assert(ret == 0);
+ assert(payload_len == 24);
+
reset();
- // bad packet
+ // bad packet. change eth protocol
test_pkt[16] = 0xaa;
assert (parse(test_pkt, sizeof(test_pkt)) == -1);
assert(m_stat_supported == false);
diff --git a/src/flow_stat_parser.h b/src/flow_stat_parser.h
index 8c9e1418..0c0655ee 100644
--- a/src/flow_stat_parser.h
+++ b/src/flow_stat_parser.h
@@ -34,6 +34,7 @@ class CFlowStatParser {
virtual int get_ip_id(uint16_t &ip_id);
virtual int set_ip_id(uint16_t ip_id);
virtual int get_l4_proto(uint8_t &proto);
+ virtual int get_payload_len(uint8_t *p, uint16_t len, uint16_t &payload_len);
virtual int test();
protected:
diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h
index 90eaa7c7..b8f40df2 100644
--- a/src/internal_api/trex_platform_api.h
+++ b/src/internal_api/trex_platform_api.h
@@ -107,6 +107,7 @@ public:
IF_STAT_IPV4_ID = 1,
IF_STAT_PAYLOAD = 2,
IF_STAT_IPV6_FLOW_LABEL = 4,
+ IF_STAT_RX_BYTES_COUNT = 8, // Card support counting rx bytes
};
enum driver_speed_e {
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index 78aee6ab..a9799540 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -191,7 +191,7 @@ public:
virtual void clear_extended_stats(CPhyEthIF * _if);
virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) {return 0;}
virtual int get_stat_counters_num() {return MAX_FLOW_STATS;}
- virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID;}
+ virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;}
virtual int wait_for_stable_link();
virtual void wait_after_link_up();
};
@@ -244,7 +244,7 @@ public:
virtual int wait_for_stable_link();
virtual int get_stat_counters_num() {return MAX_FLOW_STATS;}
- virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID;}
+ virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;}
};
@@ -281,7 +281,7 @@ public:
virtual void clear_extended_stats(CPhyEthIF * _if);
virtual int wait_for_stable_link();
virtual int get_stat_counters_num() {return MAX_FLOW_STATS;}
- virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID;}
+ virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;}
virtual CFlowStatParser *get_flow_stat_parser();
};
@@ -5139,11 +5139,12 @@ int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) {
int CTRexExtendedDriverBase40G::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats) {
uint32_t diff_stats[MAX_FLOW_STATS];
+ uint32_t diff_bytes[MAX_FLOW_STATS];
// The HW counters start from some random values. The driver give us the diffs from previous,
// each time we do get_rx_stats. We need to make one first call, at system startup,
// and ignore the returned diffs
- return get_rx_stats(_if, diff_stats, stats, NULL, NULL, 0, MAX_FLOW_STATS - 1);
+ return get_rx_stats(_if, diff_stats, stats, diff_bytes, NULL, 0, MAX_FLOW_STATS - 1);
}
// instead of adding this to rte_ethdev.h
@@ -5170,6 +5171,7 @@ int CTRexExtendedDriverBase40G::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, ui
pkts[i] = (uint64_t)((hw_stats[i - min] + ((uint64_t)1 << 32)) - prev_pkts[i]);
}
prev_pkts[i] = hw_stats[i - min];
+ bytes[i] = 0;
}
return 0;
diff --git a/src/os_time.h b/src/os_time.h
index 0e732abf..42be576f 100755
--- a/src/os_time.h
+++ b/src/os_time.h
@@ -31,6 +31,11 @@ typedef double dsec_t; //time in sec double
uint32_t os_get_time_msec();
uint32_t os_get_time_freq();
+static inline double
+usec_to_sec(double usec) {
+ return (usec / (1000 * 1000));
+}
+
#ifdef LINUX
diff --git a/src/sim/trex_sim.h b/src/sim/trex_sim.h
index 5aeeb226..0c343261 100644
--- a/src/sim/trex_sim.h
+++ b/src/sim/trex_sim.h
@@ -26,12 +26,14 @@ limitations under the License.
#include <bp_sim.h>
#include <json/json.h>
#include <trex_stateless.h>
+#include <vector>
int gtest_main(int argc, char **argv);
class TrexStateless;
class TrexPublisher;
class DpToCpHandler;
+class DPCoreStats;
void set_stateless_obj(TrexStateless *obj);
@@ -146,8 +148,8 @@ private:
void run_dp_core(int core_index,
const std::string &out_filename,
- uint64_t &simulated_pkts,
- uint64_t &written_pkts);
+ std::vector<DPCoreStats> &stats,
+ DPCoreStats &total);
void cleanup();
void flush_dp_to_cp_messages_core(int core_index);
diff --git a/src/sim/trex_sim_stateless.cpp b/src/sim/trex_sim_stateless.cpp
index fa13401d..acbeef69 100644
--- a/src/sim/trex_sim_stateless.cpp
+++ b/src/sim/trex_sim_stateless.cpp
@@ -30,6 +30,23 @@ limitations under the License.
using namespace std;
+class DPCoreStats {
+public:
+ DPCoreStats() {
+ m_simulated_pkts = 0;
+ m_non_active_pkts = 0;
+ m_written_pkts = 0;
+ }
+
+ uint64_t get_on_wire_count() {
+ return (m_simulated_pkts - m_non_active_pkts);
+ }
+
+ uint64_t m_simulated_pkts;
+ uint64_t m_non_active_pkts;
+ uint64_t m_written_pkts;
+};
+
/****** utils ******/
static string format_num(double num, const string &suffix = "") {
const char x[] = {' ','K','M','G','T','P'};
@@ -330,21 +347,21 @@ SimStateless::show_intro(const std::string &out_filename) {
void
SimStateless::run_dp(const std::string &out_filename) {
- uint64_t simulated_pkts_cnt = 0;
- uint64_t written_pkts_cnt = 0;
+ std::vector<DPCoreStats> core_stats(m_dp_core_count);
+ DPCoreStats total;
- show_intro(out_filename);
+ show_intro(out_filename);
if (is_multiple_capture()) {
for (int i = 0; i < m_dp_core_count; i++) {
std::stringstream ss;
ss << out_filename << "-" << i;
- run_dp_core(i, ss.str(), simulated_pkts_cnt, written_pkts_cnt);
+ run_dp_core(i, ss.str(), core_stats, total);
}
} else {
for (int i = 0; i < m_dp_core_count; i++) {
- run_dp_core(i, out_filename, simulated_pkts_cnt, written_pkts_cnt);
+ run_dp_core(i, out_filename, core_stats, total);
}
}
@@ -354,12 +371,25 @@ SimStateless::run_dp(const std::string &out_filename) {
std::cout << "\n\nSimulation summary:\n";
std::cout << "-------------------\n\n";
- std::cout << "simulated " << simulated_pkts_cnt << " packets\n";
+
+ for (int i = 0; i < m_dp_core_count; i++) {
+ std::cout << "core index " << i << "\n";
+ std::cout << "-----------------\n\n";
+ std::cout << " simulated packets : " << core_stats[i].m_simulated_pkts << "\n";
+ std::cout << " non active packets : " << core_stats[i].m_non_active_pkts << "\n";
+ std::cout << " on-wire packets : " << core_stats[i].get_on_wire_count() << "\n\n";
+ }
+
+ std::cout << "Total:" << "\n";
+ std::cout << "-----------------\n\n";
+ std::cout << " simulated packets : " << total.m_simulated_pkts << "\n";
+ std::cout << " non active packets : " << total.m_non_active_pkts << "\n";
+ std::cout << " on-wire packets : " << total.get_on_wire_count() << "\n\n";
if (m_is_dry_run) {
std::cout << "*DRY RUN* - no packets were written\n";
} else {
- std::cout << "written " << written_pkts_cnt << " packets " << "to '" << out_filename << "'\n\n";
+ std::cout << "written " << total.m_written_pkts << " packets " << "to '" << out_filename << "'\n\n";
}
std::cout << "\n";
@@ -395,8 +425,8 @@ SimStateless::get_limit_per_core(int core_index) {
void
SimStateless::run_dp_core(int core_index,
const std::string &out_filename,
- uint64_t &simulated_pkts,
- uint64_t &written_pkts) {
+ std::vector<DPCoreStats> &stats,
+ DPCoreStats &total) {
CFlowGenListPerThread *lpt = m_fl.m_threads_info[core_index];
@@ -406,10 +436,17 @@ SimStateless::run_dp_core(int core_index,
flush_dp_to_cp_messages_core(core_index);
- simulated_pkts += lpt->m_node_gen.m_cnt;
+ /* core */
+ stats[core_index].m_simulated_pkts = lpt->m_node_gen.m_cnt;
+ stats[core_index].m_non_active_pkts = lpt->m_node_gen.m_non_active;
+
+ /* total */
+ total.m_simulated_pkts += lpt->m_node_gen.m_cnt;
+ total.m_non_active_pkts += lpt->m_node_gen.m_non_active;
if (should_capture_core(core_index)) {
- written_pkts += lpt->m_node_gen.m_cnt;
+ stats[core_index].m_written_pkts = (lpt->m_node_gen.m_cnt - lpt->m_node_gen.m_non_active);
+ total.m_written_pkts += (lpt->m_node_gen.m_cnt - lpt->m_node_gen.m_non_active);
}
}
diff --git a/src/stateless/cp/trex_exception.h b/src/stateless/cp/trex_exception.h
index b9e20761..e184fa30 100644
--- a/src/stateless/cp/trex_exception.h
+++ b/src/stateless/cp/trex_exception.h
@@ -1,22 +1,22 @@
/*
- Itay Marom
- Cisco Systems, Inc.
+ Itay Marom
+ Cisco Systems, Inc.
*/
/*
-Copyright (c) 2015-2015 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 __TREX_EXCEPTION_H__
#define __TREX_EXCEPTION_H__
@@ -28,14 +28,46 @@ limitations under the License.
* generic exception for errors
* TODO: move this to a better place
*/
-class TrexException : public std::runtime_error
+
+class TrexException : public std::runtime_error
{
-public:
- TrexException() : std::runtime_error("") {
+ public:
+ enum TrexExceptionTypes_t {
+ T_FLOW_STAT_PG_ID_DIFF_L4,
+ T_FLOW_STAT_ADD_FAIL,
+ T_FLOW_STAT_DEL_NON_EXIST,
+ T_FLOW_STAT_ASSOC_NON_EXIST_ID,
+ T_FLOW_STAT_ASSOC_OCC_ID,
+ T_FLOW_STAT_NON_EXIST_ID,
+ T_FLOW_STAT_BAD_PKT_FORMAT,
+ T_FLOW_STAT_UNSUPP_PKT_FORMAT,
+ T_FLOW_STAT_BAD_RULE_TYPE,
+ T_FLOW_STAT_BAD_RULE_TYPE_FOR_IF,
+ T_FLOW_STAT_FAILED_FIND_L4,
+ T_FLOW_STAT_PAYLOAD_TOO_SHORT,
+ T_FLOW_STAT_NO_STREAMS_EXIST,
+ T_FLOW_STAT_ALREADY_STARTED,
+ T_FLOW_STAT_ALREADY_EXIST,
+ T_FLOW_STAT_FAILED_CHANGE_IP_ID,
+ T_FLOW_STAT_NO_FREE_HW_ID,
+ T_FLOW_STAT_RX_CORE_START_FAIL,
+ T_FLOW_STAT_BAD_HW_ID,
+ T_INVALID
+ };
+
+ TrexException() : std::runtime_error(""), m_type(T_INVALID) {
+ }
+ TrexException(enum TrexExceptionTypes_t type=T_INVALID) : std::runtime_error(""), m_type(type) {
}
- TrexException(const std::string &what) : std::runtime_error(what) {
+
+ TrexException(const std::string &what, enum TrexExceptionTypes_t type=T_INVALID) : std::runtime_error(what), m_type(type) {
}
+
+ enum TrexExceptionTypes_t type() {return m_type;}
+
+ private:
+ enum TrexExceptionTypes_t m_type;
};
#endif /* __TREX_EXCEPTION_H__ */
diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp
index e3f0ba7c..4325858c 100644
--- a/src/stateless/cp/trex_stream.cpp
+++ b/src/stateless/cp/trex_stream.cpp
@@ -129,11 +129,14 @@ TrexStream::TrexStream(uint8_t type,
uint8_t port_id, uint32_t stream_id) : m_port_id(port_id), m_stream_id(stream_id) , m_rate(*this) {
/* default values */
- m_type = type;
- m_isg_usec = 0;
- m_next_stream_id = -1;
- m_enabled = false;
- m_self_start = false;
+ m_type = type;
+ m_isg_usec = 0;
+ m_next_stream_id = -1;
+ m_enabled = false;
+ m_self_start = false;
+
+ m_mc_phase_pre_sec = 0;
+ m_mc_phase_post_sec = 0;
m_pkt.binary = NULL;
m_pkt.len = 0;
@@ -148,6 +151,7 @@ TrexStream::TrexStream(uint8_t type,
m_vm_dp = NULL;
m_flags=0;
m_action_count=0;
+ m_null_stream = false;
}
TrexStream::~TrexStream() {
diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h
index ded6363e..c5bfdb98 100644
--- a/src/stateless/cp/trex_stream.h
+++ b/src/stateless/cp/trex_stream.h
@@ -22,6 +22,9 @@ limitations under the License.
#ifndef __TREX_STREAM_H__
#define __TREX_STREAM_H__
+#include <stdio.h>
+#include <string.h>
+
#include <unordered_map>
#include <vector>
#include <stdint.h>
@@ -29,9 +32,8 @@ limitations under the License.
#include <json/json.h>
-#include <trex_stream_vm.h>
-#include <stdio.h>
-#include <string.h>
+#include "os_time.h"
+#include "trex_stream_vm.h"
#include <common/captureFile.h>
#include <common/bitMan.h>
@@ -356,6 +358,10 @@ public:
m_type = type;
}
+ void set_null_stream(bool enable) {
+ m_null_stream = enable;
+ }
+
uint8_t get_type(void) const {
return ( m_type );
}
@@ -393,6 +399,7 @@ public:
set_multi_burst(burst_total_pkts,1,0.0);
}
+
/* create new stream */
TrexStream * clone(bool full = false) const {
@@ -413,8 +420,13 @@ public:
dp->m_vm_dp = NULL;
}
- dp->m_isg_usec = m_isg_usec;
- dp->m_next_stream_id = m_next_stream_id;
+ dp->m_isg_usec = m_isg_usec;
+
+ /* multi core phase paramters */
+ dp->m_mc_phase_pre_sec = m_mc_phase_pre_sec;
+ dp->m_mc_phase_post_sec = m_mc_phase_post_sec;
+
+ dp->m_next_stream_id = m_next_stream_id;
dp->m_enabled = m_enabled;
dp->m_self_start = m_self_start;
@@ -448,7 +460,25 @@ public:
return ( (m_burst_total_pkts / get_pps()) * 1000 * 1000);
}
+ double get_ipg_sec() {
+ return (1.0 / get_pps());
+ }
+ /* return the delay before starting a stream */
+ inline double get_start_delay_sec() {
+ return usec_to_sec(m_isg_usec) + m_mc_phase_pre_sec;
+ }
+
+ /* return the delay before starting the next stream */
+ inline double get_next_stream_delay_sec() {
+ return m_mc_phase_post_sec;
+ }
+
+ /* return the delay between scheduling a new burst in a multi burst stream */
+ inline double get_next_burst_delay_sec() {
+ return usec_to_sec(m_ibg_usec) + m_mc_phase_post_sec + m_mc_phase_pre_sec;
+ }
+
void Dump(FILE *fd);
StreamVmDp * getDpVm(){
@@ -490,6 +520,9 @@ public:
/* config fields */
+ double m_mc_phase_pre_sec;
+ double m_mc_phase_post_sec;
+
double m_isg_usec;
int m_next_stream_id;
@@ -497,6 +530,8 @@ public:
bool m_enabled;
bool m_self_start;
+ /* null stream (a dummy stream) */
+ bool m_null_stream;
/* VM CP and DP */
StreamVm m_vm;
diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp
index d6971d68..d1cffbb7 100644
--- a/src/stateless/cp/trex_streams_compiler.cpp
+++ b/src/stateless/cp/trex_streams_compiler.cpp
@@ -379,6 +379,8 @@ TrexStreamsCompiler::compile(uint8_t port_id,
double factor,
std::string *fail_msg) {
+ assert(dp_core_count > 0);
+
try {
return compile_internal(port_id,streams,objs,dp_core_count,factor,fail_msg);
} catch (const TrexException &ex) {
@@ -415,12 +417,87 @@ TrexStreamsCompiler::compile_internal(uint8_t por
/* check if all are cont. streams */
bool all_continues = true;
+ int non_splitable_count = 0;
for (const auto stream : streams) {
if (stream->get_type() != TrexStream::stCONTINUOUS) {
all_continues = false;
- break;
}
+ if (!stream->is_splitable(dp_core_count)) {
+ non_splitable_count++;
+ }
+ }
+
+ /* if all streams are not splitable - all should go to core 0 */
+ if (non_splitable_count == streams.size()) {
+ compile_on_single_core(port_id,
+ streams,
+ objs,
+ dp_core_count,
+ factor,
+ nodes,
+ all_continues);
+ } else {
+ compile_on_all_cores(port_id,
+ streams,
+ objs,
+ dp_core_count,
+ factor,
+ nodes,
+ all_continues);
+ }
+
+ return true;
+
+}
+
+/**
+ * compile a list of streams on a single core (pinned to core 0)
+ *
+ */
+void
+TrexStreamsCompiler::compile_on_single_core(uint8_t port_id,
+ const std::vector<TrexStream *> &streams,
+ std::vector<TrexStreamsCompiledObj *> &objs,
+ uint8_t dp_core_count,
+ double factor,
+ GraphNodeMap &nodes,
+ bool all_continues) {
+
+ /* allocate object only for core 0 */
+ TrexStreamsCompiledObj *obj = new TrexStreamsCompiledObj(port_id);
+ obj->m_all_continues = all_continues;
+ objs.push_back(obj);
+
+ /* put NULL for the rest */
+ for (uint8_t i = 1; i < dp_core_count; i++) {
+ objs.push_back(NULL);
+ }
+
+ /* compile all the streams */
+ for (auto stream : streams) {
+
+ /* skip non-enabled streams */
+ if (!stream->m_enabled) {
+ continue;
+ }
+
+ /* compile the stream for only one core */
+ compile_stream(stream, factor, 1, objs, nodes);
}
+}
+
+/**
+ * compile a list of streams on all DP cores
+ *
+ */
+void
+TrexStreamsCompiler::compile_on_all_cores(uint8_t port_id,
+ const std::vector<TrexStream *> &streams,
+ std::vector<TrexStreamsCompiledObj *> &objs,
+ uint8_t dp_core_count,
+ double factor,
+ GraphNodeMap &nodes,
+ bool all_continues) {
/* allocate objects for all DP cores */
for (uint8_t i = 0; i < dp_core_count; i++) {
@@ -441,15 +518,6 @@ TrexStreamsCompiler::compile_internal(uint8_t por
compile_stream(stream, factor, dp_core_count, objs, nodes);
}
- /* some objects might be empty - no streams were assigned */
- for (uint8_t i = 0; i < dp_core_count; i++) {
- if (objs[i]->is_empty()) {
- delete objs[i];
- objs[i] = NULL;
- }
- }
-
- return true;
}
/**
@@ -482,16 +550,17 @@ TrexStreamsCompiler::compile_stream(TrexStream *stream,
// change the packet kept in the stream). We want the state to be saved in the original stream.
get_stateless_obj()->m_rx_flow_stat.copy_state(fixed_rx_flow_stat_stream, stream);
+ fixed_rx_flow_stat_stream->update_rate_factor(factor);
+
/* can this stream be split to many cores ? */
- if (!stream->is_splitable(dp_core_count)) {
+ if ( (dp_core_count == 1) || (!stream->is_splitable(dp_core_count)) ) {
compile_stream_on_single_core(fixed_rx_flow_stat_stream,
- factor,
- objs[0],
+ dp_core_count,
+ objs,
new_id,
new_next_id);
} else {
compile_stream_on_all_cores(fixed_rx_flow_stat_stream,
- factor,
dp_core_count,
objs,
new_id,
@@ -507,7 +576,6 @@ TrexStreamsCompiler::compile_stream(TrexStream *stream,
*/
void
TrexStreamsCompiler::compile_stream_on_all_cores(TrexStream *stream,
- double factor,
uint8_t dp_core_count,
std::vector<TrexStreamsCompiledObj *> &objs,
int new_id,
@@ -515,8 +583,13 @@ TrexStreamsCompiler::compile_stream_on_all_cores(TrexStream *stream,
std::vector<TrexStream *> core_streams(dp_core_count);
- double per_core_factor = (factor / dp_core_count);
int per_core_burst_total_pkts = (stream->m_burst_total_pkts / dp_core_count);
+ const int burst_remainder = (stream->m_burst_total_pkts % dp_core_count);
+ int remainder_left = burst_remainder;
+
+ /* this is the stream base IPG (pre split) */
+ double base_ipg_sec = stream->get_ipg_sec();
+
/* for each core - creates its own version of the stream */
for (uint8_t i = 0; i < dp_core_count; i++) {
@@ -525,18 +598,31 @@ TrexStreamsCompiler::compile_stream_on_all_cores(TrexStream *stream,
/* fix stream ID */
dp_stream->fix_dp_stream_id(new_id, new_next_id);
+ /* some phase is added to avoid all the cores TXing at once */
+ dp_stream->m_mc_phase_pre_sec = base_ipg_sec * i;
- /* adjust rate and packets count */
- dp_stream->update_rate_factor(per_core_factor);
+
+ /* each core gets a share of the packets */
dp_stream->m_burst_total_pkts = per_core_burst_total_pkts;
+
+ /* rate is slower * dp_core_count */
+ dp_stream->update_rate_factor(1.0 / dp_core_count);
+
+
+ if (remainder_left > 0) {
+ dp_stream->m_burst_total_pkts++;
+ remainder_left--;
+ /* this core needs to wait to the rest of the cores that will participate in the last round */
+ dp_stream->m_mc_phase_post_sec = base_ipg_sec * remainder_left;
+ } else {
+ /* this core did not participate in the last round so it will wait its current round's left + burst_remainder */
+ dp_stream->m_mc_phase_post_sec = base_ipg_sec * (dp_core_count - 1 - i + burst_remainder);
+ }
+
core_streams[i] = dp_stream;
}
- /* take care of remainder from a burst on core 0 */
- int burst_remainder = stream->m_burst_total_pkts - (per_core_burst_total_pkts * dp_core_count);
- core_streams[0]->m_burst_total_pkts += burst_remainder;
-
/* handle VM (split if needed) */
TrexVmSplitter vm_splitter;
vm_splitter.split( (TrexStream *)stream, core_streams);
@@ -554,8 +640,8 @@ TrexStreamsCompiler::compile_stream_on_all_cores(TrexStream *stream,
*/
void
TrexStreamsCompiler::compile_stream_on_single_core(TrexStream *stream,
- double factor,
- TrexStreamsCompiledObj *obj,
+ uint8_t dp_core_count,
+ std::vector<TrexStreamsCompiledObj *> &objs,
int new_id,
int new_next_id) {
@@ -570,8 +656,21 @@ TrexStreamsCompiler::compile_stream_on_single_core(TrexStream *stream,
dp_stream->m_vm_dp = stream->m_vm_dp->clone();
}
- /* update the core */
- obj->add_compiled_stream(dp_stream);
+ /* update core 0 with the real stream */
+ objs[0]->add_compiled_stream(dp_stream);
+
+
+ /* create dummy streams for the other cores */
+ for (uint8_t i = 1; i < dp_core_count; i++) {
+ TrexStream *null_dp_stream = stream->clone();
+
+ /* fix stream ID */
+ null_dp_stream->fix_dp_stream_id(new_id, new_next_id);
+
+ /* mark as null stream and add */
+ null_dp_stream->set_null_stream(true);
+ objs[i]->add_compiled_stream(null_dp_stream);
+ }
}
/**************************************
diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h
index b8b0be37..23b06d06 100644
--- a/src/stateless/cp/trex_streams_compiler.h
+++ b/src/stateless/cp/trex_streams_compiler.h
@@ -123,6 +123,23 @@ private:
void add_warning(const std::string &warning);
void err(const std::string &err);
+ void compile_on_single_core(uint8_t port_id,
+ const std::vector<TrexStream *> &streams,
+ std::vector<TrexStreamsCompiledObj *> &objs,
+ uint8_t dp_core_count,
+ double factor,
+ GraphNodeMap &nodes,
+ bool all_continues);
+
+ void compile_on_all_cores(uint8_t port_id,
+ const std::vector<TrexStream *> &streams,
+ std::vector<TrexStreamsCompiledObj *> &objs,
+ uint8_t dp_core_count,
+ double factor,
+ GraphNodeMap &nodes,
+ bool all_continues);
+
+
void compile_stream(TrexStream *stream,
double factor,
uint8_t dp_core_count,
@@ -130,13 +147,12 @@ private:
GraphNodeMap &nodes);
void compile_stream_on_single_core(TrexStream *stream,
- double factor,
- TrexStreamsCompiledObj *obj,
+ uint8_t dp_core_count,
+ std::vector<TrexStreamsCompiledObj *> &objs,
int new_id,
int new_next_id);
void compile_stream_on_all_cores(TrexStream *stream,
- double factor,
uint8_t dp_core_count,
std::vector<TrexStreamsCompiledObj *> &objs,
int new_id,
diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp
index f125a46a..d3d49a34 100644
--- a/src/stateless/dp/trex_stateless_dp_core.cpp
+++ b/src/stateless/dp/trex_stateless_dp_core.cpp
@@ -602,7 +602,7 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port,
node->m_state =CGenNodeStateless::ss_INACTIVE;
}
- node->m_time = m_core->m_cur_time_sec + usec_to_sec(stream->m_isg_usec);
+ node->m_time = m_core->m_cur_time_sec + stream->get_start_delay_sec();
pkt_dir_t dir = m_core->m_node_gen.m_v_if->port_id_to_dir(stream->m_port_id);
node->m_flags = 0;
@@ -627,6 +627,7 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port,
node->m_pause =0;
node->m_stream_type = stream->m_type;
node->m_next_time_offset = 1.0 / stream->get_pps();
+ node->m_null_stream = (stream->m_null_stream ? 1 : 0);
/* stateless specific fields */
switch ( stream->m_type ) {
diff --git a/src/stateless/dp/trex_stream_node.h b/src/stateless/dp/trex_stream_node.h
index 104e4d3b..c85bf8b5 100644
--- a/src/stateless/dp/trex_stream_node.h
+++ b/src/stateless/dp/trex_stream_node.h
@@ -84,7 +84,7 @@ private:
double m_next_time_offset; /* in sec */
uint16_t m_action_counter;
uint8_t m_stat_hw_id; // hw id used to count rx and tx stats
- uint8_t m_pad11;
+ uint8_t m_null_stream;
uint32_t m_pad12;
stream_state_t m_state;
@@ -144,7 +144,7 @@ public:
/* we restart the stream, schedule it using stream isg */
inline void update_refresh_time(double cur_time){
- m_time = cur_time + usec_to_sec(m_ref_stream_info->m_isg_usec);
+ m_time = cur_time + usec_to_sec(m_ref_stream_info->m_isg_usec) + m_ref_stream_info->m_mc_phase_pre_sec;
}
inline bool is_mask_for_free(){
@@ -170,6 +170,11 @@ public:
}
}
+ bool is_node_active() {
+ /* bitwise or - faster instead of two IFs */
+ return ((m_pause | m_null_stream) == 0);
+ }
+
inline uint8_t get_stream_type(){
return (m_stream_type);
}
@@ -199,7 +204,7 @@ public:
inline void handle_continues(CFlowGenListPerThread *thread) {
- if (unlikely (is_pause()==false)) {
+ if (likely (is_node_active())) {
thread->m_node_gen.m_v_if->send_node( (CGenNode *)this);
}
@@ -211,7 +216,9 @@ public:
}
inline void handle_multi_burst(CFlowGenListPerThread *thread) {
- thread->m_node_gen.m_v_if->send_node( (CGenNode *)this);
+ if (likely (is_node_active())) {
+ thread->m_node_gen.m_v_if->send_node( (CGenNode *)this);
+ }
m_single_burst--;
if (m_single_burst > 0 ) {
@@ -224,8 +231,8 @@ public:
if ( m_multi_bursts == 0 ) {
set_state(CGenNodeStateless::ss_INACTIVE);
if ( thread->set_stateless_next_node(this,m_next_stream) ){
- /* update the next stream time using isg */
- m_next_stream->update_refresh_time(m_time);
+ /* update the next stream time using isg and post phase */
+ m_next_stream->update_refresh_time(m_time + m_ref_stream_info->get_next_stream_delay_sec());
thread->m_node_gen.m_p_queue.push( (CGenNode *)m_next_stream);
}else{
@@ -234,7 +241,8 @@ public:
}
}else{
- m_time += get_multi_ibg_sec();
+ /* next burst is like starting a new stream - add pre and post phase */
+ m_time += m_ref_stream_info->get_next_burst_delay_sec();
m_single_burst = m_single_burst_refill;
thread->m_node_gen.m_p_queue.push( (CGenNode *)this);
}
diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp
index 26f537f8..2132fe9f 100644
--- a/src/stateless/rx/trex_stateless_rx_core.cpp
+++ b/src/stateless/rx/trex_stateless_rx_core.cpp
@@ -120,6 +120,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPort *lp, rte_mbuf_t *m)
}
}
}
+ rte_pktmbuf_free(m);
}
// In VM setup, handle packets coming as messages from DP cores.