From 2dab6b6d09f9f1474d2ade6b465ebfa5ce3b3f5e Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sat, 22 Oct 2016 10:38:27 +0200 Subject: dpdk_setup_ports.py: fix add of help in case of "t-rex-64 --help" dpdk_setup_ports.py: fix warning of TRex is already running if different NICs are being used singleton_daemon.py: fix error socket in use immediately after check if in use trex-console: fix crash in case of "tui --help" trex-console: try-catch commands instead of crashing add async notification on port status/atttibutes change add port xstats support add description of interfaces main_dpdk.cpp: fix --client_cfg not working with Python API Signed-off-by: Yaroslav Brustinov --- .../trex_control_plane/server/singleton_daemon.py | 1 + .../trex_control_plane/stl/console/trex_console.py | 37 +++--- .../stl/trex_stl_lib/trex_stl_client.py | 45 +++++-- .../stl/trex_stl_lib/trex_stl_jsonrpc_client.py | 2 +- .../stl/trex_stl_lib/trex_stl_port.py | 59 +++++++-- .../stl/trex_stl_lib/trex_stl_stats.py | 144 +++++++++++++++------ .../stl/trex_stl_lib/utils/common.py | 15 ++- .../stl/trex_stl_lib/utils/constants.py | 26 ++++ .../stl/trex_stl_lib/utils/parsing_opts.py | 43 +++--- scripts/dpdk_setup_ports.py | 23 ++-- src/dpdk/drivers/net/virtio/virtio_ethdev.c | 2 + src/dpdk/drivers/net/vmxnet3/vmxnet3_ethdev.c | 2 + src/internal_api/trex_platform_api.h | 45 ++----- src/main_dpdk.cpp | 138 ++++++++++++-------- src/pal/linux/rte_ethdev_includes.h | 23 ++++ src/pal/linux_dpdk/rte_ethdev_includes.h | 1 + src/publisher/trex_publisher.h | 1 + src/rpc-server/commands/trex_rpc_cmd_general.cpp | 39 +++++- src/stateless/cp/trex_stateless_port.cpp | 11 +- src/stateless/cp/trex_streams_compiler.h | 6 +- src/trex_defs.h | 1 + src/trex_port_attr.h | 39 ++++-- 22 files changed, 492 insertions(+), 211 deletions(-) create mode 100755 scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py create mode 100644 src/pal/linux/rte_ethdev_includes.h create mode 100644 src/pal/linux_dpdk/rte_ethdev_includes.h diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py index cd16d173..507967aa 100755 --- a/scripts/automation/trex_control_plane/server/singleton_daemon.py +++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py @@ -37,6 +37,7 @@ class SingletonDaemon(object): lock_socket.close() except socket.error: # Unix socket in use return True + sleep(0.5) # Unix socket is not used, but maybe it's old version of daemon not using socket return bool(self.get_pid_by_listening_port()) diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 5d23d8da..b23b5f1f 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -230,14 +230,17 @@ class TRexConsole(TRexGeneralCmd): self.save_console_history() lines = line.split(';') - - for line in lines: - stop = self.onecmd(line) - stop = self.postcmd(stop, line) - if stop: - return "quit" - - return "" + try: + for line in lines: + stop = self.onecmd(line) + stop = self.postcmd(stop, line) + if stop: + return "quit" + + return "" + except STLError as e: + print(e) + return '' def postcmd(self, stop, line): @@ -364,7 +367,7 @@ class TRexConsole(TRexGeneralCmd): 'help': "an history item index", 'default': 0}) - parser = parsing_opts.gen_parser(self, + parser = parsing_opts.gen_parser(self.stateless_client, "history", self.do_history.__doc__, item) @@ -551,16 +554,16 @@ class TRexConsole(TRexGeneralCmd): @verify_connected def do_tui (self, line): '''Shows a graphical console\n''' - parser = parsing_opts.gen_parser(self, + parser = parsing_opts.gen_parser(self.stateless_client, "tui", self.do_tui.__doc__, parsing_opts.XTERM, parsing_opts.LOCKED) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts if opts.xterm: if not os.path.exists('/usr/bin/xterm'): print(format_text("XTERM does not exists on this machine", 'bold')) @@ -784,14 +787,18 @@ def show_intro (logger, c): # find out which NICs the server has port_types = {} for port in x['ports']: - key = (port['speed'], port['driver']) - if not key in port_types: + if 'supp_speeds' in port: + speed = max(port['supp_speeds']) // 1000 + else: + speed = port['speed'] + key = (speed, port.get('description', port['driver'])) + if key not in port_types: port_types[key] = 0 port_types[key] += 1 port_line = '' for k, v in port_types.items(): - port_line += "{0} x {1}Gbps @ {2}".format(v, k[0], k[1]) + port_line += "{0} x {1}Gbps @ {2}\t".format(v, k[0], k[1]) logger.log(format_text("\nServer Info:\n", 'underline')) logger.log("Server version: {:>}".format(format_text(ver, 'bold'))) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index b11ddbe3..ec7e6eef 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -320,6 +320,13 @@ class EventsHandler(object): ev = "port {0} job failed".format(port_id) show_event = True + # port attr changed + elif (type == 8): + port_id = int(data['port_id']) + ev = "port {0} attributes changed".format(port_id) + show_event = True + self.__async_event_port_attr_changed(port_id, data['attr']) + # server stopped elif (type == 100): ev = "Server has stopped" @@ -372,6 +379,9 @@ class EventsHandler(object): def __async_event_server_stopped (self): self.client.connected = False + def __async_event_port_attr_changed (self, port_id, attr): + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_attr_changed(attr) # add event to log def __add_event_log (self, origin, ev_type, msg, show = False): @@ -887,7 +897,7 @@ class STLClient(object): # clear stats - def __clear_stats(self, port_id_list, clear_global, clear_flow_stats, clear_latency_stats): + def __clear_stats(self, port_id_list, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats): # we must be sync with the server self.async_client.barrier() @@ -904,6 +914,9 @@ class STLClient(object): if clear_latency_stats: self.latency_stats.clear_stats() + if clear_xstats: + self.xstats.clear_stats() + self.logger.log_cmd("Clearing stats on port(s) {0}:".format(port_id_list)) return RC @@ -2501,6 +2514,9 @@ class STLClient(object): clear_latency_stats : bool Clear the latency stats + clear_xstats : bool + Clear the extended stats + :raises: + :exc:`STLError` @@ -2513,7 +2529,7 @@ class STLClient(object): if not type(clear_global) is bool: raise STLArgumentError('clear_global', clear_global) - rc = self.__clear_stats(ports, clear_global, clear_flow_stats, clear_latency_stats) + rc = self.__clear_stats(ports, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats) if not rc: raise STLError(rc) @@ -2605,7 +2621,7 @@ class STLClient(object): flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable :raises: - None + + :exe:'STLError' """ @@ -3205,23 +3221,34 @@ class STLClient(object): parsing_opts.LINK_STATUS, parsing_opts.LED_STATUS, parsing_opts.FLOW_CTRL, + parsing_opts.SUPPORTED, ) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if not opts: return opts - opts.prom = parsing_opts.on_off_dict.get(opts.prom) - opts.link = parsing_opts.on_off_dict.get(opts.link) - opts.led = parsing_opts.on_off_dict.get(opts.led) - opts.flow_ctrl = parsing_opts.flow_ctrl_dict.get(opts.flow_ctrl) + opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom) + opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link) + opts.led = parsing_opts.ON_OFF_DICT.get(opts.led) + opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl) # if no attributes - fall back to printing the status - if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl]): + if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp]): self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports))) return - return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl) + if opts.supp: + info = self.ports[0].get_info() # assume for now all ports are same + print('') + print('Supported attributes for current NICs:') + print(' Promiscuous: yes') + print(' Link status: %s' % info['link_change_supported']) + print(' LED status: %s' % info['led_change_supported']) + print(' Flow control: %s' % info['fc_supported']) + print('') + else: + return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl) @__console diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py index 609ea076..1461fcec 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py @@ -182,7 +182,7 @@ class JsonRpcClient(object): tries += 1 if tries > 5: self.disconnect() - return RC_ERR("*** [RPC] - Failed to get server response at {0}".format(self.transport)) + return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport)) return response diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index f0e3b109..4a094c5d 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -5,6 +5,7 @@ from .trex_stl_packet_builder_scapy import STLPktBuilder from .trex_stl_streams import STLStream from .trex_stl_types import * from . import trex_stl_stats +from .utils.constants import FLOW_CTRL_DICT_REVERSED import base64 import copy @@ -250,9 +251,10 @@ class Port(object): self.next_available_id = int(rc.data()['max_stream_id']) + 1 # attributes - self.attr = rc.data()['attr'] + self.attr = ['attr'] + if 'speed' in rc.data(): + self.info['speed'] = rc.data()['speed'] // 1000 - return self.ok() @@ -577,7 +579,7 @@ class Port(object): return self.err(rc.err()) - self.attr.update(attr_dict) + #self.attr.update(attr_dict) return self.ok() @@ -650,13 +652,48 @@ class Port(object): def get_info (self): info = dict(self.info) - info['status'] = self.get_port_state_name() + info['status'] = self.get_port_state_name() + if 'link' in self.attr: + info['link'] = 'UP' if self.attr['link']['up'] else 'DOWN' + else: + info['link'] = 'N/A' + + if 'fc' in self.attr: + info['fc'] = FLOW_CTRL_DICT_REVERSED.get(self.attr['fc']['mode'], 'N/A') + else: + info['fc'] = 'N/A' - if self.attr.get('promiscuous'): + if 'promiscuous' in self.attr: info['prom'] = "on" if self.attr['promiscuous']['enabled'] else "off" else: info['prom'] = "N/A" + if 'description' in info: + if len(info['description']) > 18: + info['description'] = info['description'][:18] + else: + info['description'] = "N/A" + + if 'is_fc_supported' in info: + info['fc_supported'] = 'yes' if info['is_fc_supported'] else 'no' + else: + info['fc_supported'] = 'N/A' + + if 'is_led_supported' in info: + info['led_change_supported'] = 'yes' if info['is_led_supported'] else 'no' + else: + info['led_change_supported'] = 'N/A' + + if 'is_link_supported' in info: + info['link_change_supported'] = 'yes' if info['is_link_supported'] else 'no' + else: + info['link_change_supported'] = 'N/A' + + if 'is_virtual' in info: + info['is_virtual'] = 'yes' if info['is_virtual'] else 'no' + else: + info['is_virtual'] = 'N/A' + return info @@ -672,6 +709,7 @@ class Port(object): info = self.get_info() return {"driver": info['driver'], + "description": info.get('description', 'N/A'), "HW src mac": info['hw_macaddr'], "SW src mac": info['src_macaddr'], "SW dst mac": info['dst_macaddr'], @@ -679,9 +717,10 @@ class Port(object): "NUMA Node": info['numa'], "--": "", "---": "", - "maximum": "{speed} Gb/s".format(speed=info['speed']), - "status": info['status'], - "promiscuous" : info['prom'] + "link speed": "{speed} Gb/s".format(speed=info['speed']), + "status": '%s (link %s)' % (info['status'], info['link']), + "promiscuous" : info['prom'], + "flow ctrl" : info['fc'], } def clear_stats(self): @@ -726,6 +765,10 @@ class Port(object): self.state = self.STATE_STREAMS self.last_factor_type = None + def async_event_port_attr_changed (self, attr): + self.info['speed'] = attr['speed'] // 1000 + self.attr = attr + # rest of the events are used for TUI / read only sessions def async_event_port_stopped (self): if not self.is_acquired(): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index eaa5e8b0..783387ca 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -3,6 +3,7 @@ from .utils import text_tables from .utils.text_opts import format_text, format_threshold, format_num from .trex_stl_types import StatNotAvailable, is_integer +from .trex_stl_exceptions import STLError from collections import namedtuple, OrderedDict, deque import sys @@ -23,8 +24,10 @@ LATENCY_STATS = 'ls' LATENCY_HISTOGRAM = 'lh' CPU_STATS = 'c' MBUF_STATS = 'm' +EXTENDED_STATS = 'x' +EXTENDED_INC_ZERO_STATS = 'xz' -ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS] +ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS, EXTENDED_STATS, EXTENDED_INC_ZERO_STATS] COMPACT = [GLOBAL_STATS, PORT_STATS] GRAPH_PORT_COMPACT = [GLOBAL_STATS, PORT_GRAPH] SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] # stream stats @@ -184,6 +187,12 @@ class CTRexInfoGenerator(object): elif statistic_type == MBUF_STATS: return self._generate_mbuf_util_stats() + elif statistic_type == EXTENDED_STATS: + return self._generate_xstats(port_id_list, include_zero_lines = False) + + elif statistic_type == EXTENDED_INC_ZERO_STATS: + return self._generate_xstats(port_id_list, include_zero_lines = True) + else: # ignore by returning empty object return {} @@ -206,7 +215,7 @@ class CTRexInfoGenerator(object): def _generate_global_stats(self): global_stats = self._global_stats - stats_data = OrderedDict([("connection", "{host}, Port {port}".format(host=global_stats.connection_info.get("server"), + stats_data_left = OrderedDict([("connection", "{host}, Port {port}".format(host=global_stats.connection_info.get("server"), port=global_stats.connection_info.get("sync_port"))), ("version", "{ver}, UUID: {uuid}".format(ver=global_stats.server_version.get("version", "N/A"), uuid="N/A")), @@ -222,14 +231,13 @@ class CTRexInfoGenerator(object): ("async_util.", "{0}% / {1}".format( format_threshold(round_float(self._async_monitor.get_cpu_util()), [85, 100], [0, 85]), format_num(self._async_monitor.get_bps() / 8.0, suffix = "B/sec"))), - - - (" ", ""), + ]) + stats_data_right = OrderedDict([ ("total_tx_L2", "{0} {1}".format( global_stats.get("m_tx_bps", format=True, suffix="b/sec"), global_stats.get_trend_gui("m_tx_bps"))), - ("total_tx_L1", "{0} {1}".format( global_stats.get("m_tx_bps_L1", format=True, suffix="b/sec"), + ("total_tx_L1", "{0} {1}".format( global_stats.get("m_tx_bps_L1", format=True, suffix="b/sec"), global_stats.get_trend_gui("m_tx_bps_L1"))), ("total_rx", "{0} {1}".format( global_stats.get("m_rx_bps", format=True, suffix="b/sec"), @@ -238,8 +246,6 @@ class CTRexInfoGenerator(object): ("total_pps", "{0} {1}".format( global_stats.get("m_tx_pps", format=True, suffix="pkt/sec"), global_stats.get_trend_gui("m_tx_pps"))), - #(" ", ""), - ("drop_rate", "{0}".format( format_num(global_stats.get("m_rx_drop_bps"), suffix = 'b/sec', opts = 'green' if (global_stats.get("m_rx_drop_bps")== 0) else 'red'), @@ -249,19 +255,29 @@ class CTRexInfoGenerator(object): suffix = 'pkts', compact = False, opts = 'green' if (global_stats.get_rel("m_total_queue_full")== 0) else 'red'))), - - ] - ) + ]) # build table representation stats_table = text_tables.TRexTextInfo() stats_table.set_cols_align(["l", "l"]) + stats_table.set_deco(0) + stats_table.set_cols_width([50, 45]) + max_lines = max(len(stats_data_left), len(stats_data_right)) + for line_num in range(max_lines): + row = [] + if line_num < len(stats_data_left): + key = list(stats_data_left.keys())[line_num] + row.append('{:<12} : {}'.format(key, stats_data_left[key])) + else: + row.append('') + if line_num < len(stats_data_right): + key = list(stats_data_right.keys())[line_num] + row.append('{:<12} : {}'.format(key, stats_data_right[key])) + else: + row.append('') + stats_table.add_row(row) - stats_table.add_rows([[k.replace("_", " ").title(), v] - for k, v in stats_data.items()], - header=False) - - return {"global_statistics": ExportableStats(stats_data, stats_table)} + return {"global_statistics": ExportableStats(None, stats_table)} def _generate_streams_stats (self): flow_stats = self._rx_stats_ref @@ -505,6 +521,29 @@ class CTRexInfoGenerator(object): stats_table.add_row(['No Data.']) return {'mbuf_util': ExportableStats(None, stats_table)} + def _generate_xstats(self, port_id_list, include_zero_lines = False): + relevant_ports = [port.port_id for port in self.__get_relevant_ports(port_id_list)] + # get the data on relevant ports + xstats_data = OrderedDict() + for port_id in relevant_ports: + for key, val in self._xstats_ref.get_stats(port_id).items(): + if key not in xstats_data: + xstats_data[key] = [] + xstats_data[key].append(val) + + # put into table + stats_table = text_tables.TRexTextTable() + stats_table.header(['Name:'] + ['Port %s:' % port_id for port_id in relevant_ports]) + stats_table.set_cols_align(['l'] + ['r'] * len(relevant_ports)) + stats_table.set_cols_width([30] + [15] * len(relevant_ports)) + stats_table.set_cols_dtype(['t'] * (len(relevant_ports) + 1)) + for key, arr in xstats_data.items(): + if include_zero_lines or list(filter(None, arr)): + if len(key) > 28: + key = key[:28] + stats_table.add_row([key] + arr) + return {'xstats:': ExportableStats(None, stats_table)} + @staticmethod def _get_rational_block_char(value, range_start, interval): # in Konsole, utf-8 is sometimes printed with artifacts, return ascii for now @@ -558,6 +597,7 @@ class CTRexInfoGenerator(object): return_stats_data = {} per_field_stats = OrderedDict([("owner", []), + ('link', []), ("state", []), ("speed", []), ("CPU util.", []), @@ -585,8 +625,7 @@ class CTRexInfoGenerator(object): ("oerrors", []), ("ierrors", []), - ] - ) + ]) total_stats = CPortStats(None) @@ -626,9 +665,11 @@ class CTRexInfoGenerator(object): return_stats_data = {} per_field_status = OrderedDict([("driver", []), - ("maximum", []), + ("description", []), + ("link speed", []), ("status", []), ("promiscuous", []), + ("flow ctrl", []), ("--", []), ("HW src mac", []), ("SW src mac", []), @@ -1054,13 +1095,24 @@ class CPortStats(CTRexStats): def generate_stats(self): - state = self._port_obj.get_port_state_name() if self._port_obj else "" - if state == "ACTIVE": - state = format_text(state, 'green', 'bold') - elif state == "PAUSE": - state = format_text(state, 'magenta', 'bold') + port_state = self._port_obj.get_port_state_name() if self._port_obj else "" + if port_state == "ACTIVE": + port_state = format_text('TRANSMITTING', 'green', 'bold') + elif port_state == "PAUSE": + port_state = format_text(port_state, 'magenta', 'bold') + else: + port_state = format_text(port_state, 'bold') + + if self._port_obj: + if 'link' in self._port_obj.attr: + if self._port_obj.attr.get('link', {}).get('up') == False: + link_state = format_text('DOWN', 'red', 'bold') + else: + link_state = 'UP' + else: + link_state = 'N/A' else: - state = format_text(state, 'bold') + link_state = '' # default rate format modifiers rate_format = {'bpsl1': None, 'bps': None, 'pps': None, 'percentage': 'bold'} @@ -1077,7 +1129,8 @@ class CPortStats(CTRexStats): return {"owner": owner, - "state": "{0}".format(state), + "state": "{0}".format(port_state), + 'link': link_state, "speed": self._port_obj.get_formatted_speed() if self._port_obj else '', "CPU util.": "{0} {1}%".format(self.get_trend_gui("m_cpu_util", use_raw = True), format_threshold(round_float(self.get("m_cpu_util")), [85, 100], [0, 85])) if self._port_obj else '' , @@ -1439,7 +1492,7 @@ class CUtilStats(CTRexStats): if self.client.is_connected(): rc = self.client._transmit('get_utilization') if not rc: - raise Exception(rc) + raise STLError(rc) self.last_update_ts = time_now self.history.append(rc.data()) else: @@ -1452,33 +1505,44 @@ class CXStats(CTRexStats): def __init__(self, client): super(CXStats, self).__init__() self.client = client - self.history = deque(maxlen = 1) - self.names = {} + self.names = [] self.last_update_ts = -999 - def get_stats(self, port_id, use_1sec_cache = False): + def clear_stats(self, port_id = None): + if port_id == None: + ports = self.client.get_all_ports() + elif type(port_id) is list: + ports = port_id + else: + ports = [port_id] + + for port_id in ports: + self.reference_stats[port_id] = self.get_stats(port_id, relative = False) + + def get_stats(self, port_id, use_1sec_cache = False, relative = True): time_now = time.time() - if self.last_update_ts + 1 < time_now or not self.history or not use_1sec_cache: + if self.last_update_ts + 1 < time_now or not self.latest_stats or not use_1sec_cache: if self.client.is_connected(): rc = self.client._transmit('get_port_xstats_values', params = {'port_id': port_id}) if not rc: - raise Exception(rc) + raise STLError(rc) self.last_update_ts = time_now values = rc.data().get('xstats_values', []) if len(values) != len(self.names): # need to update names ("keys") rc = self.client._transmit('get_port_xstats_names', params = {'port_id': port_id}) if not rc: - raise Exception(rc) + raise STLError(rc) self.names = rc.data().get('xstats_names', []) if len(values) != len(self.names): - raise Exception('Length of get_xstats_names: %s and get_port_xstats_values: %s' % (len(self.names), len(values))) - self.history.append(dict([(key, val) for key, val in zip(self.names, values)])) - else: - self.history.append({}) + raise STLError('Length of get_xstats_names: %s and get_port_xstats_values: %s' % (len(self.names), len(values))) + self.latest_stats[port_id] = OrderedDict([(key, val) for key, val in zip(self.names, values)]) - stats = {} - for key, val in self.history[-1].items(): - stats[key] = self.history[-1][key] - self.reference_stats.get(key, 0) + stats = OrderedDict() + for key, val in self.latest_stats[port_id].items(): + if relative: + stats[key] = self.get_rel([port_id, key]) + else: + stats[key] = self.get([port_id, key]) return stats if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py index 638684c3..0214d7d7 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py @@ -27,7 +27,7 @@ def user_input(): return raw_input() -def random_id_gen(length=8): +def random_id_gen_unsafe(length=8): """ A generator for creating a random chars id of specific length @@ -47,6 +47,19 @@ def random_id_gen(length=8): return_id += random.choice(id_chars) yield return_id + +class random_id_gen: + """ Thread safe version of random_id_gen_unsafe """ + def __init__(self, length=8): + self.id_chars = string.ascii_lowercase + string.digits + self.length = length + + def next(self): + return ''.join(random.choice(self.id_chars) for _ in range(self.length)) + + __next__ = next + + # try to get number from input, return None in case of fail def get_number(input): try: diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py new file mode 100755 index 00000000..a4942094 --- /dev/null +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py @@ -0,0 +1,26 @@ +from collections import OrderedDict + +ON_OFF_DICT = OrderedDict([ + ('on', True), + ('off', False), +]) + +UP_DOWN_DICT = OrderedDict([ + ('up', True), + ('down', False), +]) + +FLOW_CTRL_DICT = OrderedDict([ + ('none', 0), # Disable flow control + ('tx', 1), # Enable flowctrl on TX side (RX pause frames) + ('rx', 2), # Enable flowctrl on RX side (TX pause frames) + ('full', 3), # Enable flow control on both sides +]) + + + +# generate reverse dicts + +for var_name in list(vars().keys()): + if var_name.endswith('_DICT'): + exec('{0}_REVERSED = OrderedDict([(val, key) for key, val in {0}.items()])'.format(var_name)) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index 148f7715..7eda8635 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -3,6 +3,7 @@ from collections import namedtuple, OrderedDict from .common import list_intersect, list_difference from .text_opts import format_text from ..trex_stl_types import * +from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT import sys import re @@ -11,18 +12,6 @@ import os ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) -on_off_dict = OrderedDict([ - ('on', True), - ('off', False), -]) - -flow_ctrl_dict = OrderedDict([ - ('none', 0), # Disable flow control - ('tx', 1), # Enable flowctrl on TX side (RX pause frames) - ('rx', 2), # Enable flowctrl on RX side (TX pause frames) - ('full', 3), # Enable flow control on both sides -]) - # list of available parsing options MULTIPLIER = 1 @@ -53,6 +42,7 @@ PIN_CORES = 25 CORE_MASK = 26 DUAL = 27 FLOW_CTRL = 28 +SUPPORTED = 29 GLOBAL_STATS = 50 PORT_STATS = 51 @@ -61,6 +51,8 @@ STREAMS_STATS = 53 STATS_MASK = 54 CPU_STATS = 55 MBUF_STATS = 56 +EXTENDED_STATS = 57 +EXTENDED_INC_ZERO_STATS = 58 STREAMS_MASK = 60 CORE_MASK_GROUP = 61 @@ -296,20 +288,25 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], PROMISCUOUS: ArgumentPack(['--prom'], {'help': "Set port promiscuous on/off", - 'choices': on_off_dict}), + 'choices': ON_OFF_DICT}), LINK_STATUS: ArgumentPack(['--link'], - {'help': 'Set link status on/off', - 'choices': on_off_dict}), + {'help': 'Set link status up/down', + 'choices': UP_DOWN_DICT}), LED_STATUS: ArgumentPack(['--led'], {'help': 'Set LED status on/off', - 'choices': on_off_dict}), + 'choices': ON_OFF_DICT}), FLOW_CTRL: ArgumentPack(['--fc'], {'help': 'Set Flow Control type', 'dest': 'flow_ctrl', - 'choices': flow_ctrl_dict}), + 'choices': FLOW_CTRL_DICT}), + + SUPPORTED: ArgumentPack(['--supp'], + {'help': 'Show which attributes are supported by current NICs', + 'default': None, + 'action': 'store_true'}), TUNABLES: ArgumentPack(['-t'], {'help': "Sets tunables for a profile. Example: '-t fsize=100,pg_id=7'", @@ -419,6 +416,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'action': 'store_true', 'help': "Fetch only MBUF utilization stats"}), + EXTENDED_STATS: ArgumentPack(['-x'], + {'action': 'store_true', + 'help': "Fetch xstats of port, excluding lines with zero values"}), + + EXTENDED_INC_ZERO_STATS: ArgumentPack(['--xz'], + {'action': 'store_true', + 'help': "Fetch xstats of port, including lines with zero values"}), + STREAMS_MASK: ArgumentPack(['--streams'], {"nargs": '+', 'dest':'streams', @@ -455,7 +460,9 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], PORT_STATUS, STREAMS_STATS, CPU_STATS, - MBUF_STATS], + MBUF_STATS, + EXTENDED_STATS, + EXTENDED_INC_ZERO_STATS,], {}), diff --git a/scripts/dpdk_setup_ports.py b/scripts/dpdk_setup_ports.py index 2782384c..5bbbdb28 100755 --- a/scripts/dpdk_setup_ports.py +++ b/scripts/dpdk_setup_ports.py @@ -358,11 +358,13 @@ Other network devices if if_list and map_driver.args.parent and dpdk_nic_bind.get_igb_uio_usage(): pid = dpdk_nic_bind.get_pid_using_pci(if_list) - cmdline = dpdk_nic_bind.read_pid_cmdline(pid) - print('Some or all of given interfaces are in use by following process:\npid: %s, cmd: %s' % (pid, cmdline)) - if not dpdk_nic_bind.confirm('Ignore and proceed (y/N):'): - sys.exit(1) - + if pid: + cmdline = dpdk_nic_bind.read_pid_cmdline(pid) + print('Some or all of given interfaces are in use by following process:\npid: %s, cmd: %s' % (pid, cmdline)) + if not dpdk_nic_bind.confirm('Ignore and proceed (y/N):'): + sys.exit(1) + else: + print('WARNING: Some other program is using DPDK driver.\nIf it is TRex and you did not configure it for dual run, current command will fail.') def do_return_to_linux(self): if not self.m_devices: @@ -459,11 +461,14 @@ Other network devices wanted_interfaces.append(dev) unbound = [] + dpdk_bound = [] for interface in wanted_interfaces: if 'Driver_str' not in interface: unbound.append(interface['Slot']) - if unbound: - for pci, info in dpdk_nic_bind.get_info_from_trex(unbound).items(): + elif interface.get('Driver_str') in dpdk_nic_bind.dpdk_drivers: + dpdk_bound.append(interface['Slot']) + if unbound or dpdk_bound: + for pci, info in dpdk_nic_bind.get_info_from_trex(unbound + dpdk_bound).items(): if pci not in self.m_devices: raise DpdkSetup('Internal error: PCI %s is not found among devices' % pci) self.m_devices[pci].update(info) @@ -653,7 +658,7 @@ Other network devices def parse_parent_cfg (parent_cfg): - parent_parser = argparse.ArgumentParser() + parent_parser = argparse.ArgumentParser(add_help = False) parent_parser.add_argument('--cfg', default='') parent_parser.add_argument('--dump-interfaces', nargs='*', default=None) args, unkown = parent_parser.parse_known_args(shlex.split(parent_cfg)) @@ -699,7 +704,7 @@ To see more detailed info on interfaces (table): help=argparse.SUPPRESS ) - parser.add_argument('--dump-pci-description', help='suppress', dest='dump_pci_desc', action='store_true') + parser.add_argument('--dump-pci-description', help=argparse.SUPPRESS, dest='dump_pci_desc', action='store_true') parser.add_argument("-i", "--interactive", action='store_true', help=""" Create TRex config in interactive mode """, diff --git a/src/dpdk/drivers/net/virtio/virtio_ethdev.c b/src/dpdk/drivers/net/virtio/virtio_ethdev.c index 07d64497..35e67b90 100644 --- a/src/dpdk/drivers/net/virtio/virtio_ethdev.c +++ b/src/dpdk/drivers/net/virtio/virtio_ethdev.c @@ -1550,6 +1550,8 @@ virtio_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) dev_info->default_txconf = (struct rte_eth_txconf) { .txq_flags = ETH_TXQ_FLAGS_NOOFFLOADS }; + /* TRex patch */ + dev_info->speed_capa = ETH_LINK_SPEED_10G; } /* diff --git a/src/dpdk/drivers/net/vmxnet3/vmxnet3_ethdev.c b/src/dpdk/drivers/net/vmxnet3/vmxnet3_ethdev.c index 58742153..47fdc3ec 100644 --- a/src/dpdk/drivers/net/vmxnet3/vmxnet3_ethdev.c +++ b/src/dpdk/drivers/net/vmxnet3/vmxnet3_ethdev.c @@ -706,6 +706,8 @@ vmxnet3_dev_info_get(__attribute__((unused))struct rte_eth_dev *dev, dev_info->min_rx_bufsize = 1518 + RTE_PKTMBUF_HEADROOM; dev_info->max_rx_pktlen = 16384; /* includes CRC, cf MAXFRS register */ dev_info->max_mac_addrs = VMXNET3_MAX_MAC_ADDRS; + /* TRex patch */ + dev_info->speed_capa = ETH_LINK_SPEED_10G; dev_info->default_txconf.txq_flags = ETH_TXQ_FLAGS_NOXSUMSCTP; dev_info->flow_type_rss_offloads = VMXNET3_RSS_OFFLOAD_ALL; diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index 998b6d49..0141186e 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -28,6 +28,7 @@ limitations under the License. #include #include "flow_stat_parser.h" #include "trex_defs.h" +#include "trex_port_attr.h" #include /** @@ -136,6 +137,7 @@ public: virtual void get_interface_info(uint8_t interface_id, intf_info_st &info) const = 0; virtual void publish_async_data_now(uint32_t key, bool baseline) const = 0; + virtual void publish_async_port_attr_changed(uint8_t port_id) 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_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset @@ -148,22 +150,15 @@ public: , uint8_t ipv6_next_h, uint16_t id) const = 0; virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto , uint8_t ipv6_next_h, uint16_t id) const = 0; - virtual int set_promiscuous(uint8_t port_id, bool enabled) const = 0; - virtual int set_link_status(uint8_t port_id, bool up) const = 0; - virtual bool get_promiscuous(uint8_t port_id) const = 0; - virtual bool get_link_status(uint8_t port_id) const = 0; virtual void flush_dp_messages() const = 0; virtual int get_active_pgids(flow_stat_active_t &result) const = 0; virtual int get_cpu_util_full(cpu_util_full_t &result) const = 0; virtual int get_mbuf_util(Json::Value &result) const = 0; virtual CFlowStatParser *get_flow_stat_parser() const = 0; + virtual TRexPortAttr *getPortAttrObj() const = 0; virtual void mark_for_shutdown() const = 0; virtual int get_xstats_values(uint8_t port_id, xstats_values_t &xstats_values) const = 0; virtual int get_xstats_names(uint8_t port_id, xstats_names_t &xstats_names) const = 0; - virtual int get_flow_ctrl(uint8_t port_id, int &mode) const = 0; - virtual int set_flow_ctrl(uint8_t port_id, int mode) const = 0; - virtual int set_led_status(uint8_t port_id, bool on) const = 0; - virtual uint32_t get_link_speed(uint8_t port_id) const = 0; virtual ~TrexPlatformApi() {} }; @@ -183,6 +178,7 @@ public: void get_interface_info(uint8_t interface_id, intf_info_st &info) const; void publish_async_data_now(uint32_t key, bool baseline) const; + void publish_async_port_attr_changed(uint8_t port_id) 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_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset @@ -195,23 +191,16 @@ public: , uint8_t ipv6_next_h, uint16_t id) const; virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto , uint8_t ipv6_next_h, uint16_t id) const; - int set_promiscuous(uint8_t port_id, bool enabled) const; - int set_link_status(uint8_t port_id, bool up) const; - bool get_promiscuous(uint8_t port_id) const; - bool get_link_status(uint8_t port_id) const; void flush_dp_messages() const; int get_active_pgids(flow_stat_active_t &result) const; int get_cpu_util_full(cpu_util_full_t &result) const; int get_mbuf_util(Json::Value &result) const; void mark_for_shutdown() const; CFlowStatParser *get_flow_stat_parser() const; + TRexPortAttr *getPortAttrObj() const; int get_xstats_values(uint8_t port_id, xstats_values_t &xstats_values) const; int get_xstats_names(uint8_t port_id, xstats_names_t &xstats_names) const; - int get_flow_ctrl(uint8_t port_id, int &mode) const; - int set_flow_ctrl(uint8_t port_id, int mode) const; - int set_led_status(uint8_t port_id, bool on) const; - uint32_t get_link_speed(uint8_t port_id) const; }; @@ -256,6 +245,10 @@ public: virtual void publish_async_data_now(uint32_t key, bool baseline) const { } + + virtual void publish_async_port_attr_changed(uint8_t port_id) const { + } + int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const {return 0;}; virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const {return 0;}; @@ -266,18 +259,6 @@ public: , uint8_t ipv6_next_h, uint16_t id) const {return 0;}; virtual int del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type, uint8_t l4_proto , uint8_t ipv6_next_h, uint16_t id) const {return 0;}; - int set_promiscuous(uint8_t port_id, bool enabled) const { - return 0; - } - int set_link_status(uint8_t port_id, bool on) const { - return 0; - } - bool get_promiscuous(uint8_t port_id) const { - return false; - } - bool get_link_status(uint8_t port_id) const { - return false; - } void flush_dp_messages() const { } @@ -285,17 +266,11 @@ public: int get_cpu_util_full(cpu_util_full_t &result) const {return 0;} int get_mbuf_util(Json::Value &result) const {return 0;} CFlowStatParser *get_flow_stat_parser() const {return new CFlowStatParser();} + TRexPortAttr *getPortAttrObj() const {printf("Port attributes should not be used in simulation\n"); return NULL;} // dummy void mark_for_shutdown() const {} int get_xstats_values(uint8_t port_id, xstats_values_t &xstats_values) const {return 0;}; int get_xstats_names(uint8_t port_id, xstats_names_t &xstats_names) const {return 0;}; - int get_flow_ctrl(uint8_t port_id, int &mode) const {return 0;}; - int set_flow_ctrl(uint8_t port_id, int mode) const {return 0;}; - int set_led_status(uint8_t port_id, bool on) const {return 0;}; - uint32_t get_link_speed(uint8_t port_id) const { - return 0; - } - private: int m_dp_core_count; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 9ef08cf1..cd4c782b 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -159,7 +159,7 @@ public: virtual void clear_extended_stats(CPhyEthIF * _if)=0; virtual int wait_for_stable_link(); virtual void wait_after_link_up(); - virtual bool flow_control_disable_supported(){return true;} + virtual bool flow_control_disable_supported(){return m_port_attr->is_fc_change_supported();} virtual bool hw_rx_stat_supported(){return false;} virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes , int min, int max) {return -1;} @@ -178,7 +178,7 @@ class CTRexExtendedDriverBase1G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase1G(){ - m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false); + m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false, true); } static CTRexExtendedDriverBase * create(){ @@ -224,7 +224,7 @@ public: CTRexExtendedDriverBase1GVm(){ /* we are working in mode that we have 1 queue for rx and one queue for tx*/ CGlobalInfo::m_options.preview.set_vm_one_queue_enable(true); - m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), true); + m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), true, true); } virtual bool has_crc_added() { @@ -270,7 +270,7 @@ public: class CTRexExtendedDriverBase10G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase10G(){ - m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false); + m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false, true); } static CTRexExtendedDriverBase * create(){ @@ -312,7 +312,8 @@ public: // If we want to support more counters in case of card having less interfaces, we // Will have to identify the number of interfaces dynamically. m_if_per_card = 4; - m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false); + // disabling flow control on 40G using DPDK API causes the interface to malfunction + m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false, false); } static CTRexExtendedDriverBase * create(){ @@ -342,8 +343,6 @@ public: return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual int wait_for_stable_link(); - // disabling flow control on 40G using DPDK API causes the interface to malfunction - virtual bool flow_control_disable_supported(){return false;} virtual bool hw_rx_stat_supported(){return true;} virtual int verify_fw_ver(int i); virtual CFlowStatParser *get_flow_stat_parser(); @@ -362,7 +361,7 @@ private: class CTRexExtendedDriverBaseVIC : public CTRexExtendedDriverBase40G { public: CTRexExtendedDriverBaseVIC(){ - m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false); + m_port_attr = new TRexPortAttr(global_platform_cfg_info.m_if_list.size(), false, false); } static CTRexExtendedDriverBase * create(){ @@ -375,8 +374,6 @@ public: virtual int verify_fw_ver(int i) {return 0;} - bool flow_control_disable_supported(){return false;} - virtual void update_configuration(port_cfg_t * cfg); }; @@ -599,6 +596,7 @@ static CSimpleOpt::SOption parser_options[] = { OPT_NO_FLOW_CONTROL, "--no-flow-control-change", SO_NONE }, { OPT_VLAN, "--vlan", SO_NONE }, { OPT_CLIENT_CFG_FILE, "--client_cfg", SO_REQ_SEP }, + { OPT_CLIENT_CFG_FILE, "--client-cfg", SO_REQ_SEP }, { OPT_NO_KEYBOARD_INPUT ,"--no-key", SO_NONE }, { OPT_VIRT_ONE_TX_RX_QUEUE, "--vm-sim", SO_NONE }, { OPT_PREFIX, "--prefix", SO_REQ_SEP }, @@ -1453,8 +1451,11 @@ void CPhyEthIF::disable_flow_control(){ ret, m_port_id); } -// Get user frienly devices description from saved env. var -void TRexPortAttr::update_info(){ +/* +Get user frienly devices description from saved env. var +Changes certain attributes based on description +*/ +void TRexPortAttr::update_descriptions(){ struct rte_pci_addr pci_addr; char pci[16]; char * envvar; @@ -1472,6 +1473,13 @@ void TRexPortAttr::update_info(){ } else { intf_info_st[port_id].description = "Unknown"; } + if (intf_info_st[port_id].description.find("82599ES") != std::string::npos) { // works for 82599EB etc. DPDK does not distinguish them + flag_is_link_change_supported = false; + } + if (intf_info_st[port_id].description.find("82545EM") != std::string::npos) { // in virtual E1000, DPDK claims fc is supported, but it's not + flag_is_fc_change_supported = false; + flag_is_led_change_supported = false; + } if ( CGlobalInfo::m_options.preview.getVMode() > 0){ printf("port %d desc: %s\n", port_id, intf_info_st[port_id].description.c_str()); } @@ -1486,21 +1494,21 @@ int TRexPortAttr::set_led(uint8_t port_id, bool on){ } } -int TRexPortAttr::get_flow_ctrl(uint8_t port_id, enum rte_eth_fc_mode *mode) { +int TRexPortAttr::get_flow_ctrl(uint8_t port_id, int &mode) { int ret = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf_tmp); if (ret) { return ret; } - *mode = fc_conf_tmp.mode; + mode = (int) fc_conf_tmp.mode; return 0; } -int TRexPortAttr::set_flow_ctrl(uint8_t port_id, const enum rte_eth_fc_mode mode) { +int TRexPortAttr::set_flow_ctrl(uint8_t port_id, int mode) { int ret = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf_tmp); if (ret) { return ret; } - fc_conf_tmp.mode = mode; + fc_conf_tmp.mode = (enum rte_eth_fc_mode) mode; return rte_eth_dev_flow_ctrl_set(port_id, &fc_conf_tmp); } @@ -1558,12 +1566,38 @@ void TRexPortAttr::dump_link(uint8_t port_id, FILE *fd){ fprintf(fd,"promiscuous : %d \n",get_promiscuous(port_id)); } +void TRexPortAttr::update_device_info(uint8_t port_id){ + rte_eth_dev_info_get(port_id, &dev_info[port_id]); +} + +void TRexPortAttr::get_supported_speeds(uint8_t port_id, supp_speeds_t &supp_speeds){ + uint32_t speed_capa = dev_info[port_id].speed_capa; + if (speed_capa & ETH_LINK_SPEED_1G) + supp_speeds.push_back(ETH_SPEED_NUM_1G); + if (speed_capa & ETH_LINK_SPEED_10G) + supp_speeds.push_back(ETH_SPEED_NUM_10G); + if (speed_capa & ETH_LINK_SPEED_40G) + supp_speeds.push_back(ETH_SPEED_NUM_40G); + if (speed_capa & ETH_LINK_SPEED_100G) + supp_speeds.push_back(ETH_SPEED_NUM_100G); +} + void TRexPortAttr::update_link_status(uint8_t port_id){ rte_eth_link_get(port_id, &m_link[port_id]); } -void TRexPortAttr::update_link_status_nowait(uint8_t port_id){ - rte_eth_link_get_nowait(port_id, &m_link[port_id]); +bool TRexPortAttr::update_link_status_nowait(uint8_t port_id){ + rte_eth_link new_link; + bool changed = false; + rte_eth_link_get_nowait(port_id, &new_link); + if (new_link.link_speed != m_link[port_id].link_speed || + new_link.link_duplex != m_link[port_id].link_duplex || + new_link.link_autoneg != m_link[port_id].link_autoneg || + new_link.link_status != m_link[port_id].link_status) { + changed = true; + } + m_link[port_id] = new_link; + return changed; } int TRexPortAttr::add_mac(uint8_t port_id, char * mac){ @@ -2941,6 +2975,7 @@ public: void publish_async_data(bool sync_now, bool baseline = false); void publish_async_barrier(uint32_t key); + void publish_async_port_attr_changed(uint8_t port_id); void dump_stats(FILE *fd, CGlobalStats::DumpFormat format); @@ -3364,7 +3399,7 @@ int CGlobalTRex::ixgbe_start(void){ /* wait for ports to be stable */ get_ex_drv()->wait_for_stable_link(); - if ( !is_all_links_are_up(true) && !get_is_stateless()){ + if ( !is_all_links_are_up(true) /*&& !get_is_stateless()*/ ){ // disable start with link down for now dump_links_status(stdout); rte_exit(EXIT_FAILURE, " " " one of the link is down \n"); @@ -3597,7 +3632,6 @@ int CGlobalTRex::ixgbe_prob_init(void){ CTRexExtendedDriverDb::Ins()->set_driver_name(dev_info.driver_name); m_drv = CTRexExtendedDriverDb::Ins()->get_drv(); m_port_attr = m_drv->m_port_attr; - m_port_attr->update_info(); // check if firmware version is new enough for (i = 0; i < m_max_ports; i++) { @@ -4120,6 +4154,24 @@ CGlobalTRex::publish_async_barrier(uint32_t key) { m_zmq_publisher.publish_barrier(key); } +void +CGlobalTRex:: publish_async_port_attr_changed(uint8_t port_id) { + Json::Value data; + data["port_id"] = port_id; + + /* attributes */ + data["attr"]["speed"] = m_port_attr->get_link_speed(port_id); + data["attr"]["promiscuous"]["enabled"] = m_port_attr->get_promiscuous(port_id); + data["attr"]["link"]["up"] = m_port_attr->is_link_up(port_id); + int mode; + int ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->get_flow_ctrl(port_id, mode); + if (ret != 0) { + mode = -1; + } + data["attr"]["fc"]["mode"] = mode; + + m_zmq_publisher.publish_event(TrexPublisher::EVENT_PORT_ATTR_CHANGED, data); +} void CGlobalTRex::handle_slow_path() { @@ -4127,7 +4179,10 @@ CGlobalTRex::handle_slow_path() { // update speed, link up/down etc. for (int i=0; iupdate_link_status_nowait(i); + bool changed = m_port_attr->update_link_status_nowait(i); + if (changed) { + publish_async_port_attr_changed(i); + } } if ( CGlobalInfo::m_options.preview.get_no_keyboard() ==false ) { @@ -6334,12 +6389,6 @@ int TrexDpdkPlatformApi::get_xstats_names(uint8_t port_id, xstats_names_t &xstat return g_trex.m_port_attr->get_xstats_names(port_id, xstats_names); } -int TrexDpdkPlatformApi::get_flow_ctrl(uint8_t port_id, int &mode) const{ - return g_trex.m_port_attr->get_flow_ctrl(port_id, (enum rte_eth_fc_mode *) &mode); -} -int TrexDpdkPlatformApi::set_flow_ctrl(uint8_t port_id, int mode) const { - return g_trex.m_port_attr->set_flow_ctrl(port_id, (enum rte_eth_fc_mode) mode); -} void TrexDpdkPlatformApi::get_port_num(uint8_t &port_num) const { port_num = g_trex.m_max_ports; @@ -6386,10 +6435,6 @@ TrexDpdkPlatformApi::port_id_to_cores(uint8_t port_id, std::vectorget_core_list(); } -uint32_t -TrexDpdkPlatformApi::get_link_speed(uint8_t port_id) const { - return g_trex.m_port_attr->get_link_speed(port_id); -} void TrexDpdkPlatformApi::get_interface_info(uint8_t interface_id, intf_info_st &info) const { @@ -6409,6 +6454,7 @@ TrexDpdkPlatformApi::get_interface_info(uint8_t interface_id, intf_info_st &info memcpy(sw_macaddr, CGlobalInfo::m_options.get_dst_src_mac_addr(interface_id), 12); for (int i = 0; i < 6; i++) { + info.mac_info.hw_macaddr[i] = rte_mac_addr.addr_bytes[i]; info.mac_info.dst_macaddr[i] = sw_macaddr[i]; info.mac_info.src_macaddr[i] = sw_macaddr[6 + i]; @@ -6429,6 +6475,11 @@ TrexDpdkPlatformApi::publish_async_data_now(uint32_t key, bool baseline) const { g_trex.publish_async_barrier(key); } +void +TrexDpdkPlatformApi::publish_async_port_attr_changed(uint8_t port_id) const { + g_trex.publish_async_port_attr_changed(port_id); +} + 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(); @@ -6470,27 +6521,6 @@ int TrexDpdkPlatformApi::del_rx_flow_stat_rule(uint8_t port_id, uint16_t l3_type ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_DELETE, l3_type, l4_proto, ipv6_next_h, id); } -int TrexDpdkPlatformApi::set_promiscuous(uint8_t port_id, bool enabled) const { - g_trex.m_port_attr->set_promiscuous(port_id, enabled); - return 0; -} - -bool TrexDpdkPlatformApi::get_promiscuous(uint8_t port_id) const { - return g_trex.m_port_attr->get_promiscuous(port_id); -} - -int TrexDpdkPlatformApi::set_link_status(uint8_t port_id, bool up) const { - return g_trex.m_port_attr->set_link_up(port_id, up); -} - -bool TrexDpdkPlatformApi::get_link_status(uint8_t port_id) const { - return g_trex.m_port_attr->is_link_up(port_id); -} - -int TrexDpdkPlatformApi::set_led_status(uint8_t port_id, bool on) const { - return g_trex.m_port_attr->set_led(port_id, on); -} - void TrexDpdkPlatformApi::flush_dp_messages() const { g_trex.check_for_dp_messages(); } @@ -6530,6 +6560,10 @@ CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const { return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser(); } +TRexPortAttr *TrexDpdkPlatformApi::getPortAttrObj() const { + return g_trex.m_port_attr; +} + /** * marks the control plane for a total server shutdown * diff --git a/src/pal/linux/rte_ethdev_includes.h b/src/pal/linux/rte_ethdev_includes.h new file mode 100644 index 00000000..de115d77 --- /dev/null +++ b/src/pal/linux/rte_ethdev_includes.h @@ -0,0 +1,23 @@ +struct rte_eth_link { + int link_autoneg; + int link_speed; + int link_duplex; + int link_status; +}; + + +enum rte_eth_fc_mode { +}; + +struct rte_eth_xstat { +}; + +struct rte_eth_xstat_name { +}; + +struct rte_eth_fc_conf { +}; + +struct rte_eth_dev_info { +}; + diff --git a/src/pal/linux_dpdk/rte_ethdev_includes.h b/src/pal/linux_dpdk/rte_ethdev_includes.h new file mode 100644 index 00000000..025ce8fc --- /dev/null +++ b/src/pal/linux_dpdk/rte_ethdev_includes.h @@ -0,0 +1 @@ +#include diff --git a/src/publisher/trex_publisher.h b/src/publisher/trex_publisher.h index fb7226c4..522c8c69 100644 --- a/src/publisher/trex_publisher.h +++ b/src/publisher/trex_publisher.h @@ -49,6 +49,7 @@ public: EVENT_PORT_ACQUIRED = 5, EVENT_PORT_RELEASED = 6, EVENT_PORT_ERROR = 7, + EVENT_PORT_ATTR_CHANGED = 8, EVENT_SERVER_STOPPED = 100, diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index f6b088a5..e4510a0a 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -295,6 +295,8 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { string src_macaddr; string dst_macaddr; string pci_addr; + string description; + supp_speeds_t supp_speeds; int numa; TrexStatelessPort *port = main->get_port_by_id(i); @@ -302,10 +304,13 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { port->get_macaddr(hw_macaddr, src_macaddr, dst_macaddr); port->get_pci_info(pci_addr, numa); + main->get_platform_api()->getPortAttrObj()->get_description(i, description); + main->get_platform_api()->getPortAttrObj()->get_supported_speeds(i, supp_speeds); section["ports"][i]["index"] = i; section["ports"][i]["driver"] = driver; + section["ports"][i]["description"] = description; section["ports"][i]["hw_macaddr"] = hw_macaddr; section["ports"][i]["src_macaddr"] = src_macaddr; section["ports"][i]["dst_macaddr"] = dst_macaddr; @@ -326,6 +331,14 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { } section["ports"][i]["rx"]["counters"] = port->get_rx_count_num(); section["ports"][i]["speed"] = (uint16_t) speed / 1000; + section["ports"][i]["is_fc_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->is_fc_change_supported(); + section["ports"][i]["is_led_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->is_led_change_supported(); + section["ports"][i]["is_link_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->is_link_change_supported(); + section["ports"][i]["is_virtual"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->is_virtual(); + section["ports"][i]["supp_speeds"] = Json::arrayValue; + for (int speed_id=0; speed_idget_platform_api()->set_promiscuous(port_id, enabled); + ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->set_promiscuous(port_id, enabled); } else if (name == "link_status") { bool up = parse_bool(attr[name], "up", result); - ret = get_stateless_obj()->get_platform_api()->set_link_status(port_id, up); + ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->set_link_up(port_id, up); } else if (name == "led_status") { bool on = parse_bool(attr[name], "on", result); - ret = get_stateless_obj()->get_platform_api()->set_led_status(port_id, on); + ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->set_led(port_id, on); } else if (name == "flow_ctrl_mode") { int mode = parse_int(attr[name], "mode", result); - ret = get_stateless_obj()->get_platform_api()->set_flow_ctrl(port_id, mode); + ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->set_flow_ctrl(port_id, mode); } else { generate_execute_err(result, "Not recognized attribute: " + name); break; @@ -376,8 +390,13 @@ TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { else if (ret) { generate_execute_err(result, "Error applying " + name + " attribute, return value: " + to_string(ret)); } + break; + } else { + changed = true; } - break; + } + if (changed) { + get_stateless_obj()->get_platform_api()->publish_async_port_attr_changed(port_id); } result["result"] = Json::objectValue; @@ -549,9 +568,17 @@ TrexRpcCmdGetPortStatus::_run(const Json::Value ¶ms, Json::Value &result) { result["result"]["owner"] = (port->get_owner().is_free() ? "" : port->get_owner().get_name()); result["result"]["state"] = port->get_state_as_string(); result["result"]["max_stream_id"] = port->get_max_stream_id(); + result["result"]["speed"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->get_link_speed(port_id); /* attributes */ - result["result"]["attr"]["promiscuous"]["enabled"] = get_stateless_obj()->get_platform_api()->get_promiscuous(port_id); + result["result"]["attr"]["promiscuous"]["enabled"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->get_promiscuous(port_id); + result["result"]["attr"]["link"]["up"] = get_stateless_obj()->get_platform_api()->getPortAttrObj()->is_link_up(port_id); + int mode; + int ret = get_stateless_obj()->get_platform_api()->getPortAttrObj()->get_flow_ctrl(port_id, mode); + if (ret != 0) { + mode = -1; + } + result["result"]["attr"]["fc"]["mode"] = mode; return (TREX_RPC_CMD_OK); } diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index 8c6a1573..1a92b309 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -255,6 +255,11 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, /* caclulate the effective factor for DP */ double factor = calculate_effective_factor(mul, force); + /* zero factor */ + if (factor == 0) { + throw TrexException("Zero multiplier, nothing to send."); + } + StreamsFeeder feeder(this); /* compiler it */ @@ -278,7 +283,7 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, feeder.set_status(true); - /* generate a message to all the relevant DP cores to start transmitting */ + /* generate a message to all the relevant DP cores to stop transmitting */ assert(m_pending_async_stop_event == TrexDpPortEvents::INVALID_ID); m_pending_async_stop_event = m_dp_events.create_event(new AsyncStopEvent()); @@ -581,7 +586,7 @@ void TrexStatelessPort::get_properties(std::string &driver, uint32_t &speed) { driver = m_api_info.driver_name; - speed = platform_api->get_link_speed(m_port_id); + speed = platform_api->getPortAttrObj()->get_link_speed(m_port_id); } bool @@ -662,7 +667,7 @@ TrexStatelessPort::send_message_to_rx(TrexStatelessCpToRxMsgBase *msg) { uint64_t TrexStatelessPort::get_port_speed_bps() const { - return (uint64_t) platform_api->get_link_speed(m_port_id) * 1000 * 1000; + return (uint64_t) platform_api->getPortAttrObj()->get_link_speed(m_port_id) * 1000 * 1000; } static inline double diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index c944df4a..43fee09b 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -309,7 +309,7 @@ public: } double get_factor_pps(double req_pps) const { - if ( (req_pps - m_fixed.m_pps) <= 0 ) { + if ( (req_pps - m_fixed.m_pps) < 0 ) { std::stringstream ss; ss << "current stream configuration enforces a minimum rate of '" << m_fixed.m_pps << "' pps"; throw TrexException(ss.str()); @@ -319,7 +319,7 @@ public: } double get_factor_bps_l1(double req_bps_l1) const { - if ( (req_bps_l1 - m_fixed.m_bps_l1) <= 0 ) { + if ( (req_bps_l1 - m_fixed.m_bps_l1) < 0 ) { std::stringstream ss; ss << "current stream configuration enforces a minimum rate of '" << m_fixed.m_bps_l1 << "' BPS L1"; throw TrexException(ss.str()); @@ -329,7 +329,7 @@ public: } double get_factor_bps_l2(double req_bps_l2) const { - if ( (req_bps_l2 - m_fixed.m_bps_l2) <= 0 ) { + if ( (req_bps_l2 - m_fixed.m_bps_l2) < 0 ) { std::stringstream ss; ss << "current stream configuration enforces a minimum rate of '" << m_fixed.m_bps_l2 << "' BPS L2"; throw TrexException(ss.str()); diff --git a/src/trex_defs.h b/src/trex_defs.h index d8139a83..8a4bf664 100644 --- a/src/trex_defs.h +++ b/src/trex_defs.h @@ -55,5 +55,6 @@ typedef std::set::iterator flow_stat_active_it_t; typedef std::vector cpu_util_full_t; typedef std::vector xstats_names_t; typedef std::vector xstats_values_t; +typedef std::vector supp_speeds_t; #endif diff --git a/src/trex_port_attr.h b/src/trex_port_attr.h index 037a3de3..cc267104 100755 --- a/src/trex_port_attr.h +++ b/src/trex_port_attr.h @@ -19,23 +19,33 @@ limitations under the License. #include #include -#include +#include "rte_ethdev_includes.h" #include "trex_defs.h" class TRexPortAttr { public: - TRexPortAttr(uint8_t total_ports, bool is_virtual = false) : total_ports(total_ports) { + TRexPortAttr(uint8_t total_ports, bool is_virtual, bool fc_change_allowed) : total_ports(total_ports) { flag_is_virtual = is_virtual; m_link.resize(total_ports); intf_info_st.resize(total_ports); + dev_info.resize(total_ports); + int tmp; + flag_is_fc_change_supported = fc_change_allowed && (get_flow_ctrl(0, tmp) != -ENOTSUP); + flag_is_led_change_supported = (set_led(0, true) != -ENOTSUP); + flag_is_link_change_supported = (set_link_up(0, true) != -ENOTSUP); + update_descriptions(); + for (int i=0; i m_link; + std::vector m_link; + std::vector dev_info; std::vector xstats_values_tmp; std::vector xstats_names_tmp; + bool flag_is_virtual; + bool flag_is_fc_change_supported; + bool flag_is_led_change_supported; + bool flag_is_link_change_supported; struct mac_cfg_st { uint8_t hw_macaddr[6]; -- cgit 1.2.3-korg