/* Hanoh Haim Cisco Systems, Inc. */ /* Copyright (c) 2015-2016 Cisco Systems, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bp_sim.h" #include "os_time.h" #include "common/arg/SimpleGlob.h" #include "common/arg/SimpleOpt.h" #include "common/basic_utils.h" #include "stateless/cp/trex_stateless.h" #include "stateless/dp/trex_stream_node.h" #include "stateless/messaging/trex_stateless_messaging.h" #include "stateless/rx/trex_stateless_rx_core.h" #include "publisher/trex_publisher.h" #include "../linux_dpdk/version.h" extern "C" { #include "dpdk/drivers/net/ixgbe/base/ixgbe_type.h" #include "dpdk_funcs.h" } #include "dpdk/drivers/net/e1000/base/e1000_regs.h" #include "global_io_mode.h" #include "utl_term_io.h" #include "msg_manager.h" #include "platform_cfg.h" #include "pre_test.h" #include "stateful_rx_core.h" #include "debug.h" #include "pkt_gen.h" #include "trex_port_attr.h" #include "internal_api/trex_platform_api.h" #include "main_dpdk.h" #include "trex_watchdog.h" #define RX_CHECK_MIX_SAMPLE_RATE 8 #define RX_CHECK_MIX_SAMPLE_RATE_1G 2 #define SOCKET0 0 #define MAX_PKT_BURST 32 #define BP_MAX_CORES 32 #define BP_MAX_TX_QUEUE 16 #define BP_MASTER_AND_LATENCY 2 #define RTE_TEST_RX_DESC_DEFAULT 64 #define RTE_TEST_RX_LATENCY_DESC_DEFAULT (1*1024) #define RTE_TEST_RX_DESC_VM_DEFAULT 512 #define RTE_TEST_TX_DESC_VM_DEFAULT 512 typedef struct rte_mbuf * (*rte_mbuf_convert_to_one_seg_t)(struct rte_mbuf *m); struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m); extern "C" int rte_eth_dev_get_port_by_addr(const struct rte_pci_addr *addr, uint8_t *port_id); void reorder_dpdk_ports(); #define RTE_TEST_TX_DESC_DEFAULT 512 #define RTE_TEST_RX_DESC_DROP 0 static int max_stat_hw_id_seen = 0; static int max_stat_hw_id_seen_payload = 0; static inline int get_vm_one_queue_enable(){ return (CGlobalInfo::m_options.preview.get_vm_one_queue_enable() ?1:0); } static inline int get_is_rx_thread_enabled() { return ((CGlobalInfo::m_options.is_rx_enabled() || CGlobalInfo::m_options.is_stateless()) ?1:0); } struct port_cfg_t; #define MAX_DPDK_ARGS 40 static CPlatformYamlInfo global_platform_cfg_info; static int global_dpdk_args_num ; static char * global_dpdk_args[MAX_DPDK_ARGS]; static char global_cores_str[100]; static char global_prefix_str[100]; static char global_loglevel_str[20]; static char global_master_id_str[10]; class CTRexExtendedDriverBase { public: /* by default NIC driver adds CRC */ virtual bool has_crc_added() { return true; } virtual int get_min_sample_rate(void)=0; virtual void update_configuration(port_cfg_t * cfg)=0; virtual void update_global_config_fdir(port_cfg_t * cfg)=0; virtual bool is_hardware_filter_is_supported(){ return(false); } virtual int configure_rx_filter_rules(CPhyEthIF * _if)=0; virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3, uint8_t l4 , uint8_t ipv6_next_h, uint16_t id) {return -1;}; virtual bool is_hardware_support_drop_queue(){ return(false); } virtual int stop_queue(CPhyEthIF * _if, uint16_t q_num); virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats)=0; virtual void clear_extended_stats(CPhyEthIF * _if)=0; virtual int wait_for_stable_link(); virtual void wait_after_link_up(); 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;} virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) {} virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) { return -1;} virtual int get_stat_counters_num() {return 0;} virtual int get_rx_stat_capabilities() {return 0;} virtual int verify_fw_ver(int i) {return 0;} virtual CFlowStatParser *get_flow_stat_parser(); virtual int set_rcv_all(CPhyEthIF * _if, bool set_on)=0; virtual TRexPortAttr * create_port_attr(uint8_t port_id) = 0; }; class CTRexExtendedDriverBase1G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase1G(){ } TRexPortAttr * create_port_attr(uint8_t port_id) { return new DpdkTRexPortAttr(port_id, false, true); } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg); virtual int get_min_sample_rate(void){ return ( RX_CHECK_MIX_SAMPLE_RATE_1G); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int stop_queue(CPhyEthIF * _if, uint16_t q_num); virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual int configure_rx_filter_rules_statefull(CPhyEthIF * _if); virtual int configure_rx_filter_rules_stateless(CPhyEthIF * _if); virtual void clear_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); 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 | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual int wait_for_stable_link(); virtual void wait_after_link_up(); virtual int set_rcv_all(CPhyEthIF * _if, bool set_on); }; class CTRexExtendedDriverBase1GVm : public CTRexExtendedDriverBase { 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); } TRexPortAttr * create_port_attr(uint8_t port_id) { return new DpdkTRexPortAttr(port_id, true, true); } virtual bool has_crc_added() { return false; } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1GVm() ); } virtual void update_global_config_fdir(port_cfg_t * cfg){ } virtual int get_min_sample_rate(void){ return ( RX_CHECK_MIX_SAMPLE_RATE_1G); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(false); } virtual int stop_queue(CPhyEthIF * _if, uint16_t q_num); virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); 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 | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual int set_rcv_all(CPhyEthIF * _if, bool set_on) {return 0;} }; class CTRexExtendedDriverBase10G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase10G(){ } TRexPortAttr * create_port_attr(uint8_t port_id) { return new DpdkTRexPortAttr(port_id, false, true); } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase10G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg); virtual int get_min_sample_rate(void){ return (RX_CHECK_MIX_SAMPLE_RATE); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual int configure_rx_filter_rules_stateless(CPhyEthIF * _if); virtual int configure_rx_filter_rules_statefull(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); 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 | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual CFlowStatParser *get_flow_stat_parser(); virtual int set_rcv_all(CPhyEthIF * _if, bool set_on) {return 0;} }; class CTRexExtendedDriverBase40G : public CTRexExtendedDriverBase10G { public: CTRexExtendedDriverBase40G(){ // Since we support only 128 counters per if, it is OK to configure here 4 statically. // If we want to support more counters in case of card having less interfaces, we // Will have to identify the number of interfaces dynamically. m_if_per_card = 4; } TRexPortAttr * create_port_attr(uint8_t port_id) { // disabling flow control on 40G using DPDK API causes the interface to malfunction return new DpdkTRexPortAttr(port_id, false, false); } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase40G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg){ } virtual void update_configuration(port_cfg_t * cfg); virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3_proto , uint8_t l4_proto, uint8_t ipv6_next_h, uint16_t id); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len); 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); virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd); virtual int get_stat_counters_num() {return MAX_FLOW_STATS;} virtual int get_rx_stat_capabilities() { return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual int wait_for_stable_link(); virtual bool hw_rx_stat_supported(){return true;} virtual int verify_fw_ver(int i); virtual CFlowStatParser *get_flow_stat_parser(); virtual int set_rcv_all(CPhyEthIF * _if, bool set_on); private: virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl , uint16_t ip_id, uint16_t l4_proto, int queue, uint16_t stat_idx); virtual int add_del_eth_type_rule(uint8_t port_id, enum rte_filter_op op, uint16_t eth_type); virtual int configure_rx_filter_rules_statefull(CPhyEthIF * _if); private: uint8_t m_if_per_card; }; class CTRexExtendedDriverBaseVIC : public CTRexExtendedDriverBase40G { public: CTRexExtendedDriverBaseVIC(){ } TRexPortAttr * create_port_attr(uint8_t port_id) { return new DpdkTRexPortAttr(port_id, false, false); } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBaseVIC() ); } virtual bool is_hardware_filter_is_supported(){ return (false); } virtual int verify_fw_ver(int i) {return 0;} virtual void update_configuration(port_cfg_t * cfg); }; typedef CTRexExtendedDriverBase * (*create_object_t) (void); class CTRexExtendedDriverRec { public: std::string m_driver_name; create_object_t m_constructor; }; class CTRexExtendedDriverDb { public: const std::string & get_driver_name() { return m_driver_name; } bool is_driver_exists(std::string name); void set_driver_name(std::string name){ m_driver_was_set=true; m_driver_name=name; printf(" set driver name %s \n",name.c_str()); m_drv=create_driver(m_driver_name); assert(m_drv); } CTRexExtendedDriverBase * get_drv(){ if (!m_driver_was_set) { printf(" ERROR too early to use this object !\n"); printf(" need to set the right driver \n"); assert(0); } assert(m_drv); return (m_drv); } public: static CTRexExtendedDriverDb * Ins(); private: CTRexExtendedDriverBase * create_driver(std::string name); CTRexExtendedDriverDb(){ register_driver(std::string("rte_ixgbe_pmd"),CTRexExtendedDriverBase10G::create); register_driver(std::string("rte_igb_pmd"),CTRexExtendedDriverBase1G::create); register_driver(std::string("rte_i40e_pmd"),CTRexExtendedDriverBase40G::create); register_driver(std::string("rte_enic_pmd"),CTRexExtendedDriverBaseVIC::create); /* virtual devices */ register_driver(std::string("rte_em_pmd"),CTRexExtendedDriverBase1GVm::create); register_driver(std::string("rte_vmxnet3_pmd"),CTRexExtendedDriverBase1GVm::create); register_driver(std::string("rte_virtio_pmd"),CTRexExtendedDriverBase1GVm::create); m_driver_was_set=false; m_drv=0; m_driver_name=""; } void register_driver(std::string name,create_object_t func); static CTRexExtendedDriverDb * m_ins; bool m_driver_was_set; std::string m_driver_name; CTRexExtendedDriverBase * m_drv; std::vector m_list; }; CTRexExtendedDriverDb * CTRexExtendedDriverDb::m_ins; void CTRexExtendedDriverDb::register_driver(std::string name, create_object_t func){ CTRexExtendedDriverRec * rec; rec = new CTRexExtendedDriverRec(); rec->m_driver_name=name; rec->m_constructor=func; m_list.push_back(rec); } bool CTRexExtendedDriverDb::is_driver_exists(std::string name){ int i; for (i=0; i<(int)m_list.size(); i++) { if (m_list[i]->m_driver_name == name) { return (true); } } return (false); } CTRexExtendedDriverBase * CTRexExtendedDriverDb::create_driver(std::string name){ int i; for (i=0; i<(int)m_list.size(); i++) { if (m_list[i]->m_driver_name == name) { return ( m_list[i]->m_constructor() ); } } return( (CTRexExtendedDriverBase *)0); } CTRexExtendedDriverDb * CTRexExtendedDriverDb::Ins(){ if (!m_ins) { m_ins = new CTRexExtendedDriverDb(); } return (m_ins); } static CTRexExtendedDriverBase * get_ex_drv(){ return ( CTRexExtendedDriverDb::Ins()->get_drv()); } static inline int get_min_sample_rate(void){ return ( get_ex_drv()->get_min_sample_rate()); } // cores =0==1,1*2,2,3,4,5,6 // An enum for all the option types enum { OPT_HELP, OPT_MODE_BATCH, OPT_MODE_INTERACTIVE, OPT_NODE_DUMP, OPT_DUMP_INTERFACES, OPT_UT, OPT_CORES, OPT_SINGLE_CORE, OPT_FLIP_CLIENT_SERVER, OPT_FLOW_FLIP_CLIENT_SERVER, OPT_FLOW_FLIP_CLIENT_SERVER_SIDE, OPT_RATE_MULT, OPT_DURATION, OPT_PLATFORM_FACTOR, OPT_PUB_DISABLE, OPT_LIMT_NUM_OF_PORTS, OPT_PLAT_CFG_FILE, OPT_MBUF_FACTOR, OPT_LATENCY, OPT_NO_CLEAN_FLOW_CLOSE, OPT_LATENCY_MASK, OPT_ONLY_LATENCY, OPT_LATENCY_PREVIEW , OPT_WAIT_BEFORE_TRAFFIC, OPT_PCAP, OPT_RX_CHECK, OPT_IO_MODE, OPT_IPV6, OPT_LEARN, OPT_LEARN_MODE, OPT_LEARN_VERIFY, OPT_L_PKT_MODE, OPT_NO_FLOW_CONTROL, OPT_VLAN, OPT_RX_CHECK_HOPS, OPT_CLIENT_CFG_FILE, OPT_NO_KEYBOARD_INPUT, OPT_VIRT_ONE_TX_RX_QUEUE, OPT_PREFIX, OPT_SEND_DEBUG_PKT, OPT_NO_WATCHDOG, OPT_ALLOW_COREDUMP, OPT_CHECKSUM_OFFLOAD, OPT_CLOSE, OPT_ARP_REF_PER, }; /* these are the argument types: SO_NONE -- no argument needed SO_REQ_SEP -- single required argument SO_MULTI -- multiple arguments needed */ static CSimpleOpt::SOption parser_options[] = { { OPT_HELP, "-?", SO_NONE }, { OPT_HELP, "-h", SO_NONE }, { OPT_HELP, "--help", SO_NONE }, { OPT_UT, "--ut", SO_NONE }, { OPT_MODE_BATCH, "-f", SO_REQ_SEP}, { OPT_MODE_INTERACTIVE, "-i", SO_NONE }, { OPT_PLAT_CFG_FILE, "--cfg", SO_REQ_SEP}, { OPT_SINGLE_CORE, "-s", SO_NONE }, { OPT_FLIP_CLIENT_SERVER,"--flip",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER,"-p",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER_SIDE,"-e",SO_NONE }, { OPT_NO_CLEAN_FLOW_CLOSE,"--nc",SO_NONE }, { OPT_LIMT_NUM_OF_PORTS,"--limit-ports", SO_REQ_SEP }, { OPT_CORES , "-c", SO_REQ_SEP }, { OPT_NODE_DUMP , "-v", SO_REQ_SEP }, { OPT_DUMP_INTERFACES , "--dump-interfaces", SO_MULTI }, { OPT_LATENCY , "-l", SO_REQ_SEP }, { OPT_DURATION , "-d", SO_REQ_SEP }, { OPT_PLATFORM_FACTOR , "-pm", SO_REQ_SEP }, { OPT_PUB_DISABLE , "-pubd", SO_NONE }, { OPT_RATE_MULT , "-m", SO_REQ_SEP }, { OPT_LATENCY_MASK , "--lm", SO_REQ_SEP }, { OPT_ONLY_LATENCY, "--lo", SO_NONE }, { OPT_LATENCY_PREVIEW , "-k", SO_REQ_SEP }, { OPT_WAIT_BEFORE_TRAFFIC , "-w", SO_REQ_SEP }, { OPT_PCAP, "--pcap", SO_NONE }, { OPT_RX_CHECK, "--rx-check", SO_REQ_SEP }, { OPT_IO_MODE, "--iom", SO_REQ_SEP }, { OPT_RX_CHECK_HOPS, "--hops", SO_REQ_SEP }, { OPT_IPV6, "--ipv6", SO_NONE }, { OPT_LEARN, "--learn", SO_NONE }, { OPT_LEARN_MODE, "--learn-mode", SO_REQ_SEP }, { OPT_LEARN_VERIFY, "--learn-verify", SO_NONE }, { OPT_L_PKT_MODE, "--l-pkt-mode", SO_REQ_SEP }, { 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 }, { OPT_SEND_DEBUG_PKT, "--send-debug-pkt", SO_REQ_SEP }, { OPT_MBUF_FACTOR , "--mbuf-factor", SO_REQ_SEP }, { OPT_NO_WATCHDOG , "--no-watchdog", SO_NONE }, { OPT_ALLOW_COREDUMP , "--allow-coredump", SO_NONE }, { OPT_CHECKSUM_OFFLOAD, "--checksum-offload", SO_NONE }, { OPT_CLOSE, "--close-at-end", SO_NONE }, { OPT_ARP_REF_PER, "--arp-refresh-period", SO_REQ_SEP }, SO_END_OF_OPTIONS }; static int usage(){ printf(" Usage: t-rex-64 [mode] \n\n"); printf(" mode is one of:\n"); printf(" -f : YAML file with traffic template configuration (Will run TRex in 'stateful' mode)\n"); printf(" -i : Run TRex in 'stateless' mode\n"); printf("\n"); printf(" Available options are:\n"); printf(" --allow-coredump : Allow creation of core dump \n"); printf(" --arp-refresh-period : Period in seconds between sending of gratuitous ARP for our addresses. Value of 0 means 'never send' \n"); printf(" -c > : Number of hardware threads to allocate for each port pair. Overrides the 'c' argument from config file \n"); printf(" --cfg : Use file as TRex config file instead of the default /etc/trex_cfg.yaml \n"); printf(" --checksum-offload : Enable IP, TCP and UDP tx checksum offloading, using DPDK. This requires all used interfaces to support this \n"); printf(" --client_cfg : YAML file describing clients configuration \n"); printf(" --close-at-end : Call rte_eth_dev_stop and close at exit. Calling these functions caused link down issues in older versions, \n"); printf(" so we do not call them by default for now. Leaving this as option in case someone thinks it is helpful for him \n"); printf(" This it temporary option. Will be removed in the future \n"); printf(" -d : Duration of the test in sec (default is 3600). Look also at --nc \n"); printf(" -e : Like -p but src/dst IP will be chosen according to the port (i.e. on client port send all packets with client src and server dest, and vice versa on server port \n"); printf(" --flip : Each flow will be sent both from client to server and server to client. This can acheive better port utilization when flow traffic is asymmetric \n"); printf(" --hops : If rx check is enabled, the hop number can be assigned. See manual for details \n"); printf(" --iom : IO mode for server output [0- silent, 1- normal , 2- short] \n"); printf(" --ipv6 : Work in ipv6 mode \n"); printf(" -k : Run 'warm up' traffic for num seconds before starting the test. \n"); printf(" -l : In parallel to the test, run latency check, sending packets at rate/sec from each interface \n"); printf(" Rate of zero means no latency check \n"); printf(" --learn (deprecated). Replaced by --learn-mode. To get older behaviour, use --learn-mode 2 \n"); printf(" --learn-mode [1-3] : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be first packet in stream \n"); printf(" 2 Add special IP option to pass NAT translation information. Will not work on certain firewalls if they drop packets with IP options \n"); printf(" 3 Like 1, but without support for sequence number randomization in server->clien direction. Performance (flow/second) better than 1 \n"); printf(" --learn-verify : Test the NAT translation mechanism. Should be used when there is no NAT in the setup \n"); printf(" --limit-ports : Limit number of ports used. Must be even number (TRex always uses port pairs) \n"); printf(" --lm : Hex mask of cores that should send traffic \n"); printf(" For example: Value of 0x5 will cause only ports 0 and 2 to send traffic \n"); printf(" --lo : Only run latency test \n"); printf(" --l-pkt-mode <0-3> : Set mode for sending latency packets \n"); printf(" 0 (default) send SCTP packets \n"); printf(" 1 Send ICMP request packets \n"); printf(" 2 Send ICMP requests from client side, and response from server side (for working with firewall) \n"); printf(" 3 Send ICMP requests with sequence ID 0 from both sides \n"); printf(" -m : Rate multiplier. Multiply basic rate of templates by this number \n"); printf(" --mbuf-factor : Factor for packet memory \n"); printf(" --nc : If set, will not wait for all flows to be closed, before terminating - see manual for more information \n"); printf(" --no-flow-control-change : By default TRex disables flow-control. If this option is given, it does not touch it \n"); printf(" --no-key : Daemon mode, don't get input from keyboard \n"); printf(" --no-watchdog : Disable watchdog \n"); printf(" -p : Send all flow packets from the same interface (choosed randomly between client ad server ports) without changing their src/dst IP \n"); printf(" -pm : Platform factor. If you have splitter in the setup, you can multiply the total results by this factor \n"); printf(" e.g --pm 2.0 will multiply all the results bps in this factor \n"); printf(" --prefix : For running multi TRex instances on the same machine. Each instance should have different name \n"); printf(" -pubd : Disable monitors publishers \n"); printf(" --rx-check : Enable rx check. TRex will sample flows at 1/rate and check order, latency and more \n"); printf(" -s : Single core. Run only one data path core. For debug \n"); printf(" --send-debug-pkt : Do not run traffic generator. Just send debug packet and dump receive queues \n"); printf(" Supported protocols are 1 for icmp, 2 for UDP, 3 for TCP, 4 for ARP, 5 for 9K UDP \n"); printf(" -v : The higher the value, print more debug information \n"); printf(" --vlan : Relevant only for stateless mode with Intel 82599 10G NIC \n"); printf(" When configuring flow stat and latency per stream rules, assume all streams uses VLAN \n"); printf(" --vm-sim : Simulate vm with driver of one input queue and one output queue \n"); printf(" -w : Wait num seconds between init of interfaces and sending traffic, default is 1 \n"); printf("\n"); printf(" Examples: "); printf(" basic trex run for 20 sec and multiplier of 10 \n"); printf(" t-rex-64 -f cap2/dns.yaml -m 10 -d 20 \n"); printf("\n\n"); printf(" Copyright (c) 2015-2016 Cisco Systems, Inc. \n"); printf(" \n"); printf(" Licensed under the Apache License, Version 2.0 (the 'License') \n"); printf(" you may not use this file except in compliance with the License. \n"); printf(" You may obtain a copy of the License at \n"); printf(" \n"); printf(" http://www.apache.org/licenses/LICENSE-2.0 \n"); printf(" \n"); printf(" Unless required by applicable law or agreed to in writing, software \n"); printf(" distributed under the License is distributed on an \"AS IS\" BASIS, \n"); printf(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n"); printf(" See the License for the specific language governing permissions and \n"); printf(" limitations under the License. \n"); printf(" \n"); printf(" Open Source Components / Libraries \n"); printf(" DPDK (BSD) \n"); printf(" YAML-CPP (BSD) \n"); printf(" JSONCPP (MIT) \n"); printf(" \n"); printf(" Open Source Binaries \n"); printf(" ZMQ (LGPL v3plus) \n"); printf(" \n"); printf(" Version : %s \n",VERSION_BUILD_NUM); printf(" DPDK version : %s \n",rte_version()); printf(" User : %s \n",VERSION_USER); printf(" Date : %s , %s \n",get_build_date(),get_build_time()); printf(" Uuid : %s \n",VERSION_UIID); printf(" Git SHA : %s \n",VERSION_GIT_SHA); return (0); } int gtest_main(int argc, char **argv) ; static void parse_err(const std::string &msg) { std::cout << "\nArgument Parsing Error: \n\n" << "*** "<< msg << "\n\n"; exit(-1); } static int parse_options(int argc, char *argv[], CParserOption* po, bool first_time ) { CSimpleOpt args(argc, argv, parser_options); bool latency_was_set=false; (void)latency_was_set; char ** rgpszArg = NULL; int a=0; int node_dump=0; po->preview.setFileWrite(true); po->preview.setRealTime(true); uint32_t tmp_data; po->m_run_mode = CParserOption::RUN_MODE_INVALID; while ( args.Next() ){ if (args.LastError() == SO_SUCCESS) { switch (args.OptionId()) { case OPT_UT : parse_err("Supported only in simulation"); break; case OPT_HELP: usage(); return -1; case OPT_MODE_BATCH: if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { parse_err("Please specify single run mode"); } po->m_run_mode = CParserOption::RUN_MODE_BATCH; po->cfg_file = args.OptionArg(); break; case OPT_MODE_INTERACTIVE: if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { parse_err("Please specify single run mode"); } po->m_run_mode = CParserOption::RUN_MODE_INTERACTIVE; break; case OPT_NO_KEYBOARD_INPUT : po->preview.set_no_keyboard(true); break; case OPT_CLIENT_CFG_FILE : po->client_cfg_file = args.OptionArg(); break; case OPT_PLAT_CFG_FILE : po->platform_cfg_file = args.OptionArg(); break; case OPT_SINGLE_CORE : po->preview.setSingleCore(true); break; case OPT_IPV6: po->preview.set_ipv6_mode_enable(true); break; case OPT_LEARN : po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION; break; case OPT_LEARN_MODE : sscanf(args.OptionArg(),"%d", &tmp_data); if (! po->is_valid_opt_val(tmp_data, CParserOption::LEARN_MODE_DISABLED, CParserOption::LEARN_MODE_MAX, "--learn-mode")) { exit(-1); } po->m_learn_mode = (uint8_t)tmp_data; break; case OPT_LEARN_VERIFY : // must configure learn_mode for learn verify to work. If different learn mode will be given later, it will be set instead. if (po->m_learn_mode == 0) { po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION; } po->preview.set_learn_and_verify_mode_enable(true); break; case OPT_L_PKT_MODE : sscanf(args.OptionArg(),"%d", &tmp_data); if (! po->is_valid_opt_val(tmp_data, 0, L_PKT_SUBMODE_0_SEQ, "--l-pkt-mode")) { exit(-1); } po->m_l_pkt_mode=(uint8_t)tmp_data; break; case OPT_NO_FLOW_CONTROL: po->preview.set_disable_flow_control_setting(true); break; case OPT_VLAN: if ( get_is_stateless() ) { po->preview.set_vlan_mode_enable(true); } break; case OPT_LIMT_NUM_OF_PORTS : po->m_expected_portd =atoi(args.OptionArg()); break; case OPT_CORES : po->preview.setCores(atoi(args.OptionArg())); break; case OPT_FLIP_CLIENT_SERVER : po->preview.setClientServerFlip(true); break; case OPT_NO_CLEAN_FLOW_CLOSE : po->preview.setNoCleanFlowClose(true); break; case OPT_FLOW_FLIP_CLIENT_SERVER : po->preview.setClientServerFlowFlip(true); break; case OPT_FLOW_FLIP_CLIENT_SERVER_SIDE: po->preview.setClientServerFlowFlipAddr(true); break; case OPT_NODE_DUMP: a=atoi(args.OptionArg()); node_dump=1; po->preview.setFileWrite(false); break; case OPT_DUMP_INTERFACES: if (first_time) { rgpszArg = args.MultiArg(1); while (rgpszArg != NULL) { po->dump_interfaces.push_back(rgpszArg[0]); rgpszArg = args.MultiArg(1); } } if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { parse_err("Please specify single run mode"); } po->m_run_mode = CParserOption::RUN_MODE_DUMP_INFO; break; case OPT_MBUF_FACTOR: sscanf(args.OptionArg(),"%f", &po->m_mbuf_factor); break; case OPT_RATE_MULT : sscanf(args.OptionArg(),"%f", &po->m_factor); break; case OPT_DURATION : sscanf(args.OptionArg(),"%f", &po->m_duration); break; case OPT_PUB_DISABLE: po->preview.set_zmq_publish_enable(false); break; case OPT_PLATFORM_FACTOR: sscanf(args.OptionArg(),"%f", &po->m_platform_factor); break; case OPT_LATENCY : latency_was_set=true; sscanf(args.OptionArg(),"%d", &po->m_latency_rate); break; case OPT_LATENCY_MASK : sscanf(args.OptionArg(),"%x", &po->m_latency_mask); break; case OPT_ONLY_LATENCY : po->preview.setOnlyLatency(true); break; case OPT_NO_WATCHDOG : po->preview.setWDDisable(true); break; case OPT_ALLOW_COREDUMP : po->preview.setCoreDumpEnable(true); break; case OPT_LATENCY_PREVIEW : sscanf(args.OptionArg(),"%d", &po->m_latency_prev); break; case OPT_WAIT_BEFORE_TRAFFIC : sscanf(args.OptionArg(),"%d", &po->m_wait_before_traffic); break; case OPT_PCAP: po->preview.set_pcap_mode_enable(true); break; case OPT_RX_CHECK : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_rx_check_sample=(uint16_t)tmp_data; po->preview.set_rx_check_enable(true); break; case OPT_RX_CHECK_HOPS : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_rx_check_hops = (uint16_t)tmp_data; break; case OPT_IO_MODE : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_io_mode=(uint16_t)tmp_data; break; case OPT_VIRT_ONE_TX_RX_QUEUE: po->preview.set_vm_one_queue_enable(true); break; case OPT_PREFIX: po->prefix = args.OptionArg(); break; case OPT_SEND_DEBUG_PKT: sscanf(args.OptionArg(),"%d", &tmp_data); po->m_debug_pkt_proto = (uint8_t)tmp_data; break; case OPT_CHECKSUM_OFFLOAD: po->preview.setChecksumOffloadEnable(true); break; case OPT_CLOSE: po->preview.setCloseEnable(true); break; case OPT_ARP_REF_PER: sscanf(args.OptionArg(),"%d", &tmp_data); po->m_arp_ref_per=(uint16_t)tmp_data; break; default: usage(); return -1; break; } // End of switch }// End of IF else { usage(); return -1; } } // End of while if ((po->m_run_mode == CParserOption::RUN_MODE_INVALID) ) { parse_err("Please provide single run mode (e.g. batch or interactive)"); } if (CGlobalInfo::is_learn_mode() && po->preview.get_ipv6_mode_enable()) { parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \ "if you think it is important,open a defect \n"); } if (po->preview.get_is_rx_check_enable() || po->is_latency_enabled() || CGlobalInfo::is_learn_mode() || (CGlobalInfo::m_options.m_arp_ref_per != 0)) { po->set_rx_enabled(); } if ( node_dump ){ po->preview.setVMode(a); } /* if we have a platform factor we need to devided by it so we can still work with normalized yaml profile */ po->m_factor = po->m_factor/po->m_platform_factor; uint32_t cores=po->preview.getCores(); if ( cores > ((BP_MAX_CORES)/2-1) ) { printf(" ERROR maximum supported cores are : %d \n",((BP_MAX_CORES)/2-1)); return -1; } if ( first_time ){ /* only first time read the configuration file */ if ( po->platform_cfg_file.length() >0 ) { if ( node_dump ){ printf("Loading platform configuration file from %s \n",po->platform_cfg_file.c_str()); } global_platform_cfg_info.load_from_yaml_file(po->platform_cfg_file); if ( node_dump ){ global_platform_cfg_info.Dump(stdout); } }else{ if ( utl_is_file_exists("/etc/trex_cfg.yaml") ){ printf("found configuration file at /etc/trex_cfg.yaml \n"); global_platform_cfg_info.load_from_yaml_file("/etc/trex_cfg.yaml"); if ( node_dump ){ global_platform_cfg_info.Dump(stdout); } } } } if ( get_is_stateless() ) { if ( po->m_duration ) { parse_err("Duration is not supported with interactive mode "); } if ( po->preview.get_is_rx_check_enable() ) { parse_err("Rx check is not supported with interactive mode "); } if ( (po->is_latency_enabled()) || (po->preview.getOnlyLatency()) ){ parse_err("Latency check is not supported with interactive mode "); } if ( po->preview.getSingleCore() ){ parse_err("Single core is not supported with interactive mode "); } } else { if ( !po->m_duration ) { po->m_duration = 3600.0; } } return 0; } static int parse_options_wrapper(int argc, char *argv[], CParserOption* po, bool first_time ) { // copy, as arg parser sometimes changes the argv char ** argv_copy = (char **) malloc(sizeof(char *) * argc); for(int i=0; iupdate_configuration(this); } inline void update_global_config_fdir(void){ get_ex_drv()->update_global_config_fdir(this); } /* enable FDIR */ inline void update_global_config_fdir_10g(void){ m_port_conf.fdir_conf.mode=RTE_FDIR_MODE_PERFECT_MAC_VLAN; m_port_conf.fdir_conf.pballoc=RTE_FDIR_PBALLOC_64K; m_port_conf.fdir_conf.status=RTE_FDIR_NO_REPORT_STATUS; /* Offset of flexbytes field in RX packets (in 16-bit word units). */ /* Note: divide by 2 to convert byte offset to word offset */ if (get_is_stateless()) { m_port_conf.fdir_conf.flexbytes_offset = (14+4)/2; /* Increment offset 4 bytes for the case where we add VLAN */ if ( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) { m_port_conf.fdir_conf.flexbytes_offset += (4/2); } } else { if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ) { m_port_conf.fdir_conf.flexbytes_offset = (14+6)/2; } else { m_port_conf.fdir_conf.flexbytes_offset = (14+8)/2; } /* Increment offset 4 bytes for the case where we add VLAN */ if ( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) { m_port_conf.fdir_conf.flexbytes_offset += (4/2); } } m_port_conf.fdir_conf.drop_queue=1; } inline void update_global_config_fdir_40g(void){ m_port_conf.fdir_conf.mode=RTE_FDIR_MODE_PERFECT; m_port_conf.fdir_conf.pballoc=RTE_FDIR_PBALLOC_64K; m_port_conf.fdir_conf.status=RTE_FDIR_NO_REPORT_STATUS; } struct rte_eth_conf m_port_conf; struct rte_eth_rxconf m_rx_conf; struct rte_eth_rxconf m_rx_drop_conf; struct rte_eth_txconf m_tx_conf; }; /* this object is per core / per port / per queue each core will have 2 ports to send to port0 port1 0,1,2,3,..15 out queue ( per core ) 0,1,2,3,..15 out queue ( per core ) */ typedef struct cnt_name_ { uint32_t offset; char * name; }cnt_name_t ; #define MY_REG(a) {a,(char *)#a} void CPhyEthIFStats::Clear() { ipackets = 0; ibytes = 0; f_ipackets = 0; f_ibytes = 0; opackets = 0; obytes = 0; ierrors = 0; oerrors = 0; imcasts = 0; rx_nombuf = 0; memset(&m_prev_stats, 0, sizeof(m_prev_stats)); memset(m_rx_per_flow_pkts, 0, sizeof(m_rx_per_flow_pkts)); memset(m_rx_per_flow_bytes, 0, sizeof(m_rx_per_flow_bytes)); } // dump all counters (even ones that equal 0) void CPhyEthIFStats::DumpAll(FILE *fd) { #define DP_A4(f) printf(" %-40s : %llu \n",#f, (unsigned long long)f) #define DP_A(f) if (f) printf(" %-40s : %llu \n",#f, (unsigned long long)f) DP_A4(opackets); DP_A4(obytes); DP_A4(ipackets); DP_A4(ibytes); DP_A(ierrors); DP_A(oerrors); } // dump all non zero counters void CPhyEthIFStats::Dump(FILE *fd) { DP_A(opackets); DP_A(obytes); DP_A(f_ipackets); DP_A(f_ibytes); DP_A(ipackets); DP_A(ibytes); DP_A(ierrors); DP_A(oerrors); DP_A(imcasts); DP_A(rx_nombuf); } void CPhyEthIgnoreStats::dump(FILE *fd) { DP_A4(opackets); DP_A4(obytes); DP_A4(ipackets); DP_A4(ibytes); DP_A4(m_tx_arp); DP_A4(m_rx_arp); } // Clear the RX queue of an interface, dropping all packets void CPhyEthIF::flush_rx_queue(void){ rte_mbuf_t * rx_pkts[32]; int j=0; uint16_t cnt=0; while (true) { j++; cnt = rx_burst(m_rx_queue,rx_pkts,32); if ( cnt ) { int i; for (i=0; i<(int)cnt;i++) { rte_mbuf_t * m=rx_pkts[i]; /*printf("rx--\n"); rte_pktmbuf_dump(stdout,m, rte_pktmbuf_pkt_len(m));*/ rte_pktmbuf_free(m); } } if ( ((cnt==0) && (j>10)) || (j>15) ) { break; } } if (cnt>0) { printf(" Warning can't flush rx-queue for port %d \n",(int)get_port_id()); } } void CPhyEthIF::dump_stats_extended(FILE *fd){ cnt_name_t reg[]={ MY_REG(IXGBE_GPTC), /* total packet */ MY_REG(IXGBE_GOTCL), /* total bytes */ MY_REG(IXGBE_GOTCH), MY_REG(IXGBE_GPRC), MY_REG(IXGBE_GORCL), MY_REG(IXGBE_GORCH), MY_REG(IXGBE_RXNFGPC), MY_REG(IXGBE_RXNFGBCL), MY_REG(IXGBE_RXNFGBCH), MY_REG(IXGBE_RXDGPC ), MY_REG(IXGBE_RXDGBCL ), MY_REG(IXGBE_RXDGBCH ), MY_REG(IXGBE_RXDDGPC ), MY_REG(IXGBE_RXDDGBCL ), MY_REG(IXGBE_RXDDGBCH ), MY_REG(IXGBE_RXLPBKGPC ), MY_REG(IXGBE_RXLPBKGBCL), MY_REG(IXGBE_RXLPBKGBCH ), MY_REG(IXGBE_RXDLPBKGPC ), MY_REG(IXGBE_RXDLPBKGBCL), MY_REG(IXGBE_RXDLPBKGBCH ), MY_REG(IXGBE_TXDGPC ), MY_REG(IXGBE_TXDGBCL ), MY_REG(IXGBE_TXDGBCH ), MY_REG(IXGBE_FDIRUSTAT ), MY_REG(IXGBE_FDIRFSTAT ), MY_REG(IXGBE_FDIRMATCH ), MY_REG(IXGBE_FDIRMISS ) }; fprintf (fd," extended counters \n"); int i; for (i=0; ioffset); // xl710 bug. Counter values are -559038737 when they should be 0 if (c && c != -559038737 ) { fprintf (fd," %s : %d \n",lp->name,c); } } } int CPhyEthIF::get_rx_stat_capabilities() { return get_ex_drv()->get_rx_stat_capabilities(); } void CPhyEthIF::configure(uint16_t nb_rx_queue, uint16_t nb_tx_queue, const struct rte_eth_conf *eth_conf){ int ret; ret = rte_eth_dev_configure(m_port_id, nb_rx_queue, nb_tx_queue, eth_conf); if (ret < 0) rte_exit(EXIT_FAILURE, "Cannot configure device: " "err=%d, port=%u\n", ret, m_port_id); /* get device info */ rte_eth_dev_info_get(m_port_id, &m_dev_info); if (CGlobalInfo::m_options.preview.getChecksumOffloadEnable()) { /* check if the device supports TCP and UDP checksum offloading */ if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM) == 0) { rte_exit(EXIT_FAILURE, "Device does not support UDP checksum offload: " "port=%u\n", m_port_id); } if ((m_dev_info.tx_offload_capa & DEV_TX_OFFLOAD_TCP_CKSUM) == 0) { rte_exit(EXIT_FAILURE, "Device does not support TCP checksum offload: " "port=%u\n", m_port_id); } } } /* rx-queue 0 - default- all traffic not goint to queue 1 will be drop as queue is disable rx-queue 1 - Latency measurement packets will go here pci_reg_write(IXGBE_L34T_IMIR(0),(1<<21)); */ void CPhyEthIF::configure_rx_duplicate_rules(){ if ( get_is_rx_filter_enable() ){ if ( get_ex_drv()->is_hardware_filter_is_supported()==false ){ printf(" ERROR this feature is not supported with current hardware \n"); exit(1); } get_ex_drv()->configure_rx_filter_rules(this); } } void CPhyEthIF::stop_rx_drop_queue() { // In debug mode, we want to see all packets. Don't want to disable any queue. if ( get_vm_one_queue_enable() || (CGlobalInfo::m_options.m_debug_pkt_proto != 0)) { return; } if ( CGlobalInfo::m_options.is_rx_enabled() ) { if ( (!get_ex_drv()->is_hardware_support_drop_queue()) ) { printf(" ERROR latency feature is not supported with current hardware \n"); exit(1); } } get_ex_drv()->stop_queue(this, MAIN_DPDK_DATA_Q); } void CPhyEthIF::rx_queue_setup(uint16_t rx_queue_id, uint16_t nb_rx_desc, unsigned int socket_id, const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool){ int ret = rte_eth_rx_queue_setup(m_port_id , rx_queue_id, nb_rx_desc, socket_id, rx_conf, mb_pool); if (ret < 0) rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: " "err=%d, port=%u\n", ret, m_port_id); } void CPhyEthIF::tx_queue_setup(uint16_t tx_queue_id, uint16_t nb_tx_desc, unsigned int socket_id, const struct rte_eth_txconf *tx_conf){ int ret = rte_eth_tx_queue_setup( m_port_id, tx_queue_id, nb_tx_desc, socket_id, tx_conf); if (ret < 0) rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: " "err=%d, port=%u queue=%u\n", ret, m_port_id, tx_queue_id); } void CPhyEthIF::stop(){ if (CGlobalInfo::m_options.preview.getCloseEnable()) { rte_eth_dev_stop(m_port_id); rte_eth_dev_close(m_port_id); } } void CPhyEthIF::start(){ get_ex_drv()->clear_extended_stats(this); int ret; m_bw_tx.reset(); m_bw_rx.reset(); m_stats.Clear(); int i; for (i=0;i<10; i++ ) { ret = rte_eth_dev_start(m_port_id); if (ret==0) { return; } delay(1000); } if (ret < 0) rte_exit(EXIT_FAILURE, "rte_eth_dev_start: " "err=%d, port=%u\n", ret, m_port_id); } // Disabling flow control on interface void CPhyEthIF::disable_flow_control(){ int ret; // see trex-64 issue with loopback on the same NIC struct rte_eth_fc_conf fc_conf; memset(&fc_conf,0,sizeof(fc_conf)); fc_conf.mode=RTE_FC_NONE; fc_conf.autoneg=1; fc_conf.pause_time=100; int i; for (i=0; i<5; i++) { ret=rte_eth_dev_flow_ctrl_set(m_port_id,&fc_conf); if (ret==0) { break; } delay(1000); } if (ret < 0) rte_exit(EXIT_FAILURE, "rte_eth_dev_flow_ctrl_set: " "err=%d, port=%u\n probably link is down. Please check your link activity, or skip flow-control disabling, using: --no-flow-control-change option\n", ret, m_port_id); } /* Get user frienly devices description from saved env. var Changes certain attributes based on description */ void DpdkTRexPortAttr::update_description(){ struct rte_pci_addr pci_addr; char pci[16]; char * envvar; std::string pci_envvar_name; pci_addr = rte_eth_devices[m_port_id].pci_dev->addr; snprintf(pci, sizeof(pci), "%04x:%02x:%02x.%d", pci_addr.domain, pci_addr.bus, pci_addr.devid, pci_addr.function); intf_info_st.pci_addr = pci; pci_envvar_name = "pci" + intf_info_st.pci_addr; std::replace(pci_envvar_name.begin(), pci_envvar_name.end(), ':', '_'); std::replace(pci_envvar_name.begin(), pci_envvar_name.end(), '.', '_'); envvar = std::getenv(pci_envvar_name.c_str()); if (envvar) { intf_info_st.description = envvar; } else { intf_info_st.description = "Unknown"; } if (intf_info_st.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.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", m_port_id, intf_info_st.description.c_str()); } } int DpdkTRexPortAttr::set_led(bool on){ if (on) { return rte_eth_led_on(m_port_id); }else{ return rte_eth_led_off(m_port_id); } } int DpdkTRexPortAttr::get_flow_ctrl(int &mode) { int ret = rte_eth_dev_flow_ctrl_get(m_port_id, &fc_conf_tmp); if (ret) { return ret; } mode = (int) fc_conf_tmp.mode; return 0; } int DpdkTRexPortAttr::set_flow_ctrl(int mode) { if (!flag_is_fc_change_supported) { return -ENOTSUP; } int ret = rte_eth_dev_flow_ctrl_get(m_port_id, &fc_conf_tmp); if (ret) { return ret; } fc_conf_tmp.mode = (enum rte_eth_fc_mode) mode; return rte_eth_dev_flow_ctrl_set(m_port_id, &fc_conf_tmp); } void DpdkTRexPortAttr::reset_xstats() { rte_eth_xstats_reset(m_port_id); } int DpdkTRexPortAttr::get_xstats_values(xstats_values_t &xstats_values) { int size = rte_eth_xstats_get(m_port_id, NULL, 0); if (size < 0) { return size; } xstats_values_tmp.resize(size); xstats_values.resize(size); size = rte_eth_xstats_get(m_port_id, xstats_values_tmp.data(), size); if (size < 0) { return size; } for (int i=0; idump_fdir_global_stats(this, fd); } void dump_hw_state(FILE *fd,struct ixgbe_hw_stats *hs ){ #define DP_A1(f) if (hs->f) fprintf(fd," %-40s : %llu \n",#f, (unsigned long long)hs->f) #define DP_A2(f,m) for (i=0;if[i]) fprintf(fd," %-40s[%d] : %llu \n",#f,i, (unsigned long long)hs->f[i]); } int i; //for (i=0;i<8; i++) { if (hs->mpc[i]) fprintf(fd," %-40s[%d] : %llu \n","mpc",i,hs->mpc[i]); } DP_A2(mpc,8); DP_A1(crcerrs); DP_A1(illerrc); //DP_A1(errbc); DP_A1(mspdc); DP_A1(mpctotal); DP_A1(mlfc); DP_A1(mrfc); DP_A1(rlec); //DP_A1(lxontxc); //DP_A1(lxonrxc); //DP_A1(lxofftxc); //DP_A1(lxoffrxc); //DP_A2(pxontxc,8); //DP_A2(pxonrxc,8); //DP_A2(pxofftxc,8); //DP_A2(pxoffrxc,8); //DP_A1(prc64); //DP_A1(prc127); //DP_A1(prc255); // DP_A1(prc511); //DP_A1(prc1023); //DP_A1(prc1522); DP_A1(gprc); DP_A1(bprc); DP_A1(mprc); DP_A1(gptc); DP_A1(gorc); DP_A1(gotc); DP_A2(rnbc,8); DP_A1(ruc); DP_A1(rfc); DP_A1(roc); DP_A1(rjc); DP_A1(mngprc); DP_A1(mngpdc); DP_A1(mngptc); DP_A1(tor); DP_A1(tpr); DP_A1(tpt); DP_A1(ptc64); DP_A1(ptc127); DP_A1(ptc255); DP_A1(ptc511); DP_A1(ptc1023); DP_A1(ptc1522); DP_A1(mptc); DP_A1(bptc); DP_A1(xec); DP_A2(qprc,16); DP_A2(qptc,16); DP_A2(qbrc,16); DP_A2(qbtc,16); DP_A2(qprdc,16); DP_A2(pxon2offc,8); DP_A1(fdirustat_add); DP_A1(fdirustat_remove); DP_A1(fdirfstat_fadd); DP_A1(fdirfstat_fremove); DP_A1(fdirmatch); DP_A1(fdirmiss); DP_A1(fccrc); DP_A1(fclast); DP_A1(fcoerpdc); DP_A1(fcoeprc); DP_A1(fcoeptc); DP_A1(fcoedwrc); DP_A1(fcoedwtc); DP_A1(fcoe_noddp); DP_A1(fcoe_noddp_ext_buff); DP_A1(ldpcec); DP_A1(pcrc8ec); DP_A1(b2ospc); DP_A1(b2ogprc); DP_A1(o2bgptc); DP_A1(o2bspc); } void CPhyEthIF::set_ignore_stats_base(CPreTestStats &pre_stats) { // reading m_stats, so drivers saving prev in m_stats will be updated. // Actually, we want m_stats to be cleared get_ex_drv()->get_extended_stats(this, &m_stats); m_ignore_stats.ipackets = m_stats.ipackets; m_ignore_stats.ibytes = m_stats.ibytes; m_ignore_stats.opackets = m_stats.opackets; m_ignore_stats.obytes = m_stats.obytes; m_stats.ipackets = 0; m_stats.opackets = 0; m_stats.ibytes = 0; m_stats.obytes = 0; m_ignore_stats.m_tx_arp = pre_stats.m_tx_arp; m_ignore_stats.m_rx_arp = pre_stats.m_rx_arp; if (CGlobalInfo::m_options.preview.getVMode() >= 3) { fprintf(stdout, "Pre test statistics for port %d\n", get_port_id()); m_ignore_stats.dump(stdout); } } void CPhyEthIF::dump_stats(FILE *fd){ update_counters(); fprintf(fd,"port : %d \n",(int)m_port_id); fprintf(fd,"------------\n"); m_stats.DumpAll(fd); //m_stats.Dump(fd); printf (" Tx : %.1fMb/sec \n",m_last_tx_rate); //printf (" Rx : %.1fMb/sec \n",m_last_rx_rate); } void CPhyEthIF::stats_clear(){ rte_eth_stats_reset(m_port_id); m_stats.Clear(); } class CCorePerPort { public: CCorePerPort (){ m_tx_queue_id=0; m_len=0; int i; for (i=0; iget_port_id() ) ); } const CCorePerPort * get_ports() { return m_ports; } protected: int send_burst(CCorePerPort * lp_port, uint16_t len, CVirtualIFPerSideStats * lp_stats); int send_pkt(CCorePerPort * lp_port, rte_mbuf_t *m, CVirtualIFPerSideStats * lp_stats); int send_pkt_lat(CCorePerPort * lp_port, rte_mbuf_t *m, CVirtualIFPerSideStats * lp_stats); void add_vlan(rte_mbuf_t *m, uint16_t vlan_id); protected: uint8_t m_core_id; uint16_t m_mbuf_cache; CCorePerPort m_ports[CS_NUM]; /* each core has 2 tx queues 1. client side and server side */ CNodeRing * m_ring_to_rx; } __rte_cache_aligned; ; class CCoreEthIFStateless : public CCoreEthIF { public: virtual int send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * node_sl, CCorePerPort * lp_port , CVirtualIFPerSideStats * lp_stats, bool is_const); virtual int send_node(CGenNode * node); protected: int handle_slow_path_node(CGenNode *node); int send_pcap_node(CGenNodePCAP *pcap_node); }; bool CCoreEthIF::Create(uint8_t core_id, uint8_t tx_client_queue_id, CPhyEthIF * tx_client_port, uint8_t tx_server_queue_id, CPhyEthIF * tx_server_port, uint8_t tx_q_id_lat ) { m_ports[CLIENT_SIDE].m_tx_queue_id = tx_client_queue_id; m_ports[CLIENT_SIDE].m_port = tx_client_port; m_ports[CLIENT_SIDE].m_tx_queue_id_lat = tx_q_id_lat; m_ports[SERVER_SIDE].m_tx_queue_id = tx_server_queue_id; m_ports[SERVER_SIDE].m_port = tx_server_port; m_ports[SERVER_SIDE].m_tx_queue_id_lat = tx_q_id_lat; m_core_id = core_id; CMessagingManager * rx_dp=CMsgIns::Ins()->getRxDp(); m_ring_to_rx = rx_dp->getRingDpToCp(core_id-1); assert( m_ring_to_rx); return (true); } // This function is only relevant if we are in VM. In this case, we only have one rx queue. Can't have // rules to drop queue 0 packets, and pass queue 1 packets to RX core, like in other cases. // We receive all packets in the same core that transmitted, and handle them to RX core. void CCoreEthIF::handle_rx_queue(void) { if ( likely( ! get_vm_one_queue_enable() ) ) { return; } pkt_dir_t dir; bool is_rx = get_is_rx_thread_enabled(); for (dir=CLIENT_SIDE; dirm_port; rte_mbuf_t * rx_pkts[32]; int j=0; while (true) { j++; uint16_t cnt =lp->rx_burst(0,rx_pkts,32); if ( cnt ) { int i; for (i=0; i<(int)cnt;i++) { rte_mbuf_t * m=rx_pkts[i]; if ( is_rx ){ if (!process_rx_pkt(dir,m)){ rte_pktmbuf_free(m); } }else{ rte_pktmbuf_free(m); } } } if ((cnt<5) || j>10 ) { break; } } } } int CCoreEthIF::flush_tx_queue(void){ /* flush both sides */ pkt_dir_t dir; for (dir = CLIENT_SIDE; dir < CS_NUM; dir++) { CCorePerPort * lp_port = &m_ports[dir]; CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; if ( likely(lp_port->m_len > 0) ) { send_burst(lp_port, lp_port->m_len, lp_stats); lp_port->m_len = 0; } } handle_rx_queue(); return 0; } void CCoreEthIF::GetCoreCounters(CVirtualIFPerSideStats *stats){ stats->Clear(); pkt_dir_t dir ; for (dir=CLIENT_SIDE; dirAdd(&m_stats[dir]); } } void CCoreEthIF::DumpCoreStats(FILE *fd){ fprintf (fd,"------------------------ \n"); fprintf (fd," per core stats core id : %d \n",m_core_id); fprintf (fd,"------------------------ \n"); CVirtualIFPerSideStats stats; GetCoreCounters(&stats); stats.Dump(stdout); } void CCoreEthIF::DumpIfCfgHeader(FILE *fd){ fprintf (fd," core, c-port, c-queue, s-port, s-queue, lat-queue\n"); fprintf (fd," ------------------------------------------\n"); } void CCoreEthIF::DumpIfCfg(FILE *fd){ fprintf (fd," %d %6u %6u %6u %6u %6u \n",m_core_id, m_ports[CLIENT_SIDE].m_port->get_port_id(), m_ports[CLIENT_SIDE].m_tx_queue_id, m_ports[SERVER_SIDE].m_port->get_port_id(), m_ports[SERVER_SIDE].m_tx_queue_id, m_ports[SERVER_SIDE].m_tx_queue_id_lat ); } void CCoreEthIF::DumpIfStats(FILE *fd){ fprintf (fd,"------------------------ \n"); fprintf (fd," per core per if stats id : %d \n",m_core_id); fprintf (fd,"------------------------ \n"); const char * t[]={"client","server"}; pkt_dir_t dir ; for (dir=CLIENT_SIDE; dirm_port->get_port_id(),lp->m_tx_queue_id,t[dir] ); fprintf (fd," ---------------------------- \n"); lpstats->Dump(fd); } } #define DELAY_IF_NEEDED int CCoreEthIF::send_burst(CCorePerPort * lp_port, uint16_t len, CVirtualIFPerSideStats * lp_stats){ uint16_t ret = lp_port->m_port->tx_burst(lp_port->m_tx_queue_id,lp_port->m_table,len); #ifdef DELAY_IF_NEEDED while ( unlikely( retm_tx_queue_full += 1; uint16_t ret1=lp_port->m_port->tx_burst(lp_port->m_tx_queue_id, &lp_port->m_table[ret], len-ret); ret+=ret1; } #else /* CPU has burst of packets larger than TX can send. Need to drop packets */ if ( unlikely(ret < len) ) { lp_stats->m_tx_drop += (len-ret); uint16_t i; for (i=ret; im_table[i]; rte_pktmbuf_free(m); } } #endif return (0); } int CCoreEthIF::send_pkt(CCorePerPort * lp_port, rte_mbuf_t *m, CVirtualIFPerSideStats * lp_stats ){ uint16_t len = lp_port->m_len; lp_port->m_table[len]=m; len++; /* enough pkts to be sent */ if (unlikely(len == MAX_PKT_BURST)) { send_burst(lp_port, MAX_PKT_BURST,lp_stats); len = 0; } lp_port->m_len = len; return (0); } int CCoreEthIF::send_pkt_lat(CCorePerPort *lp_port, rte_mbuf_t *m, CVirtualIFPerSideStats *lp_stats) { // We allow sending only from first core of each port. This is serious internal bug otherwise. assert(lp_port->m_tx_queue_id_lat != INVALID_Q_ID); int ret = lp_port->m_port->tx_burst(lp_port->m_tx_queue_id_lat, &m, 1); #ifdef DELAY_IF_NEEDED while ( unlikely( ret != 1 ) ){ rte_delay_us(1); lp_stats->m_tx_queue_full += 1; ret = lp_port->m_port->tx_burst(lp_port->m_tx_queue_id_lat, &m, 1); } #else if ( unlikely( ret != 1 ) ) { lp_stats->m_tx_drop ++; rte_pktmbuf_free(m); return 0; } #endif return ret; } void CCoreEthIF::send_one_pkt(pkt_dir_t dir, rte_mbuf_t *m){ CCorePerPort * lp_port=&m_ports[dir]; CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; send_pkt(lp_port,m,lp_stats); /* flush */ send_burst(lp_port,lp_port->m_len,lp_stats); lp_port->m_len = 0; } int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * node_sl, CCorePerPort * lp_port , CVirtualIFPerSideStats * lp_stats, bool is_const) { // Defining this makes 10% percent packet loss. 1% packet reorder. # ifdef ERR_CNTRS_TEST static int temp=1; temp++; #endif uint16_t hw_id = node_sl->get_stat_hw_id(); rte_mbuf *mi; struct flow_stat_payload_header *fsp_head = NULL; if (hw_id >= MAX_FLOW_STATS) { // payload rule hw_ids are in the range right above ip id rules uint16_t hw_id_payload = hw_id - MAX_FLOW_STATS; if (hw_id_payload > max_stat_hw_id_seen_payload) { max_stat_hw_id_seen_payload = hw_id_payload; } mi = node_sl->alloc_flow_stat_mbuf(m, fsp_head, is_const); fsp_head->seq = lp_stats->m_lat_data[hw_id_payload].get_seq_num(); fsp_head->hw_id = hw_id_payload; fsp_head->flow_seq = lp_stats->m_lat_data[hw_id_payload].get_flow_seq(); fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; lp_stats->m_lat_data[hw_id_payload].inc_seq_num(); #ifdef ERR_CNTRS_TEST if (temp % 10 == 0) { fsp_head->seq = lp_stats->m_lat_data[hw_id_payload].inc_seq_num(); } if ((temp - 1) % 100 == 0) { fsp_head->seq = lp_stats->m_lat_data[hw_id_payload].get_seq_num() - 4; } #endif } else { // ip id rule if (hw_id > max_stat_hw_id_seen) { max_stat_hw_id_seen = hw_id; } mi = m; } tx_per_flow_t *lp_s = &lp_stats->m_tx_per_flow[hw_id]; lp_s->add_pkts(1); lp_s->add_bytes(mi->pkt_len + 4); // We add 4 because of ethernet CRC if (hw_id >= MAX_FLOW_STATS) { fsp_head->time_stamp = os_get_hr_tick_64(); send_pkt_lat(lp_port, mi, lp_stats); } else { send_pkt(lp_port, mi, lp_stats); } return 0; } int CCoreEthIFStateless::send_node(CGenNode * no) { /* if a node is marked as slow path - single IF to redirect it to slow path */ if (no->get_is_slow_path()) { return handle_slow_path_node(no); } CGenNodeStateless * node_sl=(CGenNodeStateless *) no; /* check that we have mbuf */ rte_mbuf_t * m; pkt_dir_t dir=(pkt_dir_t)node_sl->get_mbuf_cache_dir(); CCorePerPort * lp_port=&m_ports[dir]; CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; if ( likely(node_sl->is_cache_mbuf_array()) ) { m=node_sl->cache_mbuf_array_get_cur(); rte_pktmbuf_refcnt_update(m,1); }else{ m=node_sl->get_cache_mbuf(); if (m) { /* cache case */ rte_pktmbuf_refcnt_update(m,1); }else{ m=node_sl->alloc_node_with_vm(); assert(m); } } if (unlikely(node_sl->is_stat_needed())) { if ( unlikely(node_sl->is_cache_mbuf_array()) ) { // No support for latency + cache. If user asks for cache on latency stream, we change cache to 0. // assert here just to make sure. assert(1); } return send_node_flow_stat(m, node_sl, lp_port, lp_stats, (node_sl->get_cache_mbuf()) ? true : false); } else { send_pkt(lp_port,m,lp_stats); } return (0); }; int CCoreEthIFStateless::send_pcap_node(CGenNodePCAP *pcap_node) { rte_mbuf_t *m = pcap_node->get_pkt(); if (!m) { return (-1); } pkt_dir_t dir = (pkt_dir_t)pcap_node->get_mbuf_dir(); CCorePerPort *lp_port=&m_ports[dir]; CVirtualIFPerSideStats *lp_stats = &m_stats[dir]; send_pkt(lp_port, m, lp_stats); return (0); } /** * slow path code goes here * */ int CCoreEthIFStateless::handle_slow_path_node(CGenNode * no) { if (no->m_type == CGenNode::PCAP_PKT) { return send_pcap_node((CGenNodePCAP *)no); } return (-1); } void CCoreEthIF::apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p) { assert(cfg); /* take the right direction config */ const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); /* dst mac */ if (cfg_dir.has_dst_mac_addr()) { memcpy(p, cfg_dir.get_dst_mac_addr(), 6); } /* src mac */ if (cfg_dir.has_src_mac_addr()) { memcpy(p + 6, cfg_dir.get_src_mac_addr(), 6); } /* VLAN */ if (cfg_dir.has_vlan()) { add_vlan(m, cfg_dir.get_vlan()); } } void CCoreEthIF::add_vlan(rte_mbuf_t *m, uint16_t vlan_id) { m->ol_flags = PKT_TX_VLAN_PKT; m->l2_len = 14; m->vlan_tci = vlan_id; } /** * slow path features goes here (avoid multiple IFs) * */ void CCoreEthIF::handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir) { /* MAC ovverride */ if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_overide_enable() ) ) { /* client side */ if ( node->is_initiator_pkt() ) { *((uint32_t*)(p+6)) = PKT_NTOHL(node->m_src_ip); } } /* flag is faster than checking the node pointer (another cacheline) */ if ( unlikely(CGlobalInfo::m_options.preview.get_is_client_cfg_enable() ) ) { apply_client_cfg(node->m_client_cfg, m, dir, p); } } int CCoreEthIF::send_node(CGenNode * node) { if ( unlikely( node->get_cache_mbuf() !=NULL ) ) { pkt_dir_t dir; rte_mbuf_t * m=node->get_cache_mbuf(); dir=(pkt_dir_t)node->get_mbuf_cache_dir(); CCorePerPort * lp_port=&m_ports[dir]; CVirtualIFPerSideStats * lp_stats = &m_stats[dir]; rte_pktmbuf_refcnt_update(m,1); send_pkt(lp_port,m,lp_stats); return (0); } CFlowPktInfo * lp=node->m_pkt_info; rte_mbuf_t * m=lp->generate_new_mbuf(node); pkt_dir_t dir; bool single_port; dir = node->cur_interface_dir(); single_port = node->get_is_all_flow_from_same_dir() ; if ( unlikely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){ /* which vlan to choose 0 or 1*/ uint8_t vlan_port = (node->m_src_ip &1); uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port]; if (likely( vlan_id >0 ) ) { dir = dir ^ vlan_port; }else{ /* both from the same dir but with VLAN0 */ vlan_id = CGlobalInfo::m_options.m_vlan_port[0]; dir = dir ^ 0; } add_vlan(m, vlan_id); } CCorePerPort *lp_port = &m_ports[dir]; CVirtualIFPerSideStats *lp_stats = &m_stats[dir]; if (unlikely(m==0)) { lp_stats->m_tx_alloc_error++; return(0); } /* update mac addr dest/src 12 bytes */ uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*); uint8_t p_id = lp_port->m_port->get_port_id(); memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12); /* when slowpath features are on */ if ( unlikely( CGlobalInfo::m_options.preview.get_is_slowpath_features_on() ) ) { handle_slowpath_features(node, m, p, dir); } if ( unlikely( node->is_rx_check_enabled() ) ) { lp_stats->m_tx_rx_check_pkt++; lp->do_generate_new_mbuf_rxcheck(m, node, single_port); lp_stats->m_template.inc_template( node->get_template_id( )); }else{ // cache only if it is not sample as this is more complex mbuf struct if ( unlikely( node->can_cache_mbuf() ) ) { if ( !CGlobalInfo::m_options.preview.isMbufCacheDisabled() ){ m_mbuf_cache++; if (m_mbuf_cache < MAX_MBUF_CACHE) { /* limit the number of object to cache */ node->set_mbuf_cache_dir( dir); node->set_cache_mbuf(m); rte_pktmbuf_refcnt_update(m,1); } } } } /*printf("send packet -- \n"); rte_pktmbuf_dump(stdout,m, rte_pktmbuf_pkt_len(m));*/ /* send the packet */ send_pkt(lp_port,m,lp_stats); return (0); } int CCoreEthIF::update_mac_addr_from_global_cfg(pkt_dir_t dir, uint8_t * p){ assert(p); assert(dir<2); CCorePerPort * lp_port=&m_ports[dir]; uint8_t p_id=lp_port->m_port->get_port_id(); memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12); return (0); } pkt_dir_t CCoreEthIF::port_id_to_dir(uint8_t port_id) { for (pkt_dir_t dir = 0; dir < CS_NUM; dir++) { if (m_ports[dir].m_port->get_port_id() == port_id) { return dir; } } return (CS_INVALID); } class CLatencyHWPort : public CPortLatencyHWBase { public: void Create(CPhyEthIF * p, uint8_t tx_queue, uint8_t rx_queue){ m_port=p; m_tx_queue_id=tx_queue; m_rx_queue_id=rx_queue; } virtual int tx(rte_mbuf_t * m){ rte_mbuf_t * tx_pkts[2]; tx_pkts[0]=m; if ( likely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){ /* vlan mode is the default */ /* set the vlan */ m->ol_flags = PKT_TX_VLAN_PKT; m->vlan_tci =CGlobalInfo::m_options.m_vlan_port[0]; m->l2_len =14; } uint16_t res=m_port->tx_burst(m_tx_queue_id,tx_pkts,1); if ( res == 0 ) { rte_pktmbuf_free(m); //printf(" queue is full for latency packet !!\n"); return (-1); } #if 0 fprintf(stdout," ==> %f.03 send packet ..\n",now_sec()); uint8_t *p1=rte_pktmbuf_mtod(m, uint8_t*); uint16_t pkt_size1=rte_pktmbuf_pkt_len(m); utl_DumpBuffer(stdout,p1,pkt_size1,0); #endif return (0); } virtual rte_mbuf_t * rx(){ rte_mbuf_t * rx_pkts[1]; uint16_t cnt=m_port->rx_burst(m_rx_queue_id,rx_pkts,1); if (cnt) { return (rx_pkts[0]); }else{ return (0); } } virtual uint16_t rx_burst(struct rte_mbuf **rx_pkts, uint16_t nb_pkts){ uint16_t cnt=m_port->rx_burst(m_rx_queue_id,rx_pkts,nb_pkts); return (cnt); } private: CPhyEthIF * m_port; uint8_t m_tx_queue_id ; uint8_t m_rx_queue_id; }; class CLatencyVmPort : public CPortLatencyHWBase { public: void Create(uint8_t port_index,CNodeRing * ring, CLatencyManager * mgr){ m_dir = (port_index%2); m_ring_to_dp = ring; m_mgr = mgr; } virtual int tx(rte_mbuf_t * m){ if ( likely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){ /* vlan mode is the default */ /* set the vlan */ m->ol_flags = PKT_TX_VLAN_PKT; m->vlan_tci =CGlobalInfo::m_options.m_vlan_port[0]; m->l2_len =14; } /* allocate node */ CGenNodeLatencyPktInfo * node=(CGenNodeLatencyPktInfo * )CGlobalInfo::create_node(); if ( node ) { node->m_msg_type = CGenNodeMsgBase::LATENCY_PKT; node->m_dir = m_dir; node->m_pkt = m; node->m_latency_offset = m_mgr->get_latency_header_offset(); if ( m_ring_to_dp->Enqueue((CGenNode*)node) ==0 ){ return (0); } } return (-1); } virtual rte_mbuf_t * rx(){ return (0); } virtual uint16_t rx_burst(struct rte_mbuf **rx_pkts, uint16_t nb_pkts){ return (0); } private: uint8_t m_dir; CNodeRing * m_ring_to_dp; /* ring dp -> latency thread */ CLatencyManager * m_mgr; }; class CPerPortStats { public: uint64_t opackets; uint64_t obytes; uint64_t ipackets; uint64_t ibytes; uint64_t ierrors; uint64_t oerrors; tx_per_flow_t m_tx_per_flow[MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD]; tx_per_flow_t m_prev_tx_per_flow[MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD]; float m_total_tx_bps; float m_total_tx_pps; float m_total_rx_bps; float m_total_rx_pps; float m_cpu_util; }; class CGlobalStats { public: enum DumpFormat { dmpSTANDARD, dmpTABLE }; uint64_t m_total_tx_pkts; uint64_t m_total_rx_pkts; uint64_t m_total_tx_bytes; uint64_t m_total_rx_bytes; uint64_t m_total_alloc_error; uint64_t m_total_queue_full; uint64_t m_total_queue_drop; uint64_t m_total_clients; uint64_t m_total_servers; uint64_t m_active_sockets; uint64_t m_total_nat_time_out; uint64_t m_total_nat_time_out_wait_ack; uint64_t m_total_nat_no_fid ; uint64_t m_total_nat_active ; uint64_t m_total_nat_syn_wait; uint64_t m_total_nat_open ; uint64_t m_total_nat_learn_error ; CPerTxthreadTemplateInfo m_template; float m_socket_util; float m_platform_factor; float m_tx_bps; float m_rx_bps; float m_tx_pps; float m_rx_pps; float m_tx_cps; float m_tx_expected_cps; float m_tx_expected_pps; float m_tx_expected_bps; float m_rx_drop_bps; float m_active_flows; float m_open_flows; float m_cpu_util; float m_cpu_util_raw; float m_rx_cpu_util; float m_bw_per_core; uint8_t m_threads; uint32_t m_num_of_ports; CPerPortStats m_port[TREX_MAX_PORTS]; public: void Dump(FILE *fd,DumpFormat mode); void DumpAllPorts(FILE *fd); void dump_json(std::string & json, bool baseline); private: std::string get_field(const char *name, float &f); std::string get_field(const char *name, uint64_t &f); std::string get_field_port(int port, const char *name, float &f); std::string get_field_port(int port, const char *name, uint64_t &f); }; std::string CGlobalStats::get_field(const char *name, float &f){ char buff[200]; if(f <= -10.0 or f >= 10.0) snprintf(buff, sizeof(buff), "\"%s\":%.1f,",name,f); else snprintf(buff, sizeof(buff), "\"%s\":%.3e,",name,f); return (std::string(buff)); } std::string CGlobalStats::get_field(const char *name, uint64_t &f){ char buff[200]; snprintf(buff, sizeof(buff), "\"%s\":%llu,", name, (unsigned long long)f); return (std::string(buff)); } std::string CGlobalStats::get_field_port(int port, const char *name, float &f){ char buff[200]; if(f <= -10.0 or f >= 10.0) snprintf(buff, sizeof(buff), "\"%s-%d\":%.1f,", name, port, f); else snprintf(buff, sizeof(buff), "\"%s-%d\":%.3e,", name, port, f); return (std::string(buff)); } std::string CGlobalStats::get_field_port(int port, const char *name, uint64_t &f){ char buff[200]; snprintf(buff, sizeof(buff), "\"%s-%d\":%llu,",name, port, (unsigned long long)f); return (std::string(buff)); } void CGlobalStats::dump_json(std::string & json, bool baseline){ /* refactor this to JSON */ json="{\"name\":\"trex-global\",\"type\":0,"; if (baseline) { json += "\"baseline\": true,"; } json +="\"data\":{"; char ts_buff[200]; snprintf(ts_buff , sizeof(ts_buff), "\"ts\":{\"value\":%lu, \"freq\":%lu},", os_get_hr_tick_64(), os_get_hr_freq()); json+= std::string(ts_buff); #define GET_FIELD(f) get_field(#f, f) #define GET_FIELD_PORT(p,f) get_field_port(p, #f, lp->f) json+=GET_FIELD(m_cpu_util); json+=GET_FIELD(m_cpu_util_raw); json+=GET_FIELD(m_bw_per_core); json+=GET_FIELD(m_rx_cpu_util); json+=GET_FIELD(m_platform_factor); json+=GET_FIELD(m_tx_bps); json+=GET_FIELD(m_rx_bps); json+=GET_FIELD(m_tx_pps); json+=GET_FIELD(m_rx_pps); json+=GET_FIELD(m_tx_cps); json+=GET_FIELD(m_tx_expected_cps); json+=GET_FIELD(m_tx_expected_pps); json+=GET_FIELD(m_tx_expected_bps); json+=GET_FIELD(m_total_alloc_error); json+=GET_FIELD(m_total_queue_full); json+=GET_FIELD(m_total_queue_drop); json+=GET_FIELD(m_rx_drop_bps); json+=GET_FIELD(m_active_flows); json+=GET_FIELD(m_open_flows); json+=GET_FIELD(m_total_tx_pkts); json+=GET_FIELD(m_total_rx_pkts); json+=GET_FIELD(m_total_tx_bytes); json+=GET_FIELD(m_total_rx_bytes); json+=GET_FIELD(m_total_clients); json+=GET_FIELD(m_total_servers); json+=GET_FIELD(m_active_sockets); json+=GET_FIELD(m_socket_util); json+=GET_FIELD(m_total_nat_time_out); json+=GET_FIELD(m_total_nat_time_out_wait_ack); json+=GET_FIELD(m_total_nat_no_fid ); json+=GET_FIELD(m_total_nat_active ); json+=GET_FIELD(m_total_nat_syn_wait); json+=GET_FIELD(m_total_nat_open ); json+=GET_FIELD(m_total_nat_learn_error); int i; for (i=0; i<(int)m_num_of_ports; i++) { CPerPortStats * lp=&m_port[i]; json+=GET_FIELD_PORT(i,opackets) ; json+=GET_FIELD_PORT(i,obytes) ; json+=GET_FIELD_PORT(i,ipackets) ; json+=GET_FIELD_PORT(i,ibytes) ; json+=GET_FIELD_PORT(i,ierrors) ; json+=GET_FIELD_PORT(i,oerrors) ; json+=GET_FIELD_PORT(i,m_total_tx_bps); json+=GET_FIELD_PORT(i,m_total_tx_pps); json+=GET_FIELD_PORT(i,m_total_rx_bps); json+=GET_FIELD_PORT(i,m_total_rx_pps); json+=GET_FIELD_PORT(i,m_cpu_util); } json+=m_template.dump_as_json("template"); json+="\"unknown\":0}}" ; } void CGlobalStats::DumpAllPorts(FILE *fd){ //fprintf (fd," Total-Tx-Pkts : %s \n",double_to_human_str((double)m_total_tx_pkts,"pkts",KBYE_1000).c_str()); //fprintf (fd," Total-Rx-Pkts : %s \n",double_to_human_str((double)m_total_rx_pkts,"pkts",KBYE_1000).c_str()); //fprintf (fd," Total-Tx-Bytes : %s \n",double_to_human_str((double)m_total_tx_bytes,"bytes",KBYE_1000).c_str()); //fprintf (fd," Total-Rx-Bytes : %s \n",double_to_human_str((double)m_total_rx_bytes,"bytes",KBYE_1000).c_str()); fprintf (fd," Cpu Utilization : %2.1f %% %2.1f Gb/core \n",m_cpu_util,m_bw_per_core); fprintf (fd," Platform_factor : %2.1f \n",m_platform_factor); fprintf (fd," Total-Tx : %s ",double_to_human_str(m_tx_bps,"bps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { fprintf (fd," NAT time out : %8llu", (unsigned long long)m_total_nat_time_out); if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { fprintf (fd," (%llu in wait for syn+ack)\n", (unsigned long long)m_total_nat_time_out_wait_ack); } else { fprintf (fd, "\n"); } }else{ fprintf (fd,"\n"); } fprintf (fd," Total-Rx : %s ",double_to_human_str(m_rx_bps,"bps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { fprintf (fd," NAT aged flow id: %8llu \n", (unsigned long long)m_total_nat_no_fid); }else{ fprintf (fd,"\n"); } fprintf (fd," Total-PPS : %s ",double_to_human_str(m_tx_pps,"pps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { fprintf (fd," Total NAT active: %8llu", (unsigned long long)m_total_nat_active); if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { fprintf (fd," (%llu waiting for syn)\n", (unsigned long long)m_total_nat_syn_wait); } else { fprintf (fd, "\n"); } }else{ fprintf (fd,"\n"); } fprintf (fd," Total-CPS : %s ",double_to_human_str(m_tx_cps,"cps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_mode() ) { fprintf (fd," Total NAT opened: %8llu \n", (unsigned long long)m_total_nat_open); }else{ fprintf (fd,"\n"); } fprintf (fd,"\n"); fprintf (fd," Expected-PPS : %s ",double_to_human_str(m_tx_expected_pps,"pps",KBYE_1000).c_str()); if ( CGlobalInfo::is_learn_verify_mode() ) { fprintf (fd," NAT learn errors: %8llu \n", (unsigned long long)m_total_nat_learn_error); }else{ fprintf (fd,"\n"); } fprintf (fd," Expected-CPS : %s \n",double_to_human_str(m_tx_expected_cps,"cps",KBYE_1000).c_str()); fprintf (fd," Expected-BPS : %s \n",double_to_human_str(m_tx_expected_bps,"bps",KBYE_1000).c_str()); fprintf (fd,"\n"); fprintf (fd," Active-flows : %8llu Clients : %8llu Socket-util : %3.4f %% \n", (unsigned long long)m_active_flows, (unsigned long long)m_total_clients, m_socket_util); fprintf (fd," Open-flows : %8llu Servers : %8llu Socket : %8llu Socket/Clients : %.1f \n", (unsigned long long)m_open_flows, (unsigned long long)m_total_servers, (unsigned long long)m_active_sockets, (float)m_active_sockets/(float)m_total_clients); if (m_total_alloc_error) { fprintf (fd," Total_alloc_err : %llu \n", (unsigned long long)m_total_alloc_error); } if ( m_total_queue_full ){ fprintf (fd," Total_queue_full : %llu \n", (unsigned long long)m_total_queue_full); } if (m_total_queue_drop) { fprintf (fd," Total_queue_drop : %llu \n", (unsigned long long)m_total_queue_drop); } //m_template.Dump(fd); fprintf (fd," drop-rate : %s \n",double_to_human_str(m_rx_drop_bps,"bps",KBYE_1000).c_str() ); } void CGlobalStats::Dump(FILE *fd,DumpFormat mode){ int i; int port_to_show=m_num_of_ports; if (port_to_show>4) { port_to_show=4; fprintf (fd," per port - limited to 4 \n"); } if ( mode== dmpSTANDARD ){ fprintf (fd," --------------- \n"); for (i=0; i<(int)port_to_show; i++) { CPerPortStats * lp=&m_port[i]; fprintf(fd,"port : %d \n",(int)i); fprintf(fd,"------------\n"); #define GS_DP_A4(f) fprintf(fd," %-40s : %llu \n",#f, (unsigned long long)lp->f) #define GS_DP_A(f) if (lp->f) fprintf(fd," %-40s : %llu \n",#f, (unsigned long long)lp->f) GS_DP_A4(opackets); GS_DP_A4(obytes); GS_DP_A4(ipackets); GS_DP_A4(ibytes); GS_DP_A(ierrors); GS_DP_A(oerrors); fprintf (fd," Tx : %s \n",double_to_human_str((double)lp->m_total_tx_bps,"bps",KBYE_1000).c_str()); } }else{ fprintf(fd," %10s ","ports"); for (i=0; i<(int)port_to_show; i++) { fprintf(fd,"| %15d ",i); } fprintf(fd,"\n"); fprintf(fd," -----------------------------------------------------------------------------------------\n"); std::string names[]={"opackets","obytes","ipackets","ibytes","ierrors","oerrors","Tx Bw" }; for (i=0; i<7; i++) { fprintf(fd," %10s ",names[i].c_str()); int j=0; for (j=0; jopackets; fprintf(fd,"| %15lu ",cnt); break; case 1: cnt=lp->obytes; fprintf(fd,"| %15lu ",cnt); break; case 2: cnt=lp->ipackets; fprintf(fd,"| %15lu ",cnt); break; case 3: cnt=lp->ibytes; fprintf(fd,"| %15lu ",cnt); break; case 4: cnt=lp->ierrors; fprintf(fd,"| %15lu ",cnt); break; case 5: cnt=lp->oerrors; fprintf(fd,"| %15lu ",cnt); break; case 6: fprintf(fd,"| %15s ",double_to_human_str((double)lp->m_total_tx_bps,"bps",KBYE_1000).c_str()); break; default: cnt=0xffffff; } } /* ports */ fprintf(fd, "\n"); }/* fields*/ } } class CGlobalTRex { public: /** * different types of shutdown causes */ typedef enum { SHUTDOWN_NONE, SHUTDOWN_TEST_ENDED, SHUTDOWN_CTRL_C, SHUTDOWN_SIGINT, SHUTDOWN_SIGTERM, SHUTDOWN_RPC_REQ } shutdown_rc_e; CGlobalTRex (){ m_max_ports=4; m_max_cores=1; m_cores_to_dual_ports=0; m_max_queues_per_port=0; m_fl_was_init=false; m_expected_pps=0.0; m_expected_cps=0.0; m_expected_bps=0.0; m_trex_stateless = NULL; m_mark_for_shutdown = SHUTDOWN_NONE; } bool Create(); void Delete(); int ixgbe_prob_init(); int cores_prob_init(); int queues_prob_init(); int ixgbe_start(); int ixgbe_rx_queue_flush(); void ixgbe_configure_mg(); void rx_sl_configure(); bool is_all_links_are_up(bool dump=false); void pre_test(); /** * mark for shutdown * on the next check - the control plane will * call shutdown() */ void mark_for_shutdown(shutdown_rc_e rc) { if (is_marked_for_shutdown()) { return; } m_mark_for_shutdown = rc; } private: void register_signals(); /* try to stop all datapath cores and RX core */ void try_stop_all_cores(); /* send message to all dp cores */ int send_message_all_dp(TrexStatelessCpToDpMsgBase *msg); int send_message_to_rx(TrexStatelessCpToRxMsgBase *msg); void check_for_dp_message_from_core(int thread_id); bool is_marked_for_shutdown() const { return (m_mark_for_shutdown != SHUTDOWN_NONE); } /** * shutdown sequence * */ void shutdown(); public: void check_for_dp_messages(); int start_master_statefull(); int start_master_stateless(); int run_in_core(virtual_thread_id_t virt_core_id); int core_for_rx(){ if ( (! get_is_rx_thread_enabled()) ) { return -1; }else{ return m_max_cores - 1; } } int run_in_rx_core(); int run_in_master(); void handle_fast_path(); void handle_slow_path(); int stop_master(); /* return the minimum number of dp cores needed to support the active ports this is for c==1 or m_cores_mul==1 */ int get_base_num_cores(){ return (m_max_ports>>1); } int get_cores_tx(){ /* 0 - master num_of_cores - last for latency */ if ( (! get_is_rx_thread_enabled()) ) { return (m_max_cores - 1 ); } else { return (m_max_cores - BP_MASTER_AND_LATENCY ); } } private: bool is_all_cores_finished(); 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); void dump_template_info(std::string & json); bool sanity_check(); void update_stats(void); tx_per_flow_t get_flow_tx_stats(uint8_t port, uint16_t hw_id); tx_per_flow_t clear_flow_tx_stats(uint8_t port, uint16_t index, bool is_lat); void get_stats(CGlobalStats & stats); float get_cpu_util_per_interface(uint8_t port_id); void dump_post_test_stats(FILE *fd); void dump_config(FILE *fd); void dump_links_status(FILE *fd); public: port_cfg_t m_port_cfg; uint32_t m_max_ports; /* active number of ports supported options are 2,4,8,10,12 */ uint32_t m_max_cores; /* current number of cores , include master and latency ==> ( master)1+c*(m_max_ports>>1)+1( latency ) */ uint32_t m_cores_mul; /* how cores multipler given c=4 ==> m_cores_mul */ uint32_t m_max_queues_per_port; uint32_t m_cores_to_dual_ports; /* number of ports that will handle dual ports */ uint16_t m_latency_tx_queue_id; // statistic CPPSMeasure m_cps; float m_expected_pps; float m_expected_cps; float m_expected_bps;//bps float m_last_total_cps; CPhyEthIF m_ports[TREX_MAX_PORTS]; CCoreEthIF m_cores_vif_sf[BP_MAX_CORES]; /* counted from 1 , 2,3 core zero is reserved - stateful */ CCoreEthIFStateless m_cores_vif_sl[BP_MAX_CORES]; /* counted from 1 , 2,3 core zero is reserved - stateless*/ CCoreEthIF * m_cores_vif[BP_MAX_CORES]; CParserOption m_po ; CFlowGenList m_fl; bool m_fl_was_init; volatile uint8_t m_signal[BP_MAX_CORES] __rte_cache_aligned ; // Signal to main core when DP thread finished volatile bool m_sl_rx_running; // Signal main core when RX thread finished CLatencyManager m_mg; // statefull RX core CRxCoreStateless m_rx_sl; // stateless RX core CTrexGlobalIoMode m_io_modes; CTRexExtendedDriverBase * m_drv; private: CLatencyHWPort m_latency_vports[TREX_MAX_PORTS]; /* read hardware driver */ CLatencyVmPort m_latency_vm_vports[TREX_MAX_PORTS]; /* vm driver */ CLatencyPktInfo m_latency_pkt; TrexPublisher m_zmq_publisher; CGlobalStats m_stats; uint32_t m_stats_cnt; std::mutex m_cp_lock; TrexMonitor m_monitor; shutdown_rc_e m_mark_for_shutdown; public: TrexStateless *m_trex_stateless; }; // Before starting, send gratuitous ARP on our addresses, and try to resolve dst MAC addresses. void CGlobalTRex::pre_test() { CPretest pretest(m_max_ports); bool resolve_needed = false; uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0}; for (int port_id = 0; port_id < m_max_ports; port_id++) { CPhyEthIF *pif = &m_ports[port_id]; // Configure port to send all packets to software CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true); if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { resolve_needed = true; } else { resolve_needed = false; } if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) { rte_eth_macaddr_get(port_id, (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src); CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(true); } else { // If we got src MAC from config file, do not send gratuitous ARP for it (for compatibility with old behaviour) CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false); } pretest.set_port_params(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id] , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src , resolve_needed); } pretest.send_grat_arp_all(); bool ret; int count = 0; do { ret = pretest.resolve_all(); count++; } while ((ret != true) && (count < 10)); if ( CGlobalInfo::m_options.preview.getVMode() > 0) { pretest.dump(stdout); } uint8_t mac[ETHER_ADDR_LEN]; for (int port_id = 0; port_id < m_max_ports; port_id++) { if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { // we don't have dest MAC. Get it from what we resolved. uint32_t ip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); if (! pretest.get_mac(port_id, ip, mac)) { fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n" , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id); exit(1); } memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN); // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters. if (pretest.is_loopback(port_id)) CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false); } // update statistics baseline, so we can ignore what happened in pre test phase CPhyEthIF *pif = &m_ports[port_id]; CPreTestStats pre_stats = pretest.get_stats(port_id); pif->set_ignore_stats_base(pre_stats); // Configure port back to normal mode. Only relevant packets handled by software. CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false); } } /** * check for a single core * * @author imarom (19-Nov-15) * * @param thread_id */ void CGlobalTRex::check_for_dp_message_from_core(int thread_id) { CNodeRing *ring = CMsgIns::Ins()->getCpDp()->getRingDpToCp(thread_id); /* fast path check */ if ( likely ( ring->isEmpty() ) ) { return; } while ( true ) { CGenNode * node = NULL; if (ring->Dequeue(node) != 0) { break; } assert(node); TrexStatelessDpToCpMsgBase * msg = (TrexStatelessDpToCpMsgBase *)node; msg->handle(); delete msg; } } /** * check for messages that arrived from DP to CP * */ void CGlobalTRex::check_for_dp_messages() { /* for all the cores - check for a new message */ for (int i = 0; i < get_cores_tx(); i++) { check_for_dp_message_from_core(i); } } bool CGlobalTRex::is_all_links_are_up(bool dump){ bool all_link_are=true; int i; for (i=0; iget_port_attr()->update_link_status(); if ( dump ){ _if->dump_stats(stdout); } if ( _if->get_port_attr()->is_link_up() == false){ all_link_are=false; break; } } return (all_link_are); } void CGlobalTRex::try_stop_all_cores(){ TrexStatelessDpQuit * dp_msg= new TrexStatelessDpQuit(); TrexStatelessRxQuit * rx_msg= new TrexStatelessRxQuit(); send_message_all_dp(dp_msg); if (get_is_stateless()) { send_message_to_rx(rx_msg); } delete dp_msg; // no need to delete rx_msg. Deleted by receiver bool all_core_finished = false; int i; for (i=0; i<20; i++) { if ( is_all_cores_finished() ){ all_core_finished =true; break; } delay(100); } if ( all_core_finished ){ m_zmq_publisher.publish_event(TrexPublisher::EVENT_SERVER_STOPPED); printf(" All cores stopped !! \n"); }else{ printf(" ERROR one of the DP core is stucked !\n"); } } int CGlobalTRex::send_message_all_dp(TrexStatelessCpToDpMsgBase *msg){ int max_threads=(int)CMsgIns::Ins()->getCpDp()->get_num_threads(); int i; for (i=0; igetCpDp()->getRingCpToDp((uint8_t)i); ring->Enqueue((CGenNode*)msg->clone()); } return (0); } int CGlobalTRex::send_message_to_rx(TrexStatelessCpToRxMsgBase *msg) { CNodeRing *ring = CMsgIns::Ins()->getCpRx()->getRingCpToDp(0); ring->Enqueue((CGenNode *) msg); return (0); } int CGlobalTRex::ixgbe_rx_queue_flush(){ int i; for (i=0; iflush_rx_queue(); } return (0); } void CGlobalTRex::ixgbe_configure_mg(void) { int i; CLatencyManagerCfg mg_cfg; mg_cfg.m_max_ports = m_max_ports; uint32_t latency_rate=CGlobalInfo::m_options.m_latency_rate; if ( latency_rate ) { mg_cfg.m_cps = (double)latency_rate ; } else { // If RX core needed, we need something to make the scheduler running. // If nothing configured, send 1 CPS latency measurement packets. if (CGlobalInfo::m_options.m_arp_ref_per == 0) { mg_cfg.m_cps = 1.0; } else { mg_cfg.m_cps = 0; } } if ( get_vm_one_queue_enable() ) { /* vm mode, indirect queues */ for (i=0; igetRxDp(); uint8_t thread_id = (i>>1); CNodeRing * r = rx_dp->getRingCpToDp(thread_id); m_latency_vm_vports[i].Create((uint8_t)i,r,&m_mg); mg_cfg.m_ports[i] =&m_latency_vm_vports[i]; } }else{ for (i=0; idump_stats(stdout); m_latency_vports[i].Create(_if,m_latency_tx_queue_id,1); mg_cfg.m_ports[i] =&m_latency_vports[i]; } } m_mg.Create(&mg_cfg); m_mg.set_mask(CGlobalInfo::m_options.m_latency_mask); } // init m_rx_sl object for stateless rx core void CGlobalTRex::rx_sl_configure(void) { CRxSlCfg rx_sl_cfg; int i; rx_sl_cfg.m_max_ports = m_max_ports; if ( get_vm_one_queue_enable() ) { /* vm mode, indirect queues */ for (i=0; i < m_max_ports; i++) { CMessagingManager * rx_dp = CMsgIns::Ins()->getRxDp(); uint8_t thread_id = (i >> 1); CNodeRing * r = rx_dp->getRingCpToDp(thread_id); m_latency_vm_vports[i].Create((uint8_t)i, r, &m_mg); rx_sl_cfg.m_ports[i] = &m_latency_vm_vports[i]; } } else { for (i = 0; i < m_max_ports; i++) { CPhyEthIF * _if = &m_ports[i]; m_latency_vports[i].Create(_if, m_latency_tx_queue_id, 1); rx_sl_cfg.m_ports[i] = &m_latency_vports[i]; } } m_rx_sl.create(rx_sl_cfg); } int CGlobalTRex::ixgbe_start(void){ int i; for (i=0; iCreate((uint8_t)i); /* last TX queue if for latency check */ if ( get_vm_one_queue_enable() ) { /* one tx one rx */ /* VMXNET3 does claim to support 16K but somehow does not work */ /* reduce to 2000 */ m_port_cfg.m_port_conf.rxmode.max_rx_pkt_len = 2000; _if->configure(1, 1, &m_port_cfg.m_port_conf); /* will not be used */ m_latency_tx_queue_id= m_cores_to_dual_ports; socket_id_t socket_id = CGlobalInfo::m_socket.port_to_socket((port_id_t)i); assert(CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048); _if->set_rx_queue(0); _if->rx_queue_setup(0, RTE_TEST_RX_DESC_VM_DEFAULT, socket_id, &m_port_cfg.m_rx_conf, CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048); int qid; for ( qid=0; qid<(m_max_queues_per_port); qid++) { _if->tx_queue_setup((uint16_t)qid, RTE_TEST_TX_DESC_VM_DEFAULT , socket_id, &m_port_cfg.m_tx_conf); } }else{ _if->configure(2, m_cores_to_dual_ports+1, &m_port_cfg.m_port_conf); /* the latency queue for latency measurement packets */ m_latency_tx_queue_id= m_cores_to_dual_ports; socket_id_t socket_id = CGlobalInfo::m_socket.port_to_socket((port_id_t)i); assert(CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048); /* drop queue */ _if->rx_queue_setup(0, RTE_TEST_RX_DESC_DEFAULT, socket_id, &m_port_cfg.m_rx_conf, CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048); /* set the filter queue */ _if->set_rx_queue(1); /* latency measurement ring is 1 */ _if->rx_queue_setup(1, RTE_TEST_RX_LATENCY_DESC_DEFAULT, socket_id, &m_port_cfg.m_rx_conf, CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_9k); int qid; for ( qid=0; qid<(m_max_queues_per_port+1); qid++) { _if->tx_queue_setup((uint16_t)qid, RTE_TEST_TX_DESC_DEFAULT , socket_id, &m_port_cfg.m_tx_conf); } } _if->stats_clear(); _if->start(); _if->configure_rx_duplicate_rules(); if ( ! get_vm_one_queue_enable() && ! CGlobalInfo::m_options.preview.get_is_disable_flow_control_setting() && _if->get_port_attr()->is_fc_change_supported()) { _if->disable_flow_control(); } _if->get_port_attr()->add_mac((char *)CGlobalInfo::m_options.get_src_mac_addr(i)); fflush(stdout); } if ( !is_all_links_are_up() ){ /* wait for ports to be stable */ get_ex_drv()->wait_for_stable_link(); 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"); } } else { get_ex_drv()->wait_after_link_up(); } dump_links_status(stdout); ixgbe_rx_queue_flush(); if (! get_is_stateless()) { ixgbe_configure_mg(); } else { rx_sl_configure(); } /* core 0 - control core 1 - port 0-0,1-0, core 2 - port 2-0,3-0, core 3 - port 0-1,1-1, core 4 - port 2-1,3-1, */ int port_offset=0; uint8_t lat_q_id; if ( get_vm_one_queue_enable() ) { lat_q_id = 0; } else { lat_q_id = get_cores_tx() / get_base_num_cores(); } for (i=0; iCreate(j, queue_id, &m_ports[port_offset], /* 0,2*/ queue_id, &m_ports[port_offset+1], /*1,3*/ lat_q_id); port_offset+=2; if (port_offset == m_max_ports) { port_offset = 0; // We want to allow sending latency packets only from first core handling a port lat_q_id = CCoreEthIF::INVALID_Q_ID; } } fprintf(stdout," -------------------------------\n"); CCoreEthIF::DumpIfCfgHeader(stdout); for (i=0; iDumpIfCfg(stdout); } fprintf(stdout," -------------------------------\n"); return (0); } static void trex_termination_handler(int signum); void CGlobalTRex::register_signals() { struct sigaction action; /* handler */ action.sa_handler = trex_termination_handler; /* blocked signals during handling */ sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGINT); sigaddset(&action.sa_mask, SIGTERM); /* no flags */ action.sa_flags = 0; /* register */ sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); } bool CGlobalTRex::Create(){ CFlowsYamlInfo pre_yaml_info; register_signals(); m_stats_cnt =0; if (!get_is_stateless()) { pre_yaml_info.load_from_yaml_file(CGlobalInfo::m_options.cfg_file); } if ( !m_zmq_publisher.Create( CGlobalInfo::m_options.m_zmq_port, !CGlobalInfo::m_options.preview.get_zmq_publish_enable() ) ){ return (false); } if ( pre_yaml_info.m_vlan_info.m_enable ){ CGlobalInfo::m_options.preview.set_vlan_mode_enable(true); } /* End update pre flags */ ixgbe_prob_init(); cores_prob_init(); queues_prob_init(); /* allocate rings */ assert( CMsgIns::Ins()->Create(get_cores_tx()) ); if ( sizeof(CGenNodeNatInfo) != sizeof(CGenNode) ) { printf("ERROR sizeof(CGenNodeNatInfo) %lu != sizeof(CGenNode) %lu must be the same size \n",sizeof(CGenNodeNatInfo),sizeof(CGenNode)); assert(0); } if ( sizeof(CGenNodeLatencyPktInfo) != sizeof(CGenNode) ) { printf("ERROR sizeof(CGenNodeLatencyPktInfo) %lu != sizeof(CGenNode) %lu must be the same size \n",sizeof(CGenNodeLatencyPktInfo),sizeof(CGenNode)); assert(0); } /* allocate the memory */ uint32_t rx_mbuf = 0 ; if ( get_vm_one_queue_enable() ) { rx_mbuf = (m_max_ports * RTE_TEST_RX_DESC_VM_DEFAULT); }else{ rx_mbuf = (m_max_ports * (RTE_TEST_RX_LATENCY_DESC_DEFAULT+RTE_TEST_RX_DESC_DEFAULT)); } CGlobalInfo::init_pools(rx_mbuf); ixgbe_start(); dump_config(stdout); /* start stateless */ if (get_is_stateless()) { TrexStatelessCfg cfg; TrexRpcServerConfig rpc_req_resp_cfg(TrexRpcServerConfig::RPC_PROT_TCP, global_platform_cfg_info.m_zmq_rpc_port, &m_cp_lock); cfg.m_port_count = CGlobalInfo::m_options.m_expected_portd; cfg.m_rpc_req_resp_cfg = &rpc_req_resp_cfg; cfg.m_rpc_server_verbose = false; cfg.m_platform_api = new TrexDpdkPlatformApi(); cfg.m_publisher = &m_zmq_publisher; m_trex_stateless = new TrexStateless(cfg); } return (true); } void CGlobalTRex::Delete(){ m_zmq_publisher.Delete(); } int CGlobalTRex::ixgbe_prob_init(void){ m_max_ports = rte_eth_dev_count(); if (m_max_ports == 0) rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); printf(" Number of ports found: %d \n",m_max_ports); if ( m_max_ports %2 !=0 ) { rte_exit(EXIT_FAILURE, " Number of ports %d should be even, mask the one port in the configuration file \n, ", m_max_ports); } if ( CGlobalInfo::m_options.get_expected_ports() > TREX_MAX_PORTS ) { rte_exit(EXIT_FAILURE, " Maximum ports supported are %d, use the configuration file to set the expected number of ports \n",TREX_MAX_PORTS); } if ( CGlobalInfo::m_options.get_expected_ports() > m_max_ports ){ rte_exit(EXIT_FAILURE, " There are %d ports you expected more %d,use the configuration file to set the expected number of ports \n", m_max_ports, CGlobalInfo::m_options.get_expected_ports()); } if (CGlobalInfo::m_options.get_expected_ports() < m_max_ports ) { /* limit the number of ports */ m_max_ports=CGlobalInfo::m_options.get_expected_ports(); } assert(m_max_ports <= TREX_MAX_PORTS); struct rte_eth_dev_info dev_info; rte_eth_dev_info_get((uint8_t) 0,&dev_info); if ( CGlobalInfo::m_options.preview.getVMode() > 0){ printf("\n\n"); printf("if_index : %d \n",dev_info.if_index); printf("driver name : %s \n",dev_info.driver_name); printf("min_rx_bufsize : %d \n",dev_info.min_rx_bufsize); printf("max_rx_pktlen : %d \n",dev_info.max_rx_pktlen); printf("max_rx_queues : %d \n",dev_info.max_rx_queues); printf("max_tx_queues : %d \n",dev_info.max_tx_queues); printf("max_mac_addrs : %d \n",dev_info.max_mac_addrs); printf("rx_offload_capa : %x \n",dev_info.rx_offload_capa); printf("tx_offload_capa : %x \n",dev_info.tx_offload_capa); } if ( !CTRexExtendedDriverDb::Ins()->is_driver_exists(dev_info.driver_name) ){ printf(" Error: driver %s is not supported. Please consult the documentation for a list of supported drivers\n" ,dev_info.driver_name); exit(1); } int i; struct rte_eth_dev_info dev_info1; for (i=1; iset_driver_name(dev_info.driver_name); m_drv = CTRexExtendedDriverDb::Ins()->get_drv(); // check if firmware version is new enough for (i = 0; i < m_max_ports; i++) { if (m_drv->verify_fw_ver(i) < 0) { // error message printed by verify_fw_ver exit(1); } } m_port_cfg.update_var(); if ( get_is_rx_filter_enable() ){ m_port_cfg.update_global_config_fdir(); } if ( get_vm_one_queue_enable() ) { /* verify that we have only one thread/core per dual- interface */ if ( CGlobalInfo::m_options.preview.getCores()>1 ) { printf(" ERROR the number of cores should be 1 when the driver support only one tx queue and one rx queue \n"); exit(1); } } return (0); } int CGlobalTRex::cores_prob_init(){ m_max_cores = rte_lcore_count(); assert(m_max_cores>0); return (0); } int CGlobalTRex::queues_prob_init(){ if (m_max_cores < 2) { rte_exit(EXIT_FAILURE, "number of cores should be at least 2 \n"); } assert((m_max_ports>>1) <= get_cores_tx() ); m_cores_mul = CGlobalInfo::m_options.preview.getCores(); m_cores_to_dual_ports = m_cores_mul; /* core 0 - control -core 1 - port 0/1 -core 2 - port 2/3 -core 3 - port 0/1 -core 4 - port 2/3 m_cores_to_dual_ports = 2; */ /* number of queue - 1 per core for dual ports*/ m_max_queues_per_port = m_cores_to_dual_ports; if (m_max_queues_per_port > BP_MAX_TX_QUEUE) { rte_exit(EXIT_FAILURE, "maximum number of queue should be maximum %d \n",BP_MAX_TX_QUEUE); } assert(m_max_queues_per_port>0); return (0); } void CGlobalTRex::dump_config(FILE *fd){ fprintf(fd," number of ports : %u \n",m_max_ports); fprintf(fd," max cores for 2 ports : %u \n",m_cores_to_dual_ports); fprintf(fd," max queue per port : %u \n",m_max_queues_per_port); } void CGlobalTRex::dump_links_status(FILE *fd){ for (int i=0; iupdate_link_status_nowait(); m_ports[i].get_port_attr()->dump_link(fd); } } void CGlobalTRex::dump_post_test_stats(FILE *fd){ uint64_t pkt_out=0; uint64_t pkt_out_bytes=0; uint64_t pkt_in_bytes=0; uint64_t pkt_in=0; uint64_t sw_pkt_out=0; uint64_t sw_pkt_out_err=0; uint64_t sw_pkt_out_bytes=0; uint64_t tx_arp = 0; uint64_t rx_arp = 0; int i; for (i=0; iGetCoreCounters(&stats); sw_pkt_out += stats.m_tx_pkt; sw_pkt_out_err += stats.m_tx_drop +stats.m_tx_queue_full +stats.m_tx_alloc_error ; sw_pkt_out_bytes +=stats.m_tx_bytes; } for (i=0; iget_stats().ipackets; pkt_in_bytes +=_if->get_stats().ibytes; pkt_out +=_if->get_stats().opackets; pkt_out_bytes +=_if->get_stats().obytes; tx_arp += _if->get_ignore_stats().get_tx_arp(); rx_arp += _if->get_ignore_stats().get_rx_arp(); } if ( CGlobalInfo::m_options.is_latency_enabled() ){ sw_pkt_out += m_mg.get_total_pkt(); sw_pkt_out_bytes +=m_mg.get_total_bytes(); } fprintf (fd," summary stats \n"); fprintf (fd," -------------- \n"); if (pkt_in > pkt_out) { fprintf (fd, " Total-pkt-drop : 0 pkts \n"); if (pkt_in > pkt_out * 1.01) fprintf (fd, " Warning : number of rx packets exceeds 101%% of tx packets!\n"); } else fprintf (fd, " Total-pkt-drop : %llu pkts \n", (unsigned long long) (pkt_out - pkt_in)); fprintf (fd," Total-tx-bytes : %llu bytes \n", (unsigned long long)pkt_out_bytes); fprintf (fd," Total-tx-sw-bytes : %llu bytes \n", (unsigned long long)sw_pkt_out_bytes); fprintf (fd," Total-rx-bytes : %llu byte \n", (unsigned long long)pkt_in_bytes); fprintf (fd," \n"); fprintf (fd," Total-tx-pkt : %llu pkts \n", (unsigned long long)pkt_out); fprintf (fd," Total-rx-pkt : %llu pkts \n", (unsigned long long)pkt_in); fprintf (fd," Total-sw-tx-pkt : %llu pkts \n", (unsigned long long)sw_pkt_out); fprintf (fd," Total-sw-err : %llu pkts \n", (unsigned long long)sw_pkt_out_err); fprintf (fd," Total ARP sent : %llu pkts \n", (unsigned long long)tx_arp); fprintf (fd," Total ARP received : %llu pkts \n", (unsigned long long)rx_arp); if ( CGlobalInfo::m_options.is_latency_enabled() ){ fprintf (fd," maximum-latency : %.0f usec \n",m_mg.get_max_latency()); fprintf (fd," average-latency : %.0f usec \n",m_mg.get_avr_latency()); fprintf (fd," latency-any-error : %s \n",m_mg.is_any_error()?"ERROR":"OK"); } } void CGlobalTRex::update_stats(){ int i; for (i=0; iupdate_counters(); } uint64_t total_open_flows=0; CFlowGenListPerThread * lpt; for (i=0; im_stats.m_total_open_flows ; } m_last_total_cps = m_cps.add(total_open_flows); } tx_per_flow_t CGlobalTRex::get_flow_tx_stats(uint8_t port, uint16_t index) { return m_stats.m_port[port].m_tx_per_flow[index] - m_stats.m_port[port].m_prev_tx_per_flow[index]; } // read stats. Return read value, and clear. tx_per_flow_t CGlobalTRex::clear_flow_tx_stats(uint8_t port, uint16_t index, bool is_lat) { uint8_t port0; CFlowGenListPerThread * lpt; tx_per_flow_t ret; m_stats.m_port[port].m_tx_per_flow[index].clear(); for (int i=0; i < get_cores_tx(); i++) { lpt = m_fl.m_threads_info[i]; port0 = lpt->getDualPortId() * 2; if ((port == port0) || (port == port0 + 1)) { m_stats.m_port[port].m_tx_per_flow[index] += lpt->m_node_gen.m_v_if->m_stats[port - port0].m_tx_per_flow[index]; if (is_lat) lpt->m_node_gen.m_v_if->m_stats[port - port0].m_lat_data[index - MAX_FLOW_STATS].reset(); } } ret = m_stats.m_port[port].m_tx_per_flow[index] - m_stats.m_port[port].m_prev_tx_per_flow[index]; // Since we return diff from prev, following "clears" the stats. m_stats.m_port[port].m_prev_tx_per_flow[index] = m_stats.m_port[port].m_tx_per_flow[index]; return ret; } void CGlobalTRex::get_stats(CGlobalStats & stats){ int i; float total_tx=0.0; float total_rx=0.0; float total_tx_pps=0.0; float total_rx_pps=0.0; stats.m_total_tx_pkts = 0; stats.m_total_rx_pkts = 0; stats.m_total_tx_bytes = 0; stats.m_total_rx_bytes = 0; stats.m_total_alloc_error=0; stats.m_total_queue_full=0; stats.m_total_queue_drop=0; stats.m_num_of_ports = m_max_ports; stats.m_cpu_util = m_fl.GetCpuUtil(); stats.m_cpu_util_raw = m_fl.GetCpuUtilRaw(); if (get_is_stateless()) { stats.m_rx_cpu_util = m_rx_sl.get_cpu_util(); } stats.m_threads = m_fl.m_threads_info.size(); for (i=0; iget_stats(); stp->opackets = st.opackets; stp->obytes = st.obytes; stp->ipackets = st.ipackets; stp->ibytes = st.ibytes; stp->ierrors = st.ierrors; stp->oerrors = st.oerrors; stp->m_total_tx_bps = _if->get_last_tx_rate()*_1Mb_DOUBLE; stp->m_total_tx_pps = _if->get_last_tx_pps_rate(); stp->m_total_rx_bps = _if->get_last_rx_rate()*_1Mb_DOUBLE; stp->m_total_rx_pps = _if->get_last_rx_pps_rate(); stats.m_total_tx_pkts += st.opackets; stats.m_total_rx_pkts += st.ipackets; stats.m_total_tx_bytes += st.obytes; stats.m_total_rx_bytes += st.ibytes; total_tx +=_if->get_last_tx_rate(); total_rx +=_if->get_last_rx_rate(); total_tx_pps +=_if->get_last_tx_pps_rate(); total_rx_pps +=_if->get_last_rx_pps_rate(); // IP ID rules for (uint16_t flow = 0; flow <= max_stat_hw_id_seen; flow++) { stats.m_port[i].m_tx_per_flow[flow].clear(); } // payload rules for (uint16_t flow = MAX_FLOW_STATS; flow <= MAX_FLOW_STATS + max_stat_hw_id_seen_payload; flow++) { stats.m_port[i].m_tx_per_flow[flow].clear(); } stp->m_cpu_util = get_cpu_util_per_interface(i); } uint64_t total_open_flows=0; uint64_t total_active_flows=0; uint64_t total_clients=0; uint64_t total_servers=0; uint64_t active_sockets=0; uint64_t total_sockets=0; uint64_t total_nat_time_out =0; uint64_t total_nat_time_out_wait_ack =0; uint64_t total_nat_no_fid =0; uint64_t total_nat_active =0; uint64_t total_nat_syn_wait = 0; uint64_t total_nat_open =0; uint64_t total_nat_learn_error=0; CFlowGenListPerThread * lpt; stats.m_template.Clear(); for (i=0; im_stats.m_total_open_flows ; total_active_flows += (lpt->m_stats.m_total_open_flows-lpt->m_stats.m_total_close_flows) ; stats.m_total_alloc_error += lpt->m_node_gen.m_v_if->m_stats[0].m_tx_alloc_error+ lpt->m_node_gen.m_v_if->m_stats[1].m_tx_alloc_error; stats.m_total_queue_full +=lpt->m_node_gen.m_v_if->m_stats[0].m_tx_queue_full+ lpt->m_node_gen.m_v_if->m_stats[1].m_tx_queue_full; stats.m_total_queue_drop +=lpt->m_node_gen.m_v_if->m_stats[0].m_tx_drop+ lpt->m_node_gen.m_v_if->m_stats[1].m_tx_drop; stats.m_template.Add(&lpt->m_node_gen.m_v_if->m_stats[0].m_template); stats.m_template.Add(&lpt->m_node_gen.m_v_if->m_stats[1].m_template); total_clients += lpt->m_smart_gen.getTotalClients(); total_servers += lpt->m_smart_gen.getTotalServers(); active_sockets += lpt->m_smart_gen.ActiveSockets(); total_sockets += lpt->m_smart_gen.MaxSockets(); total_nat_time_out +=lpt->m_stats.m_nat_flow_timeout; total_nat_time_out_wait_ack += lpt->m_stats.m_nat_flow_timeout_wait_ack; total_nat_no_fid +=lpt->m_stats.m_nat_lookup_no_flow_id ; total_nat_active +=lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_remove_flow_id; total_nat_syn_wait += lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_wait_ack_state; total_nat_open +=lpt->m_stats.m_nat_lookup_add_flow_id; total_nat_learn_error +=lpt->m_stats.m_nat_flow_learn_error; uint8_t port0 = lpt->getDualPortId() *2; // IP ID rules for (uint16_t flow = 0; flow <= max_stat_hw_id_seen; flow++) { stats.m_port[port0].m_tx_per_flow[flow] += lpt->m_node_gen.m_v_if->m_stats[0].m_tx_per_flow[flow]; stats.m_port[port0 + 1].m_tx_per_flow[flow] += lpt->m_node_gen.m_v_if->m_stats[1].m_tx_per_flow[flow]; } // payload rules for (uint16_t flow = MAX_FLOW_STATS; flow <= MAX_FLOW_STATS + max_stat_hw_id_seen_payload; flow++) { stats.m_port[port0].m_tx_per_flow[flow] += lpt->m_node_gen.m_v_if->m_stats[0].m_tx_per_flow[flow]; stats.m_port[port0 + 1].m_tx_per_flow[flow] += lpt->m_node_gen.m_v_if->m_stats[1].m_tx_per_flow[flow]; } } stats.m_total_nat_time_out = total_nat_time_out; stats.m_total_nat_time_out_wait_ack = total_nat_time_out_wait_ack; stats.m_total_nat_no_fid = total_nat_no_fid; stats.m_total_nat_active = total_nat_active; stats.m_total_nat_syn_wait = total_nat_syn_wait; stats.m_total_nat_open = total_nat_open; stats.m_total_nat_learn_error = total_nat_learn_error; stats.m_total_clients = total_clients; stats.m_total_servers = total_servers; stats.m_active_sockets = active_sockets; if (total_sockets != 0) { stats.m_socket_util =100.0*(double)active_sockets/(double)total_sockets; } else { stats.m_socket_util = 0; } float drop_rate=total_tx-total_rx; if ( (drop_rate<0.0) || (drop_rate < 0.1*total_tx ) ) { drop_rate=0.0; } float pf =CGlobalInfo::m_options.m_platform_factor; stats.m_platform_factor = pf; stats.m_active_flows = total_active_flows*pf; stats.m_open_flows = total_open_flows*pf; stats.m_rx_drop_bps = drop_rate*pf *_1Mb_DOUBLE; stats.m_tx_bps = total_tx*pf*_1Mb_DOUBLE; stats.m_rx_bps = total_rx*pf*_1Mb_DOUBLE; stats.m_tx_pps = total_tx_pps*pf; stats.m_rx_pps = total_rx_pps*pf; stats.m_tx_cps = m_last_total_cps*pf; if(stats.m_cpu_util < 0.0001) stats.m_bw_per_core = 0; else stats.m_bw_per_core = 2*(stats.m_tx_bps/1e9)*100.0/(stats.m_cpu_util*stats.m_threads); stats.m_tx_expected_cps = m_expected_cps*pf; stats.m_tx_expected_pps = m_expected_pps*pf; stats.m_tx_expected_bps = m_expected_bps*pf; } float CGlobalTRex::get_cpu_util_per_interface(uint8_t port_id) { CPhyEthIF * _if = &m_ports[port_id]; float tmp = 0; uint8_t cnt = 0; for (const auto &p : _if->get_core_list()) { uint8_t core_id = p.first; CFlowGenListPerThread *lp = m_fl.m_threads_info[core_id]; if (lp->is_port_active(port_id)) { tmp += lp->m_cpu_cp_u.GetVal(); cnt++; } } return ( (cnt > 0) ? (tmp / cnt) : 0); } bool CGlobalTRex::sanity_check(){ CFlowGenListPerThread * lpt; uint32_t errors=0; int i; for (i=0; im_smart_gen.getErrorAllocationCounter(); } if ( errors ) { printf(" ERRORs sockets allocation errors! \n"); printf(" you should allocate more clients in the pool \n"); return(true); } return ( false); } /* dump the template info */ void CGlobalTRex::dump_template_info(std::string & json){ CFlowGenListPerThread * lpt = m_fl.m_threads_info[0]; CFlowsYamlInfo * yaml_info=&lpt->m_yaml_info; json="{\"name\":\"template_info\",\"type\":0,\"data\":["; int i; for (i=0; im_vec.size()-1; i++) { CFlowYamlInfo * r=&yaml_info->m_vec[i] ; json+="\""+ r->m_name+"\""; json+=","; } json+="\""+yaml_info->m_vec[i].m_name+"\""; json+="]}" ; } void CGlobalTRex::dump_stats(FILE *fd, CGlobalStats::DumpFormat format){ update_stats(); get_stats(m_stats); if (format==CGlobalStats::dmpTABLE) { if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNORMAL ){ switch (m_io_modes.m_pp_mode ){ case CTrexGlobalIoMode::ppDISABLE: fprintf(fd,"\n+Per port stats disabled \n"); break; case CTrexGlobalIoMode::ppTABLE: fprintf(fd,"\n-Per port stats table \n"); m_stats.Dump(fd,CGlobalStats::dmpTABLE); break; case CTrexGlobalIoMode::ppSTANDARD: fprintf(fd,"\n-Per port stats - standard\n"); m_stats.Dump(fd,CGlobalStats::dmpSTANDARD); break; }; switch (m_io_modes.m_ap_mode ){ case CTrexGlobalIoMode::apDISABLE: fprintf(fd,"\n+Global stats disabled \n"); break; case CTrexGlobalIoMode::apENABLE: fprintf(fd,"\n-Global stats enabled \n"); m_stats.DumpAllPorts(fd); break; }; } }else{ /* at exit , always need to dump it in standartd mode for scripts*/ m_stats.Dump(fd,format); m_stats.DumpAllPorts(fd); } } void CGlobalTRex::publish_async_data(bool sync_now, bool baseline) { std::string json; /* refactor to update, dump, and etc. */ if (sync_now) { update_stats(); get_stats(m_stats); } m_stats.dump_json(json, baseline); m_zmq_publisher.publish_json(json); /* generator json , all cores are the same just sample the first one */ m_fl.m_threads_info[0]->m_node_gen.dump_json(json); m_zmq_publisher.publish_json(json); if ( !get_is_stateless() ){ dump_template_info(json); m_zmq_publisher.publish_json(json); } if ( get_is_rx_check_mode() ) { m_mg.rx_check_dump_json(json ); m_zmq_publisher.publish_json(json); } /* backward compatible */ m_mg.dump_json(json ); m_zmq_publisher.publish_json(json); /* more info */ m_mg.dump_json_v2(json ); m_zmq_publisher.publish_json(json); if (get_is_stateless()) { std::string stat_json; std::string latency_json; if (m_trex_stateless->m_rx_flow_stat.dump_json(stat_json, latency_json, baseline)) { m_zmq_publisher.publish_json(stat_json); m_zmq_publisher.publish_json(latency_json); } } } void 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; TRexPortAttr * _attr = m_ports[port_id].get_port_attr(); /* attributes */ data["attr"]["speed"] = _attr->get_link_speed(); data["attr"]["promiscuous"]["enabled"] = _attr->get_promiscuous(); data["attr"]["link"]["up"] = _attr->is_link_up(); data["attr"]["rx_filter_mode"] = _attr->get_rx_filter_mode(); int mode; int ret = _attr->get_flow_ctrl(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() { m_stats_cnt+=1; // update speed, link up/down etc. for (int i=0; iupdate_link_status_nowait(); if (changed) { publish_async_port_attr_changed(i); } } if ( CGlobalInfo::m_options.preview.get_no_keyboard() ==false ) { if ( m_io_modes.handle_io_modes() ) { mark_for_shutdown(SHUTDOWN_CTRL_C); return; } } if ( sanity_check() ) { mark_for_shutdown(SHUTDOWN_TEST_ENDED); return; } if (m_io_modes.m_g_mode != CTrexGlobalIoMode::gDISABLE ) { fprintf(stdout,"\033[2J"); fprintf(stdout,"\033[2H"); } else { if ( m_io_modes.m_g_disable_first ) { m_io_modes.m_g_disable_first=false; fprintf(stdout,"\033[2J"); fprintf(stdout,"\033[2H"); printf("clean !!!\n"); fflush(stdout); } } if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gHELP ) { m_io_modes.DumpHelp(stdout); } dump_stats(stdout,CGlobalStats::dmpTABLE); if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gNORMAL ) { fprintf (stdout," current time : %.1f sec \n",now_sec()); float d= CGlobalInfo::m_options.m_duration - now_sec(); if (d<0) { d=0; } fprintf (stdout," test duration : %.1f sec \n",d); } if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gMem) { if ( m_stats_cnt%4==0) { fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json_str().c_str()); } } if ( CGlobalInfo::m_options.is_rx_enabled() && (! get_is_stateless())) { m_mg.update(); if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNORMAL ) { switch (m_io_modes.m_l_mode) { case CTrexGlobalIoMode::lDISABLE: fprintf(stdout,"\n+Latency stats disabled \n"); break; case CTrexGlobalIoMode::lENABLE: fprintf(stdout,"\n-Latency stats enabled \n"); m_mg.DumpShort(stdout); break; case CTrexGlobalIoMode::lENABLE_Extended: fprintf(stdout,"\n-Latency stats extended \n"); m_mg.Dump(stdout); break; } if ( get_is_rx_check_mode() ) { switch (m_io_modes.m_rc_mode) { case CTrexGlobalIoMode::rcDISABLE: fprintf(stdout,"\n+Rx Check stats disabled \n"); break; case CTrexGlobalIoMode::rcENABLE: fprintf(stdout,"\n-Rx Check stats enabled \n"); m_mg.DumpShortRxCheck(stdout); break; case CTrexGlobalIoMode::rcENABLE_Extended: fprintf(stdout,"\n-Rx Check stats enhanced \n"); m_mg.DumpRxCheck(stdout); break; } } } } if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNAT ) { if ( m_io_modes.m_nat_mode == CTrexGlobalIoMode::natENABLE ) { if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { fprintf(stdout, "NAT flow table info\n"); m_mg.dump_nat_flow_table(stdout); } else { fprintf(stdout, "\nThis is only relevant in --learn-mode %d\n", CParserOption::LEARN_MODE_TCP_ACK); } } } /* publish data */ publish_async_data(false); } void CGlobalTRex::handle_fast_path() { /* check from messages from DP */ check_for_dp_messages(); /* measure CPU utilization by sampling (we sample 1000 to get an accurate sampling) */ for (int i = 0; i < 1000; i++) { m_fl.UpdateFast(); if (get_is_stateless()) { m_rx_sl.update_cpu_util(); }else{ m_mg.update_fast(); } rte_pause(); } if ( is_all_cores_finished() ) { mark_for_shutdown(SHUTDOWN_TEST_ENDED); } } /** * shutdown sequence * */ void CGlobalTRex::shutdown() { std::stringstream ss; ss << " *** TRex is shutting down - cause: '"; switch (m_mark_for_shutdown) { case SHUTDOWN_TEST_ENDED: ss << "test has ended'"; break; case SHUTDOWN_CTRL_C: ss << "CTRL + C detected'"; break; case SHUTDOWN_SIGINT: ss << "received signal SIGINT'"; break; case SHUTDOWN_SIGTERM: ss << "received signal SIGTERM'"; break; case SHUTDOWN_RPC_REQ: ss << "server received RPC 'shutdown' request'"; break; default: assert(0); } /* report */ std::cout << ss.str() << "\n"; /* first stop the WD */ TrexWatchDog::getInstance().stop(); /* stateless shutdown */ if (get_is_stateless()) { m_trex_stateless->shutdown(); } if (!is_all_cores_finished()) { try_stop_all_cores(); } m_mg.stop(); delay(1000); /* shutdown drivers */ for (int i = 0; i < m_max_ports; i++) { m_ports[i].stop(); } if (m_mark_for_shutdown != SHUTDOWN_TEST_ENDED) { /* we should stop latency and exit to stop agents */ exit(-1); } } int CGlobalTRex::run_in_master() { //rte_thread_setname(pthread_self(), "TRex Control"); if ( get_is_stateless() ) { m_trex_stateless->launch_control_plane(); } /* exception and scope safe */ std::unique_lock cp_lock(m_cp_lock); uint32_t slow_path_counter = 0; const int FASTPATH_DELAY_MS = 10; const int SLOWPATH_DELAY_MS = 500; m_monitor.create("master", 2); TrexWatchDog::getInstance().register_monitor(&m_monitor); TrexWatchDog::getInstance().start(); while (!is_marked_for_shutdown()) { /* fast path */ handle_fast_path(); /* slow path */ if (slow_path_counter >= SLOWPATH_DELAY_MS) { handle_slow_path(); slow_path_counter = 0; } cp_lock.unlock(); delay(FASTPATH_DELAY_MS); slow_path_counter += FASTPATH_DELAY_MS; cp_lock.lock(); m_monitor.tickle(); } /* on exit release the lock */ cp_lock.unlock(); /* shutdown everything gracefully */ shutdown(); return (0); } int CGlobalTRex::run_in_rx_core(void){ rte_thread_setname(pthread_self(), "TRex RX"); if (get_is_stateless()) { m_sl_rx_running = true; m_rx_sl.start(); m_sl_rx_running = false; } else { if ( CGlobalInfo::m_options.is_rx_enabled() ){ m_sl_rx_running = false; m_mg.start(0, true); } } return (0); } int CGlobalTRex::run_in_core(virtual_thread_id_t virt_core_id){ std::stringstream ss; ss << "Trex DP core " << int(virt_core_id); rte_thread_setname(pthread_self(), ss.str().c_str()); CPreviewMode *lp=&CGlobalInfo::m_options.preview; if ( lp->getSingleCore() && (virt_core_id==2 ) && (lp-> getCores() ==1) ){ printf(" bypass this core \n"); m_signal[virt_core_id]=1; return (0); } assert(m_fl_was_init); CFlowGenListPerThread * lpt; lpt = m_fl.m_threads_info[virt_core_id-1]; /* register a watchdog handle on current core */ lpt->m_monitor.create(ss.str(), 1); TrexWatchDog::getInstance().register_monitor(&lpt->m_monitor); if (get_is_stateless()) { lpt->start_stateless_daemon(*lp); }else{ lpt->start_generate_stateful(CGlobalInfo::m_options.out_file,*lp); } /* done - remove this from the watchdog (we might wait on join for a long time) */ lpt->m_monitor.disable(); m_signal[virt_core_id]=1; return (0); } int CGlobalTRex::stop_master(){ delay(1000); fprintf(stdout," ==================\n"); fprintf(stdout," interface sum \n"); fprintf(stdout," ==================\n"); dump_stats(stdout,CGlobalStats::dmpSTANDARD); fprintf(stdout," ==================\n"); fprintf(stdout," \n\n"); fprintf(stdout," ==================\n"); fprintf(stdout," interface sum \n"); fprintf(stdout," ==================\n"); CFlowGenListPerThread * lpt; uint64_t total_tx_rx_check=0; int i; for (i=0; iDumpCoreStats(stdout); erf_vif->DumpIfStats(stdout); total_tx_rx_check+=erf_vif->m_stats[CLIENT_SIDE].m_tx_rx_check_pkt+ erf_vif->m_stats[SERVER_SIDE].m_tx_rx_check_pkt; } fprintf(stdout," ==================\n"); fprintf(stdout," generators \n"); fprintf(stdout," ==================\n"); for (i=0; im_node_gen.DumpHist(stdout); lpt->DumpStats(stdout); } if ( CGlobalInfo::m_options.is_latency_enabled() ){ fprintf(stdout," ==================\n"); fprintf(stdout," latency \n"); fprintf(stdout," ==================\n"); m_mg.DumpShort(stdout); m_mg.Dump(stdout); m_mg.DumpShortRxCheck(stdout); m_mg.DumpRxCheck(stdout); m_mg.DumpRxCheckVerification(stdout,total_tx_rx_check); } dump_stats(stdout,CGlobalStats::dmpSTANDARD); dump_post_test_stats(stdout); m_fl.Delete(); return (0); } bool CGlobalTRex::is_all_cores_finished() { int i; for (i=0; iset_vif(erf_vif); lpt->m_node_gen.m_socket_id =m_cores_vif[i+1]->get_socket_id(); } m_fl_was_init=true; return (0); } int CGlobalTRex::start_master_statefull() { int i; for (i=0; i 2000) { /* disable flows cache */ CGlobalInfo::m_options.preview.setDisableMbufCache(true); } CTupleGenYamlInfo * tg=&m_fl.m_yaml_info.m_tuple_gen; m_mg.set_ip( tg->m_client_pool[0].get_ip_start(), tg->m_server_pool[0].get_ip_start(), tg->m_client_pool[0].getDualMask() ); if ( CGlobalInfo::m_options.preview.getVMode() >0 ) { m_fl.DumpCsv(stdout); for (i=0; i<100; i++) { fprintf(stdout,"\n"); } fflush(stdout); } m_fl.generate_p_thread_info(get_cores_tx()); CFlowGenListPerThread * lpt; for (i=0; iset_vif(erf_vif); /* socket id */ lpt->m_node_gen.m_socket_id =m_cores_vif[i+1]->get_socket_id(); } m_fl_was_init=true; return (0); } //////////////////////////////////////////// static CGlobalTRex g_trex; void CPhyEthIF::update_counters() { get_ex_drv()->get_extended_stats(this, &m_stats); CRXCoreIgnoreStat ign_stats; g_trex.m_mg.get_ignore_stats(m_port_id, ign_stats, true); m_stats.obytes -= ign_stats.get_tx_bytes(); m_stats.opackets -= ign_stats.get_tx_pkts(); m_ignore_stats.opackets += ign_stats.get_tx_pkts(); m_ignore_stats.obytes += ign_stats.get_tx_bytes(); m_ignore_stats.m_tx_arp += ign_stats.get_tx_arp(); m_last_tx_rate = m_bw_tx.add(m_stats.obytes); m_last_rx_rate = m_bw_rx.add(m_stats.ibytes); m_last_tx_pps = m_pps_tx.add(m_stats.opackets); m_last_rx_pps = m_pps_rx.add(m_stats.ipackets); } bool CPhyEthIF::Create(uint8_t portid) { m_port_id = portid; m_last_rx_rate = 0.0; m_last_tx_rate = 0.0; m_last_tx_pps = 0.0; m_port_attr = g_trex.m_drv->create_port_attr(portid); return true; } const std::vector> & CPhyEthIF::get_core_list() { /* lazy find */ if (m_core_id_list.size() == 0) { for (uint8_t core_id = 0; core_id < g_trex.get_cores_tx(); core_id++) { /* iterate over all the directions*/ for (uint8_t dir = 0 ; dir < CS_NUM; dir++) { if (g_trex.m_cores_vif[core_id + 1]->get_ports()[dir].m_port->get_port_id() == m_port_id) { m_core_id_list.push_back(std::make_pair(core_id, dir)); } } } } return m_core_id_list; } int CPhyEthIF::reset_hw_flow_stats() { if (get_ex_drv()->hw_rx_stat_supported()) { get_ex_drv()->reset_rx_stats(this, m_stats.m_fdir_prev_pkts, 0, MAX_FLOW_STATS); } else { g_trex.m_rx_sl.reset_rx_stats(get_port_id()); } return 0; } // get/reset flow director counters // return 0 if OK. -1 if operation not supported. // rx_stats, tx_stats - arrays of len max - min + 1. Returning rx, tx updated absolute values. // min, max - minimum, maximum counters range to get // reset - If true, need to reset counter value after reading int CPhyEthIF::get_flow_stats(rx_per_flow_t *rx_stats, tx_per_flow_t *tx_stats, int min, int max, bool reset) { uint32_t diff_pkts[MAX_FLOW_STATS]; uint32_t diff_bytes[MAX_FLOW_STATS]; bool hw_rx_stat_supported = get_ex_drv()->hw_rx_stat_supported(); if (hw_rx_stat_supported) { if (get_ex_drv()->get_rx_stats(this, diff_pkts, m_stats.m_fdir_prev_pkts , diff_bytes, m_stats.m_fdir_prev_bytes, min, max) < 0) { return -1; } } else { g_trex.m_rx_sl.get_rx_stats(get_port_id(), rx_stats, min, max, reset, TrexPlatformApi::IF_STAT_IPV4_ID); } for (int i = min; i <= max; i++) { if ( reset ) { // return value so far, and reset if (hw_rx_stat_supported) { if (rx_stats != NULL) { rx_stats[i - min].set_pkts(m_stats.m_rx_per_flow_pkts[i] + diff_pkts[i]); rx_stats[i - min].set_bytes(m_stats.m_rx_per_flow_bytes[i] + diff_bytes[i]); } m_stats.m_rx_per_flow_pkts[i] = 0; m_stats.m_rx_per_flow_bytes[i] = 0; get_ex_drv()->reset_rx_stats(this, &m_stats.m_fdir_prev_pkts[i], i, 1); } if (tx_stats != NULL) { tx_stats[i - min] = g_trex.clear_flow_tx_stats(m_port_id, i, false); } } else { if (hw_rx_stat_supported) { m_stats.m_rx_per_flow_pkts[i] += diff_pkts[i]; m_stats.m_rx_per_flow_bytes[i] += diff_bytes[i]; if (rx_stats != NULL) { rx_stats[i - min].set_pkts(m_stats.m_rx_per_flow_pkts[i]); rx_stats[i - min].set_bytes(m_stats.m_rx_per_flow_bytes[i]); } } if (tx_stats != NULL) { tx_stats[i - min] = g_trex.get_flow_tx_stats(m_port_id, i); } } } return 0; } int CPhyEthIF::get_flow_stats_payload(rx_per_flow_t *rx_stats, tx_per_flow_t *tx_stats, int min, int max, bool reset) { g_trex.m_rx_sl.get_rx_stats(get_port_id(), rx_stats, min, max, reset, TrexPlatformApi::IF_STAT_PAYLOAD); for (int i = min; i <= max; i++) { if ( reset ) { if (tx_stats != NULL) { tx_stats[i - min] = g_trex.clear_flow_tx_stats(m_port_id, i + MAX_FLOW_STATS, true); } } else { if (tx_stats != NULL) { tx_stats[i - min] = g_trex.get_flow_tx_stats(m_port_id, i + MAX_FLOW_STATS); } } } return 0; } // If needed, send packets to rx core for processing. // This is relevant only in VM case, where we receive packets to the working DP core (only 1 DP core in this case) bool CCoreEthIF::process_rx_pkt(pkt_dir_t dir, rte_mbuf_t * m) { CFlowStatParser parser; uint32_t ip_id; if (parser.parse(rte_pktmbuf_mtod(m, uint8_t*), rte_pktmbuf_pkt_len(m)) != 0) { return false; } bool send=false; // e1000 on ESXI hands us the packet with the ethernet FCS if (parser.get_pkt_size() < rte_pktmbuf_pkt_len(m)) { rte_pktmbuf_trim(m, rte_pktmbuf_pkt_len(m) - parser.get_pkt_size()); } if ( get_is_stateless() ) { // In stateless RX, we only care about flow stat packets if ((parser.get_ip_id(ip_id) == 0) && ((ip_id & 0xff00) == IP_ID_RESERVE_BASE)) { send = true; } } else { CLatencyPktMode *c_l_pkt_mode = g_trex.m_mg.c_l_pkt_mode; bool is_lateancy_pkt = c_l_pkt_mode->IsLatencyPkt((IPHeader *)parser.get_l4()) & CCPortLatency::IsLatencyPkt(parser.get_l4() + c_l_pkt_mode->l4_header_len()); if (is_lateancy_pkt) { send = true; } else { if ( get_is_rx_filter_enable() ) { uint8_t max_ttl = 0xff - get_rx_check_hops(); uint8_t pkt_ttl = parser.get_ttl(); if ( (pkt_ttl==max_ttl) || (pkt_ttl==(max_ttl-1) ) ) { send=true; } } } } if (send) { CGenNodeLatencyPktInfo * node=(CGenNodeLatencyPktInfo * )CGlobalInfo::create_node(); if ( node ) { node->m_msg_type = CGenNodeMsgBase::LATENCY_PKT; node->m_dir = dir; node->m_latency_offset = 0xdead; node->m_pkt = m; if ( m_ring_to_rx->Enqueue((CGenNode*)node)==0 ){ }else{ CGlobalInfo::free_node((CGenNode *)node); send=false; } #ifdef LATENCY_QUEUE_TRACE_ printf("rx to cp --\n"); rte_pktmbuf_dump(stdout,m, rte_pktmbuf_pkt_len(m)); #endif }else{ send=false; } } return (send); } TrexStateless * get_stateless_obj() { return g_trex.m_trex_stateless; } CRxCoreStateless * get_rx_sl_core_obj() { return &g_trex.m_rx_sl; } static int latency_one_lcore(__attribute__((unused)) void *dummy) { CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; physical_thread_id_t phy_id =rte_lcore_id(); if ( lpsock->thread_phy_is_rx(phy_id) ) { g_trex.run_in_rx_core(); }else{ if ( lpsock->thread_phy_is_master( phy_id ) ) { g_trex.run_in_master(); delay(1); }else{ delay((uint32_t)(1000.0*CGlobalInfo::m_options.m_duration)); /* this core has stopped */ g_trex.m_signal[ lpsock->thread_phy_to_virt( phy_id ) ]=1; } } return 0; } static int slave_one_lcore(__attribute__((unused)) void *dummy) { CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; physical_thread_id_t phy_id =rte_lcore_id(); if ( lpsock->thread_phy_is_rx(phy_id) ) { g_trex.run_in_rx_core(); }else{ if ( lpsock->thread_phy_is_master( phy_id ) ) { g_trex.run_in_master(); delay(1); }else{ g_trex.run_in_core( lpsock->thread_phy_to_virt( phy_id ) ); } } return 0; } uint32_t get_cores_mask(uint32_t cores,int offset){ int i; uint32_t res=1; uint32_t mask=(1<<(offset+1)); for (i=0; i<(cores-1); i++) { res |= mask ; mask = mask <<1; } return (res); } static char *g_exe_name; const char *get_exe_name() { return g_exe_name; } int main(int argc , char * argv[]){ g_exe_name = argv[0]; return ( main_test(argc , argv)); } int update_global_info_from_platform_file(){ CPlatformYamlInfo *cg=&global_platform_cfg_info; CGlobalInfo::m_socket.Create(&cg->m_platform); if (!cg->m_info_exist) { /* nothing to do ! */ return 0; } CGlobalInfo::m_options.prefix =cg->m_prefix; CGlobalInfo::m_options.preview.setCores(cg->m_thread_per_dual_if); if ( cg->m_port_limit_exist ){ CGlobalInfo::m_options.m_expected_portd =cg->m_port_limit; } if ( cg->m_enable_zmq_pub_exist ){ CGlobalInfo::m_options.preview.set_zmq_publish_enable(cg->m_enable_zmq_pub); CGlobalInfo::m_options.m_zmq_port = cg->m_zmq_pub_port; } if ( cg->m_telnet_exist ){ CGlobalInfo::m_options.m_telnet_port = cg->m_telnet_port; } if ( cg->m_mac_info_exist ){ int i; /* cop the file info */ int port_size=cg->m_mac_info.size(); if ( port_size > TREX_MAX_PORTS ){ port_size = TREX_MAX_PORTS; } for (i=0; im_mac_info[i].copy_src(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.src) ; cg->m_mac_info[i].copy_dest(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.dest) ; CGlobalInfo::m_options.m_ip_cfg[i].set_def_gw(cg->m_mac_info[i].get_def_gw()); CGlobalInfo::m_options.m_ip_cfg[i].set_ip(cg->m_mac_info[i].get_ip()); CGlobalInfo::m_options.m_ip_cfg[i].set_mask(cg->m_mac_info[i].get_mask()); CGlobalInfo::m_options.m_ip_cfg[i].set_vlan(cg->m_mac_info[i].get_vlan()); } } /* mul by interface type */ float mul=1.0; if (cg->m_port_bandwidth_gb<10) { cg->m_port_bandwidth_gb=10.0; } mul = mul*(float)cg->m_port_bandwidth_gb/10.0; mul= mul * (float)cg->m_port_limit/2.0; mul= mul * CGlobalInfo::m_options.m_mbuf_factor; CGlobalInfo::m_memory_cfg.set_pool_cache_size(RTE_MEMPOOL_CACHE_MAX_SIZE); CGlobalInfo::m_memory_cfg.set_number_of_dp_cors( CGlobalInfo::m_options.get_number_of_dp_cores_needed() ); CGlobalInfo::m_memory_cfg.set(cg->m_memory,mul); return (0); } extern "C" int eal_cpu_detected(unsigned lcore_id); // return mask representing available cores int core_mask_calc() { uint32_t mask = 0; int lcore_id; for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { if (eal_cpu_detected(lcore_id)) { mask |= (1 << lcore_id); } } return mask; } // Return number of set bits in i uint32_t num_set_bits(uint32_t i) { i = i - ((i >> 1) & 0x55555555); i = (i & 0x33333333) + ((i >> 2) & 0x33333333); return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; } // sanity check if the cores we want to use really exist int core_mask_sanity(uint32_t wanted_core_mask) { uint32_t calc_core_mask = core_mask_calc(); uint32_t wanted_core_num, calc_core_num; wanted_core_num = num_set_bits(wanted_core_mask); calc_core_num = num_set_bits(calc_core_mask); if (calc_core_num == 1) { printf ("Error: You have only 1 core available. Minimum configuration requires 2 cores\n"); printf(" If you are running on VM, consider adding more cores if possible\n"); return -1; } if (wanted_core_num > calc_core_num) { printf("Error: You have %d threads available, but you asked for %d threads.\n", calc_core_num, wanted_core_num); printf(" Calculation is: -c (%d) * dual ports (%d) + 1 master thread %s" , CGlobalInfo::m_options.preview.getCores(), CGlobalInfo::m_options.get_expected_dual_ports() , get_is_rx_thread_enabled() ? "+1 latency thread (because of -l flag)\n" : "\n"); if (CGlobalInfo::m_options.preview.getCores() > 1) printf(" Maybe try smaller -c .\n"); printf(" If you are running on VM, consider adding more cores if possible\n"); return -1; } if (wanted_core_mask != (wanted_core_mask & calc_core_mask)) { printf ("Serious error: Something is wrong with the hardware. Wanted core mask is %x. Existing core mask is %x\n", wanted_core_mask, calc_core_mask); return -1; } return 0; } int update_dpdk_args(void){ CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; CParserOption * lpop= &CGlobalInfo::m_options; lpsock->set_rx_thread_is_enabled(get_is_rx_thread_enabled()); lpsock->set_number_of_threads_per_ports(lpop->preview.getCores() ); lpsock->set_number_of_dual_ports(lpop->get_expected_dual_ports()); if ( !lpsock->sanity_check() ){ printf(" ERROR in configuration file \n"); return (-1); } if ( CGlobalInfo::m_options.preview.getVMode() > 0 ) { lpsock->dump(stdout); } snprintf(global_cores_str, sizeof(global_cores_str), "0x%llx" ,(unsigned long long)lpsock->get_cores_mask()); if (core_mask_sanity(strtol(global_cores_str, NULL, 16)) < 0) { return -1; } /* set the DPDK options */ global_dpdk_args_num = 0; global_dpdk_args[global_dpdk_args_num++]=(char *)"xx"; global_dpdk_args[global_dpdk_args_num++]=(char *)"-c"; global_dpdk_args[global_dpdk_args_num++]=(char *)global_cores_str; global_dpdk_args[global_dpdk_args_num++]=(char *)"-n"; global_dpdk_args[global_dpdk_args_num++]=(char *)"4"; if ( CGlobalInfo::m_options.preview.getVMode() == 0 ) { global_dpdk_args[global_dpdk_args_num++]=(char *)"--log-level"; snprintf(global_loglevel_str, sizeof(global_loglevel_str), "%d", 4); global_dpdk_args[global_dpdk_args_num++]=(char *)global_loglevel_str; }else{ global_dpdk_args[global_dpdk_args_num++]=(char *)"--log-level"; snprintf(global_loglevel_str, sizeof(global_loglevel_str), "%d", CGlobalInfo::m_options.preview.getVMode()+1); global_dpdk_args[global_dpdk_args_num++]=(char *)global_loglevel_str; } global_dpdk_args[global_dpdk_args_num++] = (char *)"--master-lcore"; snprintf(global_master_id_str, sizeof(global_master_id_str), "%u", lpsock->get_master_phy_id()); global_dpdk_args[global_dpdk_args_num++] = global_master_id_str; /* add white list */ if (lpop->m_run_mode == CParserOption::RUN_MODE_DUMP_INFO and lpop->dump_interfaces.size()) { for (int i=0; i<(int)lpop->dump_interfaces.size(); i++) { global_dpdk_args[global_dpdk_args_num++]=(char *)"-w"; global_dpdk_args[global_dpdk_args_num++]=(char *)lpop->dump_interfaces[i].c_str(); } } else { for (int i=0; i<(int)global_platform_cfg_info.m_if_list.size(); i++) { global_dpdk_args[global_dpdk_args_num++]=(char *)"-w"; global_dpdk_args[global_dpdk_args_num++]=(char *)global_platform_cfg_info.m_if_list[i].c_str(); } } if ( lpop->prefix.length() ){ global_dpdk_args[global_dpdk_args_num++]=(char *)"--file-prefix"; snprintf(global_prefix_str, sizeof(global_prefix_str), "%s", lpop->prefix.c_str()); global_dpdk_args[global_dpdk_args_num++]=(char *)global_prefix_str; global_dpdk_args[global_dpdk_args_num++]=(char *)"-m"; if (global_platform_cfg_info.m_limit_memory.length()) { global_dpdk_args[global_dpdk_args_num++]=(char *)global_platform_cfg_info.m_limit_memory.c_str(); }else{ global_dpdk_args[global_dpdk_args_num++]=(char *)"1024"; } } if ( CGlobalInfo::m_options.preview.getVMode() > 0 ) { printf("args \n"); int i; for (i=0; icfg_file,1); if ( op->preview.getVMode() >0 ) { fl.DumpCsv(stdout); } uint32_t start= os_get_time_msec(); CErfIF erf_vif; fl.generate_p_thread_info(1); CFlowGenListPerThread * lpt; lpt=fl.m_threads_info[0]; lpt->set_vif(&erf_vif); if ( (op->preview.getVMode() >1) || op->preview.getFileWrite() ) { lpt->start_generate_stateful(op->out_file,op->preview); } lpt->m_node_gen.DumpHist(stdout); uint32_t stop= os_get_time_msec(); printf(" d time = %ul %ul \n",stop-start,os_get_time_freq()); fl.Delete(); return (0); } void dump_interfaces_info() { printf("Showing interfaces info.\n"); uint8_t m_max_ports = rte_eth_dev_count(); struct ether_addr mac_addr; char mac_str[ETHER_ADDR_FMT_SIZE]; struct rte_pci_addr pci_addr; for (uint8_t port_id=0; port_idaddr; rte_eth_macaddr_get(port_id, &mac_addr); ether_format_addr(mac_str, sizeof mac_str, &mac_addr); printf("PCI: %04x:%02x:%02x.%d - MAC: %s - Driver: %s\n", pci_addr.domain, pci_addr.bus, pci_addr.devid, pci_addr.function, mac_str, rte_eth_devices[port_id].pci_dev->driver->name); } } int main_test(int argc , char * argv[]){ utl_termio_init(); int ret; unsigned lcore_id; printf("Starting TRex %s please wait ... \n",VERSION_BUILD_NUM); CGlobalInfo::m_options.preview.clean(); if ( parse_options_wrapper(argc, argv, &CGlobalInfo::m_options,true ) != 0){ exit(-1); } /* enable core dump if requested */ if (CGlobalInfo::m_options.preview.getCoreDumpEnable()) { utl_set_coredump_size(-1); } else { utl_set_coredump_size(0); } update_global_info_from_platform_file(); /* It is not a mistake. Give the user higher priorty over the configuration file */ if (parse_options_wrapper(argc, argv, &CGlobalInfo::m_options ,false) != 0) { exit(-1); } if ( CGlobalInfo::m_options.preview.getVMode() > 0){ CGlobalInfo::m_options.dump(stdout); CGlobalInfo::m_memory_cfg.Dump(stdout); } if (update_dpdk_args() < 0) { return -1; } CParserOption * po=&CGlobalInfo::m_options; if ( CGlobalInfo::m_options.preview.getVMode() == 0 ) { rte_set_log_level(1); } uid_t uid; uid = geteuid (); if ( uid != 0 ) { printf("ERROR you must run with superuser priviliges \n"); printf("User id : %d \n",uid); printf("try 'sudo' %s \n",argv[0]); return (-1); } /* set affinity to the master core as default */ cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(CGlobalInfo::m_socket.get_master_phy_id(), &mask); pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask); ret = rte_eal_init(global_dpdk_args_num, (char **)global_dpdk_args); if (ret < 0){ printf(" You might need to run ./trex-cfg once \n"); rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); } if (CGlobalInfo::m_options.m_run_mode == CParserOption::RUN_MODE_DUMP_INFO) { dump_interfaces_info(); exit(0); } reorder_dpdk_ports(); time_init(); /* check if we are in simulation mode */ if ( CGlobalInfo::m_options.out_file != "" ){ printf(" t-rex simulation mode into %s \n",CGlobalInfo::m_options.out_file.c_str()); return ( sim_load_list_of_cap_files(&CGlobalInfo::m_options) ); } if ( !g_trex.Create() ){ exit(1); } if (po->preview.get_is_rx_check_enable() && (po->m_rx_check_sample< get_min_sample_rate()) ) { po->m_rx_check_sample = get_min_sample_rate(); printf("Warning:rx check sample rate should not be lower than %d. Setting it to %d\n",get_min_sample_rate(),get_min_sample_rate()); } /* set dump mode */ g_trex.m_io_modes.set_mode((CTrexGlobalIoMode::CliDumpMode)CGlobalInfo::m_options.m_io_mode); /* disable WD if needed */ bool wd_enable = (CGlobalInfo::m_options.preview.getWDDisable() ? false : true); TrexWatchDog::getInstance().init(wd_enable); g_trex.m_sl_rx_running = false; if ( get_is_stateless() ) { g_trex.start_master_stateless(); }else{ g_trex.start_master_statefull(); } // For unit testing of HW rules and queues configuration. Just send some packets and exit. if (CGlobalInfo::m_options.m_debug_pkt_proto != 0) { CTrexDebug debug = CTrexDebug(g_trex.m_ports, g_trex.m_max_ports); int ret; if (CGlobalInfo::m_options.m_debug_pkt_proto == D_PKT_TYPE_HW_TOGGLE_TEST) { // Unit test: toggle many times between receive all and stateless/stateful modes, // to test resiliency of add/delete fdir filters printf("Starting receive all/normal mode toggle unit test\n"); for (int i = 0; i < 100; i++) { for (int port_id = 0; port_id < g_trex.m_max_ports; port_id++) { CPhyEthIF *pif = &g_trex.m_ports[port_id]; CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true); } ret = debug.test_send(D_PKT_TYPE_HW_VERIFY_RCV_ALL); if (ret != 0) { printf("Iteration %d: Receive all mode failed\n", i); exit(ret); } for (int port_id = 0; port_id < g_trex.m_max_ports; port_id++) { CPhyEthIF *pif = &g_trex.m_ports[port_id]; CTRexExtendedDriverDb::Ins()->get_drv()->configure_rx_filter_rules(pif); } ret = debug.test_send(D_PKT_TYPE_HW_VERIFY); if (ret != 0) { printf("Iteration %d: Normal mode failed\n", i); exit(ret); } printf("Iteration %d OK\n", i); } exit(0); } else { if (CGlobalInfo::m_options.m_debug_pkt_proto == D_PKT_TYPE_HW_VERIFY_RCV_ALL) { for (int port_id = 0; port_id < g_trex.m_max_ports; port_id++) { CPhyEthIF *pif = &g_trex.m_ports[port_id]; CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true); } } ret = debug.test_send(CGlobalInfo::m_options.m_debug_pkt_proto); exit(ret); } } g_trex.pre_test(); // after doing all needed ARP resolution, we need to flush queues, and stop our drop queue g_trex.ixgbe_rx_queue_flush(); for (int i = 0; i < g_trex.m_max_ports; i++) { CPhyEthIF *_if = &g_trex.m_ports[i]; _if->stop_rx_drop_queue(); } if ( CGlobalInfo::m_options.is_latency_enabled() && (CGlobalInfo::m_options.m_latency_prev > 0)) { uint32_t pkts = CGlobalInfo::m_options.m_latency_prev * CGlobalInfo::m_options.m_latency_rate; printf("Starting warm up phase for %d sec\n",CGlobalInfo::m_options.m_latency_prev); g_trex.m_mg.start(pkts, NULL); delay(CGlobalInfo::m_options.m_latency_prev* 1000); printf("Finished \n"); g_trex.m_mg.reset(); } if ( CGlobalInfo::m_options.preview.getOnlyLatency() ){ rte_eal_mp_remote_launch(latency_one_lcore, NULL, CALL_MASTER); RTE_LCORE_FOREACH_SLAVE(lcore_id) { if (rte_eal_wait_lcore(lcore_id) < 0) return -1; } g_trex.stop_master(); return (0); } if ( CGlobalInfo::m_options.preview.getSingleCore() ) { g_trex.run_in_core(1); g_trex.stop_master(); return (0); } rte_eal_mp_remote_launch(slave_one_lcore, NULL, CALL_MASTER); RTE_LCORE_FOREACH_SLAVE(lcore_id) { if (rte_eal_wait_lcore(lcore_id) < 0) return -1; } g_trex.stop_master(); g_trex.Delete(); utl_termio_reset(); return (0); } void wait_x_sec(int sec) { int i; printf(" wait %d sec ", sec); fflush(stdout); for (i=0; i 0){ printf("TRex cfg port id: %d <-> DPDK port id: %d\n", i, port_id); } } // actual reorder for (int i=0; i<(int)global_platform_cfg_info.m_if_list.size(); i++) { memcpy(&rte_eth_devices[m_port_map[i]], &rte_eth_devices_temp[i], sizeof rte_eth_devices_temp[i]); } } ////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// // driver section ////////////////////////////////////////////////////////////////////////////////////////////// int CTRexExtendedDriverBase::stop_queue(CPhyEthIF * _if, uint16_t q_num) { uint8_t port_id=_if->get_rte_port_id(); return (rte_eth_dev_rx_queue_stop(port_id, q_num)); } int CTRexExtendedDriverBase::wait_for_stable_link() { wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); return 0; } void CTRexExtendedDriverBase::wait_after_link_up() { wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); } CFlowStatParser *CTRexExtendedDriverBase::get_flow_stat_parser() { CFlowStatParser *parser = new CFlowStatParser(); assert (parser); return parser; } // in 1G we need to wait if links became ready to soon void CTRexExtendedDriverBase1G::wait_after_link_up(){ //wait_x_sec(6 + CGlobalInfo::m_options.m_wait_before_traffic); } int CTRexExtendedDriverBase1G::wait_for_stable_link(){ wait_x_sec(9 + CGlobalInfo::m_options.m_wait_before_traffic); return(0); } void CTRexExtendedDriverBase1G::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH_1G; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = 0; } void CTRexExtendedDriverBase1G::update_global_config_fdir(port_cfg_t * cfg){ // Configuration is done in configure_rx_filter_rules by writing to registers } #define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 // e1000 driver does not support the generic stop/start queue API, so we need to implement ourselves int CTRexExtendedDriverBase1G::stop_queue(CPhyEthIF * _if, uint16_t q_num) { uint32_t reg_val = _if->pci_reg_read( E1000_RXDCTL(q_num)); reg_val &= ~E1000_RXDCTL_QUEUE_ENABLE; _if->pci_reg_write( E1000_RXDCTL(q_num), reg_val); return 0; } int CTRexExtendedDriverBase1G::configure_rx_filter_rules(CPhyEthIF * _if){ if ( get_is_stateless() ) { return configure_rx_filter_rules_stateless(_if); } else { return configure_rx_filter_rules_statefull(_if); } return 0; } int CTRexExtendedDriverBase1G::configure_rx_filter_rules_statefull(CPhyEthIF * _if) { uint16_t hops = get_rx_check_hops(); uint16_t v4_hops = (hops << 8)&0xff00; uint8_t protocol; if (CGlobalInfo::m_options.m_l_pkt_mode == 0) { protocol = IPPROTO_SCTP; } else { protocol = IPPROTO_ICMP; } /* enable filter to pass packet to rx queue 1 */ _if->pci_reg_write( E1000_IMIR(0), 0x00020000); _if->pci_reg_write( E1000_IMIREXT(0), 0x00081000); _if->pci_reg_write( E1000_TTQF(0), protocol | 0x00008100 /* enable */ | 0xE0010000 /* RX queue is 1 */ ); /* 16 : 12 MAC , (2)0x0800,2 | DW0 , DW1 6 bytes , TTL , PROTO | DW2=0 , DW3=0x0000FF06 */ int i; // IPv4: bytes being compared are {TTL, Protocol} uint16_t ff_rules_v4[6]={ (uint16_t)(0xFF06 - v4_hops), (uint16_t)(0xFE11 - v4_hops), (uint16_t)(0xFF11 - v4_hops), (uint16_t)(0xFE06 - v4_hops), (uint16_t)(0xFF01 - v4_hops), (uint16_t)(0xFE01 - v4_hops), } ; // IPv6: bytes being compared are {NextHdr, HopLimit} uint16_t ff_rules_v6[2]={ (uint16_t)(0x3CFF - hops), (uint16_t)(0x3CFE - hops), } ; uint16_t *ff_rules; uint16_t num_rules; uint32_t mask=0; int rule_id; if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ ff_rules = &ff_rules_v6[0]; num_rules = sizeof(ff_rules_v6)/sizeof(ff_rules_v6[0]); }else{ ff_rules = &ff_rules_v4[0]; num_rules = sizeof(ff_rules_v4)/sizeof(ff_rules_v4[0]); } clear_rx_filter_rules(_if); uint8_t len = 24; for (rule_id=0; rule_idpci_reg_write( (E1000_FHFT(rule_id)+i) , 0); } if ( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ){ len += 8; if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ // IPv6 VLAN: NextHdr/HopLimit offset = 0x18 _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+0) , PKT_NTOHS(ff_rules[rule_id]) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+8) , 0x03); /* MASK */ }else{ // IPv4 VLAN: TTL/Protocol offset = 0x1A _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+0) , (PKT_NTOHS(ff_rules[rule_id])<<16) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+8) , 0x0C); /* MASK */ } }else{ if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ // IPv6: NextHdr/HopLimit offset = 0x14 _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+4) , PKT_NTOHS(ff_rules[rule_id]) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+8) , 0x30); /* MASK */ }else{ // IPv4: TTL/Protocol offset = 0x16 _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+4) , (PKT_NTOHS(ff_rules[rule_id])<<16) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+8) , 0xC0); /* MASK */ } } // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id)+0xFC) , (1<<16) | (1<<8) | len); mask |=(1<pci_reg_write(E1000_WUFC, (mask<<16) | (1<<14) ); return (0); } // Sadly, DPDK has no support for i350 filters, so we need to implement by writing to registers. int CTRexExtendedDriverBase1G::configure_rx_filter_rules_stateless(CPhyEthIF * _if) { /* enable filter to pass packet to rx queue 1 */ _if->pci_reg_write( E1000_IMIR(0), 0x00020000); _if->pci_reg_write( E1000_IMIREXT(0), 0x00081000); uint8_t len = 24; uint32_t mask = 0; int rule_id; clear_rx_filter_rules(_if); rule_id = 0; mask |= 0x1 << rule_id; // filter for byte 18 of packet (msb of IP ID) should equal ff _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)) , 0x00ff0000); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x04); /* MASK */ // + bytes 12 + 13 (ether type) should indicate IP. _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x00000008); _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); // same as 0, but with vlan. type should be vlan. Inside vlan, should be IP with lsb of IP ID equals 0xff rule_id = 1; mask |= 0x1 << rule_id; // filter for byte 22 of packet (msb of IP ID) should equal ff _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 4) , 0x00ff0000); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x40 | 0x03); /* MASK */ // + bytes 12 + 13 (ether type) should indicate VLAN. _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x00000081); _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ // + bytes 16 + 17 (vlan type) should indicate IP. _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) ) , 0x00000008); // Was written together with IP ID filter // _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x03); /* MASK */ // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); rule_id = 2; mask |= 0x1 << rule_id; // ipv6 flow stat // filter for byte 16 of packet (part of flow label) should equal 0xff _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)) , 0x000000ff); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x01); /* MASK */ // + bytes 12 + 13 (ether type) should indicate IPv6. _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x0000dd86); _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); rule_id = 3; mask |= 0x1 << rule_id; // same as 2, with vlan. Type is vlan. Inside vlan, IPv6 with flow label second bits 4-11 equals 0xff // filter for byte 20 of packet (part of flow label) should equal 0xff _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 4) , 0x000000ff); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x10 | 0x03); /* MASK */ // + bytes 12 + 13 (ether type) should indicate VLAN. _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x00000081); _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x30); /* MASK */ // + bytes 16 + 17 (vlan type) should indicate IP. _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) ) , 0x0000dd86); // Was written together with flow label filter // _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16) + 8) , 0x03); /* MASK */ // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | len); /* enable rules */ _if->pci_reg_write(E1000_WUFC, (mask << 16) | (1 << 14) ); return (0); } // clear registers of rules void CTRexExtendedDriverBase1G::clear_rx_filter_rules(CPhyEthIF * _if) { for (int rule_id = 0 ; rule_id < 8; rule_id++) { for (int i = 0; i < 0xff; i += 4) { _if->pci_reg_write( (E1000_FHFT(rule_id) + i) , 0); } } } int CTRexExtendedDriverBase1G::set_rcv_all(CPhyEthIF * _if, bool set_on) { // byte 12 equals 08 - for IPv4 and ARP // 86 - For IPv6 // 81 - For VLAN // 88 - For MPLS uint8_t eth_types[] = {0x08, 0x86, 0x81, 0x88}; uint32_t mask = 0; clear_rx_filter_rules(_if); if (set_on) { for (int rule_id = 0; rule_id < sizeof(eth_types); rule_id++) { mask |= 0x1 << rule_id; // Filter for byte 12 of packet _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 4) , 0x000000 | eth_types[rule_id]); _if->pci_reg_write( (E1000_FHFT(rule_id)+(1*16) + 8) , 0x10); /* MASK */ // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1, len = 24 _if->pci_reg_write( (E1000_FHFT(rule_id) + 0xFC) , (1 << 16) | (1 << 8) | 24); } } else { configure_rx_filter_rules(_if); } return 0; } void CTRexExtendedDriverBase1G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ stats->ipackets += _if->pci_reg_read(E1000_GPRC) ; stats->ibytes += (_if->pci_reg_read(E1000_GORCL) ); stats->ibytes += (((uint64_t)_if->pci_reg_read(E1000_GORCH))<<32); stats->opackets += _if->pci_reg_read(E1000_GPTC); stats->obytes += _if->pci_reg_read(E1000_GOTCL) ; stats->obytes += ( (((uint64_t)_if->pci_reg_read(IXGBE_GOTCH))<<32) ); stats->f_ipackets += 0; stats->f_ibytes += 0; stats->ierrors += ( _if->pci_reg_read(E1000_RNBC) + _if->pci_reg_read(E1000_CRCERRS) + _if->pci_reg_read(E1000_ALGNERRC ) + _if->pci_reg_read(E1000_SYMERRS ) + _if->pci_reg_read(E1000_RXERRC ) + _if->pci_reg_read(E1000_ROC)+ _if->pci_reg_read(E1000_RUC)+ _if->pci_reg_read(E1000_RJC) + _if->pci_reg_read(E1000_XONRXC)+ _if->pci_reg_read(E1000_XONTXC)+ _if->pci_reg_read(E1000_XOFFRXC)+ _if->pci_reg_read(E1000_XOFFTXC)+ _if->pci_reg_read(E1000_FCRUC) ); stats->oerrors += 0; stats->imcasts = 0; stats->rx_nombuf = 0; } void CTRexExtendedDriverBase1G::clear_extended_stats(CPhyEthIF * _if){ } #if 0 int CTRexExtendedDriverBase1G::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts ,uint32_t *bytes, uint32_t *prev_bytes, int min, int max) { uint32_t port_id = _if->get_port_id(); return g_trex.m_rx_sl.get_rx_stats(port_id, pkts, prev_pkts, bytes, prev_bytes, min, max); } #endif void CTRexExtendedDriverBase10G::clear_extended_stats(CPhyEthIF * _if){ _if->pci_reg_read(IXGBE_RXNFGPC); } void CTRexExtendedDriverBase10G::update_global_config_fdir(port_cfg_t * cfg){ cfg->update_global_config_fdir_10g(); } void CTRexExtendedDriverBase10G::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH; } int CTRexExtendedDriverBase10G::configure_rx_filter_rules(CPhyEthIF * _if) { if ( get_is_stateless() ) { return configure_rx_filter_rules_stateless(_if); } else { return configure_rx_filter_rules_statefull(_if); } return 0; } int CTRexExtendedDriverBase10G::configure_rx_filter_rules_stateless(CPhyEthIF * _if) { uint8_t port_id = _if->get_rte_port_id(); int ip_id_lsb; // 0..MAX_FLOW_STATS-1 is for rules using ip_id. // MAX_FLOW_STATS rule is for the payload rules. Meaning counter value is in the payload for (ip_id_lsb = 0; ip_id_lsb <= MAX_FLOW_STATS; ip_id_lsb++ ) { struct rte_eth_fdir_filter fdir_filter; int res = 0; memset(&fdir_filter,0,sizeof(fdir_filter)); fdir_filter.input.flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; fdir_filter.soft_id = ip_id_lsb; // We can use the ip_id_lsb also as filter soft_id fdir_filter.input.flow_ext.flexbytes[0] = 0xff; fdir_filter.input.flow_ext.flexbytes[1] = ip_id_lsb; fdir_filter.action.rx_queue = 1; fdir_filter.action.behavior = RTE_ETH_FDIR_ACCEPT; fdir_filter.action.report_status = RTE_ETH_FDIR_NO_REPORT_STATUS; res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, &fdir_filter); if (res != 0) { rte_exit(EXIT_FAILURE, " ERROR rte_eth_dev_filter_ctrl : %d\n",res); } } return 0; } int CTRexExtendedDriverBase10G::configure_rx_filter_rules_statefull(CPhyEthIF * _if) { uint8_t port_id=_if->get_rte_port_id(); uint16_t hops = get_rx_check_hops(); uint16_t v4_hops = (hops << 8)&0xff00; /* enable rule 0 SCTP -> queue 1 for latency */ /* 1<<21 means that queue 1 is for SCTP */ _if->pci_reg_write(IXGBE_L34T_IMIR(0),(1<<21)); _if->pci_reg_write(IXGBE_FTQF(0), IXGBE_FTQF_PROTOCOL_SCTP| (IXGBE_FTQF_PRIORITY_MASK<> 8) & 0xff; fdir_filter.input.flow_ext.flexbytes[1] = ff_rule & 0xff; fdir_filter.action.rx_queue = 1; fdir_filter.action.behavior = RTE_ETH_FDIR_ACCEPT; fdir_filter.action.report_status = RTE_ETH_FDIR_NO_REPORT_STATUS; res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, &fdir_filter); if (res != 0) { rte_exit(EXIT_FAILURE, " ERROR rte_eth_dev_filter_ctrl : %d\n",res); } } return (0); } void CTRexExtendedDriverBase10G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ int i; uint64_t t=0; if ( !get_is_stateless() ) { for (i=0; i<8;i++) { t+=_if->pci_reg_read(IXGBE_MPC(i)); } } stats->ipackets += _if->pci_reg_read(IXGBE_GPRC) ; stats->ibytes += (_if->pci_reg_read(IXGBE_GORCL) +(((uint64_t)_if->pci_reg_read(IXGBE_GORCH))<<32)); stats->opackets += _if->pci_reg_read(IXGBE_GPTC); stats->obytes += (_if->pci_reg_read(IXGBE_GOTCL) +(((uint64_t)_if->pci_reg_read(IXGBE_GOTCH))<<32)); stats->f_ipackets += _if->pci_reg_read(IXGBE_RXDGPC); stats->f_ibytes += (_if->pci_reg_read(IXGBE_RXDGBCL) +(((uint64_t)_if->pci_reg_read(IXGBE_RXDGBCH))<<32)); stats->ierrors += ( _if->pci_reg_read(IXGBE_RLEC) + _if->pci_reg_read(IXGBE_ERRBC) + _if->pci_reg_read(IXGBE_CRCERRS) + _if->pci_reg_read(IXGBE_ILLERRC ) + _if->pci_reg_read(IXGBE_ROC)+ _if->pci_reg_read(IXGBE_RUC)+t); stats->oerrors += 0; stats->imcasts = 0; stats->rx_nombuf = 0; } int CTRexExtendedDriverBase10G::wait_for_stable_link(){ wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic); return (0); } CFlowStatParser *CTRexExtendedDriverBase10G::get_flow_stat_parser() { CFlowStatParser *parser = new C82599Parser(CGlobalInfo::m_options.preview.get_vlan_mode_enable() ? true:false); assert (parser); return parser; } void CTRexExtendedDriverBase40G::clear_extended_stats(CPhyEthIF * _if){ rte_eth_stats_reset(_if->get_port_id()); } void CTRexExtendedDriverBaseVIC::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH; cfg->m_port_conf.rxmode.max_rx_pkt_len =9*1000-10; } void CTRexExtendedDriverBase40G::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH; cfg->update_global_config_fdir_40g(); } // What is the type of the rule the respective hw_id counter counts. struct fdir_hw_id_params_t { uint16_t rule_type; uint16_t l4_proto; }; static struct fdir_hw_id_params_t fdir_hw_id_rule_params[512]; /* Add rule to send packets with protocol 'type', and ttl 'ttl' to rx queue 1 */ // ttl is used in statefull mode, and ip_id in stateless. We configure the driver registers so that only one of them applies. // So, the rule will apply if packet has either the correct ttl or IP ID, depending if we are in statfull or stateless. void CTRexExtendedDriverBase40G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl , uint16_t ip_id, uint16_t l4_proto, int queue, uint16_t stat_idx) { int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR); static int filter_soft_id = 0; if ( ret != 0 ){ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_supported " "err=%d, port=%u \n", ret, port_id); } struct rte_eth_fdir_filter filter; memset(&filter,0,sizeof(struct rte_eth_fdir_filter)); #if 0 printf("40g::%s rules: port:%d type:%d ttl:%d ip_id:%x l4:%d q:%d hw index:%d\n" , (op == RTE_ETH_FILTER_ADD) ? "add" : "del" , port_id, type, ttl, ip_id, l4_proto, queue, stat_idx); #endif filter.action.rx_queue = queue; filter.action.behavior =RTE_ETH_FDIR_ACCEPT; filter.action.report_status =RTE_ETH_FDIR_NO_REPORT_STATUS; filter.action.stat_count_index = stat_idx; filter.soft_id = filter_soft_id++; filter.input.flow_type = type; if (op == RTE_ETH_FILTER_ADD) { fdir_hw_id_rule_params[stat_idx].rule_type = type; fdir_hw_id_rule_params[stat_idx].l4_proto = l4_proto; } switch (type) { case RTE_ETH_FLOW_NONFRAG_IPV4_UDP: case RTE_ETH_FLOW_NONFRAG_IPV4_TCP: case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP: case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER: filter.input.flow.ip4_flow.ttl=ttl; filter.input.flow.ip4_flow.ip_id = ip_id; if (l4_proto != 0) filter.input.flow.ip4_flow.proto = l4_proto; break; case RTE_ETH_FLOW_NONFRAG_IPV6_UDP: case RTE_ETH_FLOW_NONFRAG_IPV6_TCP: case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER: filter.input.flow.ipv6_flow.hop_limits=ttl; filter.input.flow.ipv6_flow.flow_label = ip_id; filter.input.flow.ipv6_flow.proto = l4_proto; break; } ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, op, (void*)&filter); if ( ret != 0 ) { rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_ctrl: err=%d, port=%u\n", ret, port_id); } } int CTRexExtendedDriverBase40G::add_del_eth_type_rule(uint8_t port_id, enum rte_filter_op op, uint16_t eth_type) { int ret; struct rte_eth_ethertype_filter filter; memset(&filter, 0, sizeof(filter)); filter.ether_type = eth_type; filter.flags = 0; filter.queue = MAIN_DPDK_RX_Q; ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_ETHERTYPE, op, (void *) &filter); return ret; } extern "C" int rte_eth_fdir_stats_reset(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len); // type - rule type. Currently we only support rules in IP ID. // proto - Packet protocol: UDP or TCP // id - Counter id in HW. We assume it is in the range 0..MAX_FLOW_STATS int CTRexExtendedDriverBase40G::add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3_proto , uint8_t l4_proto, uint8_t ipv6_next_h, uint16_t id) { uint32_t rule_id = (port_id % m_if_per_card) * MAX_FLOW_STATS + id; uint16_t rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; uint8_t next_proto; if (l3_proto == EthernetHeader::Protocol::IP) { next_proto = l4_proto; switch(l4_proto) { case IPPROTO_TCP: rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_TCP; break; case IPPROTO_UDP: rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_UDP; break; default: rte_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; break; } } else { // IPv6 next_proto = ipv6_next_h; switch(l4_proto) { case IPPROTO_TCP: rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_TCP; break; case IPPROTO_UDP: rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_UDP; break; default: rte_type = RTE_ETH_FLOW_NONFRAG_IPV6_OTHER; break; } } add_del_rules(op, port_id, rte_type, 0, IP_ID_RESERVE_BASE + id, next_proto, MAIN_DPDK_DATA_Q, rule_id); return 0; } int CTRexExtendedDriverBase40G::configure_rx_filter_rules_statefull(CPhyEthIF * _if) { uint32_t port_id = _if->get_port_id(); uint16_t hops = get_rx_check_hops(); int i; rte_eth_fdir_stats_reset(port_id, NULL, 0, 1); for (i = 0; i < 2; i++) { uint8_t ttl = TTL_RESERVE_DUPLICATE - i - hops; add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, ttl, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, ttl, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, ttl, 0, RX_CHECK_V6_OPT_TYPE, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, ttl, 0, RX_CHECK_V6_OPT_TYPE, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, ttl, 0, RX_CHECK_V6_OPT_TYPE, MAIN_DPDK_RX_Q, 0); /* Rules for latency measurement packets */ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, ttl, 0, IPPROTO_ICMP, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, ttl, 0, 0, MAIN_DPDK_RX_Q, 0); } return 0; } const uint32_t FDIR_TEMP_HW_ID = 511; const uint32_t FDIR_PAYLOAD_RULES_HW_ID = 510; extern const uint32_t FLOW_STAT_PAYLOAD_IP_ID; int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) { uint32_t port_id = _if->get_port_id(); if (get_is_stateless()) { i40e_trex_fdir_reg_init(port_id, I40E_TREX_INIT_STL); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 0 , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, 0 , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 0 , FLOW_STAT_PAYLOAD_IP_ID, IPPROTO_ICMP, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 0 , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, 0 , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 0 , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0); rte_eth_fdir_stats_reset(_if->get_port_id(), NULL, FDIR_TEMP_HW_ID, 1); return 0; // Other rules are configured dynamically in stateless } else { i40e_trex_fdir_reg_init(port_id, I40E_TREX_INIT_STF); return configure_rx_filter_rules_statefull(_if); } } void CTRexExtendedDriverBase40G::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) { uint32_t port_id = _if->get_port_id(); uint32_t rule_id = (port_id % m_if_per_card) * MAX_FLOW_STATS + min; // Since flow dir counters are not wrapped around as promised in the data sheet, but rather get stuck at 0xffffffff // we reset the HW value rte_eth_fdir_stats_reset(port_id, NULL, rule_id, len); for (int i =0; i < len; i++) { stats[i] = 0; } } // instead of adding this to rte_ethdev.h extern "C" int rte_eth_fdir_stats_get(uint8_t port_id, uint32_t *stats, uint32_t start, uint32_t len); // we read every 0.5 second. We want to catch the counter when it approach the maximum (where it will stuck, // and we will start losing packets). const uint32_t X710_FDIR_RESET_THRESHOLD = 0xffffffff - 1000000000/8/64*40; // get rx stats on _if, between min and max // prev_pkts should be the previous values read from the hardware. // Getting changed to be equal to current HW values. // pkts return the diff between prev_pkts and current hw values // bytes and prev_bytes are not used. X710 fdir filters do not support byte count. int CTRexExtendedDriverBase40G::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts ,uint32_t *bytes, uint32_t *prev_bytes, int min, int max) { uint32_t hw_stats[MAX_FLOW_STATS]; uint32_t port_id = _if->get_port_id(); uint32_t start = (port_id % m_if_per_card) * MAX_FLOW_STATS + min; uint32_t len = max - min + 1; uint32_t loop_start = min; rte_eth_fdir_stats_get(port_id, hw_stats, start, len); for (int i = loop_start; i < loop_start + len; i++) { if (unlikely(hw_stats[i - min] > X710_FDIR_RESET_THRESHOLD)) { // When x710 fdir counters reach max of 32 bits (4G), the get stuck. To handle this, we temporarily // move to temp counter, reset the counter in danger, and go back to using it. // see trex-199 for more details uint32_t counter, temp_count; uint32_t hw_id = start - min + i; add_del_rules( RTE_ETH_FILTER_ADD, port_id, fdir_hw_id_rule_params[hw_id].rule_type, 0 , IP_ID_RESERVE_BASE + i, fdir_hw_id_rule_params[hw_id].l4_proto, MAIN_DPDK_DATA_Q , FDIR_TEMP_HW_ID); delay(100); rte_eth_fdir_stats_reset(port_id, &counter, hw_id, 1); add_del_rules( RTE_ETH_FILTER_ADD, port_id, fdir_hw_id_rule_params[hw_id].rule_type, 0 , IP_ID_RESERVE_BASE + i, fdir_hw_id_rule_params[hw_id].l4_proto, MAIN_DPDK_DATA_Q, hw_id); delay(100); rte_eth_fdir_stats_reset(port_id, &temp_count, FDIR_TEMP_HW_ID, 1); pkts[i] = counter + temp_count - prev_pkts[i]; prev_pkts[i] = 0; } else { pkts[i] = hw_stats[i - min] - prev_pkts[i]; prev_pkts[i] = hw_stats[i - min]; } bytes[i] = 0; } return 0; } // if fd != NULL, dump fdir stats of _if // return num of filters int CTRexExtendedDriverBase40G::dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) { uint32_t port_id = _if->get_port_id(); struct rte_eth_fdir_stats stat; int ret; ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_STATS, (void*)&stat); if (ret == 0) { if (fd) fprintf(fd, "Num filters on guarant poll:%d, best effort poll:%d\n", stat.guarant_cnt, stat.best_cnt); return (stat.guarant_cnt + stat.best_cnt); } else { if (fd) fprintf(fd, "Failed reading fdir statistics\n"); return -1; } } void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats) { struct rte_eth_stats stats1; struct rte_eth_stats *prev_stats = &stats->m_prev_stats; rte_eth_stats_get(_if->get_port_id(), &stats1); stats->ipackets += stats1.ipackets - prev_stats->ipackets; stats->ibytes += stats1.ibytes - prev_stats->ibytes; stats->opackets += stats1.opackets - prev_stats->opackets; stats->obytes += stats1.obytes - prev_stats->obytes + (stats1.opackets << 2) - (prev_stats->opackets << 2); stats->f_ipackets += 0; stats->f_ibytes += 0; stats->ierrors += stats1.imissed + stats1.ierrors + stats1.rx_nombuf - prev_stats->imissed - prev_stats->ierrors - prev_stats->rx_nombuf; stats->oerrors += stats1.oerrors - prev_stats->oerrors; stats->imcasts += 0; stats->rx_nombuf += stats1.rx_nombuf - prev_stats->rx_nombuf; prev_stats->ipackets = stats1.ipackets; prev_stats->ibytes = stats1.ibytes; prev_stats->opackets = stats1.opackets; prev_stats->obytes = stats1.obytes; prev_stats->imissed = stats1.imissed; prev_stats->oerrors = stats1.oerrors; prev_stats->ierrors = stats1.ierrors; prev_stats->rx_nombuf = stats1.rx_nombuf; } int CTRexExtendedDriverBase40G::wait_for_stable_link(){ wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic); return (0); } extern "C" int rte_eth_get_fw_ver(int port, uint32_t *ver); int CTRexExtendedDriverBase40G::verify_fw_ver(int port_id) { uint32_t version; int ret; ret = rte_eth_get_fw_ver(port_id, &version); if (ret == 0) { if (CGlobalInfo::m_options.preview.getVMode() >= 1) { printf("port %d: FW ver %02d.%02d.%02d\n", port_id, ((version >> 12) & 0xf), ((version >> 4) & 0xff) ,(version & 0xf)); } if ((((version >> 12) & 0xf) < 5) || ((((version >> 12) & 0xf) == 5) && ((version >> 4 & 0xff) == 0) && ((version & 0xf) < 4))) { printf("Error: In this TRex version, X710 firmware must be at least 05.00.04\n"); printf(" Please refer to %s for upgrade instructions\n", "https://trex-tgn.cisco.com/trex/doc/trex_manual.html#_firmware_update_to_xl710_x710"); exit(1); } } return ret; } CFlowStatParser *CTRexExtendedDriverBase40G::get_flow_stat_parser() { CFlowStatParser *parser = new CFlowStatParser(); assert (parser); return parser; } int CTRexExtendedDriverBase40G::set_rcv_all(CPhyEthIF * _if, bool set_on) { uint32_t port_id = _if->get_port_id(); enum rte_filter_op op = set_on ? RTE_ETH_FILTER_ADD : RTE_ETH_FILTER_DELETE; add_del_eth_type_rule(port_id, op, EthernetHeader::Protocol::ARP); if (set_on) { i40e_trex_fdir_reg_init(port_id, I40E_TREX_INIT_RCV_ALL); } // In order to receive packets, we also need to configure rules for each type. add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 10, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, 10, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 10, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 10, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, 10, 0, 0, MAIN_DPDK_RX_Q, 0); add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 10, 0, 0, MAIN_DPDK_RX_Q, 0); if (! set_on) { configure_rx_filter_rules(_if); } return 0; } ///////////////////////////////////////////////////////////////////// void CTRexExtendedDriverBase1GVm::update_configuration(port_cfg_t * cfg){ struct rte_eth_dev_info dev_info; rte_eth_dev_info_get((uint8_t) 0,&dev_info); cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH_1G; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = 0; cfg->m_tx_conf.txq_flags=dev_info.default_txconf.txq_flags; } int CTRexExtendedDriverBase1GVm::configure_rx_filter_rules(CPhyEthIF * _if){ return (0); } void CTRexExtendedDriverBase1GVm::clear_extended_stats(CPhyEthIF * _if){ rte_eth_stats_reset(_if->get_port_id()); } int CTRexExtendedDriverBase1GVm::stop_queue(CPhyEthIF * _if, uint16_t q_num) { return (0); } void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ struct rte_eth_stats stats1; struct rte_eth_stats *prev_stats = &stats->m_prev_stats; rte_eth_stats_get(_if->get_port_id(), &stats1); stats->ipackets += stats1.ipackets - prev_stats->ipackets; stats->ibytes += stats1.ibytes - prev_stats->ibytes; stats->opackets += stats1.opackets - prev_stats->opackets; stats->obytes += stats1.obytes - prev_stats->obytes; stats->f_ipackets += 0; stats->f_ibytes += 0; stats->ierrors += stats1.imissed + stats1.ierrors + stats1.rx_nombuf - prev_stats->imissed - prev_stats->ierrors - prev_stats->rx_nombuf; stats->oerrors += stats1.oerrors - prev_stats->oerrors; stats->imcasts += 0; stats->rx_nombuf += stats1.rx_nombuf - prev_stats->rx_nombuf; prev_stats->ipackets = stats1.ipackets; prev_stats->ibytes = stats1.ibytes; prev_stats->opackets = stats1.opackets; prev_stats->obytes = stats1.obytes; prev_stats->imissed = stats1.imissed; prev_stats->oerrors = stats1.oerrors; prev_stats->ierrors = stats1.ierrors; prev_stats->rx_nombuf = stats1.rx_nombuf; } int CTRexExtendedDriverBase1GVm::wait_for_stable_link(){ wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic); return (0); } /** * convert chain of mbuf to one big mbuf * * @param m * * @return */ struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m){ unsigned int len; struct rte_mbuf * r; struct rte_mbuf * old_m; old_m=m; len=rte_pktmbuf_pkt_len(m); /* allocate one big mbuf*/ r = CGlobalInfo::pktmbuf_alloc(0,len); assert(r); if (r==0) { rte_pktmbuf_free(m); return(r); } char *p=rte_pktmbuf_append(r,len); while ( m ) { len = m->data_len; assert(len); memcpy(p,(char *)m->buf_addr, len); p+=len; m = m->next; } rte_pktmbuf_free(old_m); return(r); } /** * handle a signal for termination * * @author imarom (7/27/2016) * * @param signum */ static void trex_termination_handler(int signum) { std::stringstream ss; /* be sure that this was given on the main process */ assert(rte_eal_process_type() == RTE_PROC_PRIMARY); switch (signum) { case SIGINT: g_trex.mark_for_shutdown(CGlobalTRex::SHUTDOWN_SIGINT); break; case SIGTERM: g_trex.mark_for_shutdown(CGlobalTRex::SHUTDOWN_SIGTERM); break; default: assert(0); } } /*********************************************************** * platfrom API object * TODO: REMOVE THIS TO A SEPERATE FILE * **********************************************************/ int TrexDpdkPlatformApi::get_xstats_values(uint8_t port_id, xstats_values_t &xstats_values) const { return g_trex.m_ports[port_id].get_port_attr()->get_xstats_values(xstats_values); } int TrexDpdkPlatformApi::get_xstats_names(uint8_t port_id, xstats_names_t &xstats_names) const { return g_trex.m_ports[port_id].get_port_attr()->get_xstats_names(xstats_names); } void TrexDpdkPlatformApi::get_port_num(uint8_t &port_num) const { port_num = g_trex.m_max_ports; } void TrexDpdkPlatformApi::get_global_stats(TrexPlatformGlobalStats &stats) const { CGlobalStats trex_stats; g_trex.get_stats(trex_stats); stats.m_stats.m_cpu_util = trex_stats.m_cpu_util; if (get_is_stateless()) { stats.m_stats.m_rx_cpu_util = trex_stats.m_rx_cpu_util; } stats.m_stats.m_tx_bps = trex_stats.m_tx_bps; stats.m_stats.m_tx_pps = trex_stats.m_tx_pps; stats.m_stats.m_total_tx_pkts = trex_stats.m_total_tx_pkts; stats.m_stats.m_total_tx_bytes = trex_stats.m_total_tx_bytes; stats.m_stats.m_rx_bps = trex_stats.m_rx_bps; stats.m_stats.m_rx_pps = /*trex_stats.m_rx_pps*/ 0; /* missing */ stats.m_stats.m_total_rx_pkts = trex_stats.m_total_rx_pkts; stats.m_stats.m_total_rx_bytes = trex_stats.m_total_rx_bytes; } void TrexDpdkPlatformApi::get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const { } uint8_t TrexDpdkPlatformApi::get_dp_core_count() const { return CGlobalInfo::m_options.get_number_of_dp_cores_needed(); } void TrexDpdkPlatformApi::port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const { CPhyEthIF *lpt = &g_trex.m_ports[port_id]; /* copy data from the interface */ cores_id_list = lpt->get_core_list(); } void TrexDpdkPlatformApi::get_interface_info(uint8_t interface_id, intf_info_st &info) const { struct ether_addr rte_mac_addr; info.driver_name = CTRexExtendedDriverDb::Ins()->get_driver_name(); info.has_crc = CTRexExtendedDriverDb::Ins()->get_drv()->has_crc_added(); /* mac INFO */ /* hardware */ g_trex.m_ports[interface_id].get_port_attr()->macaddr_get(&rte_mac_addr); assert(ETHER_ADDR_LEN == 6); /* software */ uint8_t sw_macaddr[12]; 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]; } info.numa_node = g_trex.m_ports[interface_id].m_dev_info.pci_dev->numa_node; struct rte_pci_addr *loc = &g_trex.m_ports[interface_id].m_dev_info.pci_dev->addr; char pci_addr[50]; snprintf(pci_addr, sizeof(pci_addr), PCI_PRI_FMT, loc->domain, loc->bus, loc->devid, loc->function); info.pci_addr = pci_addr; } void TrexDpdkPlatformApi::publish_async_data_now(uint32_t key, bool baseline) const { g_trex.publish_async_data(true, baseline); 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(); capabilities = CTRexExtendedDriverDb::Ins()->get_drv()->get_rx_stat_capabilities(); } int TrexDpdkPlatformApi::get_flow_stats(uint8 port_id, void *rx_stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const { if (type == TrexPlatformApi::IF_STAT_PAYLOAD) { return g_trex.m_ports[port_id].get_flow_stats_payload((rx_per_flow_t *)rx_stats, (tx_per_flow_t *)tx_stats , min, max, reset); } else { return g_trex.m_ports[port_id].get_flow_stats((rx_per_flow_t *)rx_stats, (tx_per_flow_t *)tx_stats , min, max, reset); } } int TrexDpdkPlatformApi::get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const { return g_trex.m_rx_sl.get_rfc2544_info((rfc2544_info_t *)rfc2544_info, min, max, reset); } int TrexDpdkPlatformApi::get_rx_err_cntrs(void *rx_err_cntrs) const { return g_trex.m_rx_sl.get_rx_err_cntrs((CRxCoreErrCntrs *)rx_err_cntrs); } int TrexDpdkPlatformApi::reset_hw_flow_stats(uint8_t port_id) const { return g_trex.m_ports[port_id].reset_hw_flow_stats(); } int TrexDpdkPlatformApi::add_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 CTRexExtendedDriverDb::Ins()->get_drv() ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_ADD, l3_type, l4_proto, ipv6_next_h, id); } int TrexDpdkPlatformApi::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 CTRexExtendedDriverDb::Ins()->get_drv() ->add_del_rx_flow_stat_rule(port_id, RTE_ETH_FILTER_DELETE, l3_type, l4_proto, ipv6_next_h, id); } void TrexDpdkPlatformApi::flush_dp_messages() const { g_trex.check_for_dp_messages(); } int TrexDpdkPlatformApi::get_active_pgids(flow_stat_active_t &result) const { return g_trex.m_trex_stateless->m_rx_flow_stat.get_active_pgids(result); } int TrexDpdkPlatformApi::get_cpu_util_full(cpu_util_full_t &cpu_util_full) const { uint8_t p1; uint8_t p2; cpu_util_full.resize((int)g_trex.m_fl.m_threads_info.size()); for (int thread_id=0; thread_id<(int)g_trex.m_fl.m_threads_info.size(); thread_id++) { /* history */ CFlowGenListPerThread *lp = g_trex.m_fl.m_threads_info[thread_id]; cpu_vct_st &per_cpu = cpu_util_full[thread_id]; lp->m_cpu_cp_u.GetHistory(per_cpu); /* active ports */ lp->get_port_ids(p1, p2); per_cpu.m_port1 = (lp->is_port_active(p1) ? p1 : -1); per_cpu.m_port2 = (lp->is_port_active(p2) ? p2 : -1); } return 0; } int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const { CGlobalInfo::dump_pool_as_json(mbuf_pool); return 0; } CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const { return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser(); } TRexPortAttr *TrexDpdkPlatformApi::getPortAttrObj(uint8_t port_id) const { return g_trex.m_ports[port_id].get_port_attr(); } int DpdkTRexPortAttr::set_rx_filter_mode(rx_filter_mode_e rx_filter_mode) { CPhyEthIF *_if = &g_trex.m_ports[m_port_id]; bool recv_all = (rx_filter_mode == RX_FILTER_MODE_ALL); int rc = CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(_if, recv_all); if (rc != 0) { return (rc); } m_rx_filter_mode = rx_filter_mode; return (0); } /** * marks the control plane for a total server shutdown * * @author imarom (7/27/2016) */ void TrexDpdkPlatformApi::mark_for_shutdown() const { g_trex.mark_for_shutdown(CGlobalTRex::SHUTDOWN_RPC_REQ); }