From d3b66fddf9840272b367f42e26ce16198eeadaf6 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Mon, 11 Apr 2016 10:34:23 +0300 Subject: payload tx stat work --- src/bp_sim.h | 3 +- src/flow_stat.cpp | 254 ++++++++++++++++++++-------- src/flow_stat.h | 30 +++- src/gtest/trex_stateless_gtest.cpp | 102 ++++++++++- src/internal_api/trex_platform_api.h | 2 +- src/latency.h | 3 + src/main_dpdk.cpp | 63 +++++-- src/stateless/cp/trex_exception.h | 3 +- src/stateless/cp/trex_stream.cpp | 2 +- src/stateless/cp/trex_stream.h | 1 + src/stateless/dp/trex_stateless_dp_core.cpp | 2 +- src/trex_defs.h | 3 + 12 files changed, 378 insertions(+), 90 deletions(-) diff --git a/src/bp_sim.h b/src/bp_sim.h index ff80b560..97e14158 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -273,7 +273,8 @@ public: uint64_t m_tx_drop; uint64_t m_tx_queue_full; uint64_t m_tx_alloc_error; - tx_per_flow_t m_tx_per_flow[MAX_FLOW_STATS]; + tx_per_flow_t m_tx_per_flow[MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD]; + uint64_t m_seq_num[MAX_FLOW_STATS_PAYLOAD]; // seq num to put in packet for payload rules CPerTxthreadTemplateInfo m_template; public: diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp index 10b0c3ea..5640b054 100644 --- a/src/flow_stat.cpp +++ b/src/flow_stat.cpp @@ -62,6 +62,7 @@ stream_del: HW_ID_INIT static const uint16_t HW_ID_INIT = UINT16_MAX; static const uint16_t HW_ID_FREE = UINT16_MAX - 1; +static const uint8_t PAYLOAD_RULE_PROTO = 255; inline std::string methodName(const std::string& prettyFunction) { @@ -133,17 +134,16 @@ std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdInfo& cf) { return os; } -int CFlowStatUserIdInfo::add_stream(uint8_t proto) { +void CFlowStatUserIdInfo::add_stream(uint8_t proto) { #ifdef __DEBUG_FUNC_ENTRY__ std::cout << __METHOD_NAME__ << " proto:" << (uint16_t)proto << std::endl; #endif if (proto != m_proto) - throw TrexException("Can't use same pg_id for streams with different l4 protocol"); + throw TrexFStatEx("Can't use same pg_id for streams with different l4 protocol", + TrexException::T_FLOW_STAT_PG_ID_DIFF_L4); m_ref_count++; - - return 0; } void CFlowStatUserIdInfo::reset_hw_id() { @@ -159,6 +159,15 @@ void CFlowStatUserIdInfo::reset_hw_id() { memset(&m_tx_counter[i], 0, sizeof(m_tx_counter[0])); } } + +/************** class CFlowStatUserIdInfoPayload ***************/ +void CFlowStatUserIdInfoPayload::add_stream(uint8_t proto) { + //??? add unit test + throw TrexFStatEx("Can't have two streams with same packet group id for payload rules" + , TrexException::T_FLOW_STAT_DUP_PG_ID); +} + + /************** class CFlowStatUserIdMap ***************/ CFlowStatUserIdMap::CFlowStatUserIdMap() { @@ -204,21 +213,28 @@ CFlowStatUserIdMap::add_user_id(uint32_t user_id, uint8_t proto) { #endif CFlowStatUserIdInfo *new_id = new CFlowStatUserIdInfo(proto); + + if (proto == PAYLOAD_RULE_PROTO) { + new_id = new CFlowStatUserIdInfo(proto); + } else { + new_id = new CFlowStatUserIdInfoPayload(proto); + } if (new_id != NULL) { std::pair ret; ret = m_map.insert(std::pair(user_id, new_id)); if (ret.second == false) { - printf("%s Error: Trying to add user id %d which already exist\n", __func__, user_id); delete new_id; - return NULL; + throw TrexFStatEx("packet group id " + std::to_string(user_id) + " already exists" + , TrexException::T_FLOW_STAT_ALREADY_EXIST); } return new_id; } else { - return NULL; + throw TrexFStatEx("Failed allocating memory for new statistic counter" + , TrexException::T_FLOW_STAT_ALLOC_FAIL); } } -int CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) { +void CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) { #ifdef __DEBUG_FUNC_ENTRY__ std::cout << __METHOD_NAME__ << " user id:" << user_id << " proto:" << (uint16_t)proto << std::endl; @@ -228,12 +244,9 @@ int CFlowStatUserIdMap::add_stream(uint32_t user_id, uint8_t proto) { c_user_id = find_user_id(user_id); if (! c_user_id) { - c_user_id = add_user_id(user_id, proto); - if (! c_user_id) - throw TrexException("Failed adding statistic counter - Failure in add_stream"); - return 0; + c_user_id = add_user_id(user_id, proto); // throws exception on error } else { - return c_user_id->add_stream(proto); + c_user_id->add_stream(proto); } } @@ -246,7 +259,8 @@ int CFlowStatUserIdMap::del_stream(uint32_t user_id) { c_user_id = find_user_id(user_id); if (! c_user_id) { - throw TrexException("Trying to delete stream which does not exist"); + throw TrexFStatEx("Trying to delete stream which does not exist" + , TrexException::T_FLOW_STAT_DEL_NON_EXIST); } if (c_user_id->del_stream() == 0) { @@ -267,15 +281,16 @@ int CFlowStatUserIdMap::start_stream(uint32_t user_id, uint16_t hw_id) { c_user_id = find_user_id(user_id); if (! c_user_id) { - fprintf(stderr, "%s Error: Trying to associate hw id %d to user_id %d but it does not exist\n" - , __func__, hw_id, user_id); - throw TrexException("Internal error: Trying to associate non exist group id"); + throw TrexFStatEx("Internal error: Trying to associate non exist group id " + std::to_string(user_id) + + " to hardware id " + std::to_string(hw_id) + , TrexException::T_FLOW_STAT_ASSOC_NON_EXIST_ID); } if (c_user_id->is_hw_id()) { - fprintf(stderr, "%s Error: Trying to associate hw id %d to user_id %d but it is already associated to %u\n" - , __func__, hw_id, user_id, c_user_id->get_hw_id()); - throw TrexException("Internal error: Trying to associate used packet group id to different hardware counter"); + throw TrexFStatEx("Internal error: Trying to associate hw id " + std::to_string(hw_id) + " to user_id " + + std::to_string(user_id) + ", but it is already associated to " + + std::to_string(c_user_id->get_hw_id()) + , TrexException::T_FLOW_STAT_ASSOC_OCC_ID); } c_user_id->set_hw_id(hw_id); c_user_id->add_started_stream(); @@ -292,9 +307,8 @@ int CFlowStatUserIdMap::start_stream(uint32_t user_id) { c_user_id = find_user_id(user_id); if (! c_user_id) { - fprintf(stderr, "%s Error: Trying to start stream on pg_id %d but it does not exist\n" - , __func__, user_id); - throw TrexException("Trying to start stream with non exist packet group id"); + throw TrexFStatEx("Trying to start stream with non exist packet group id " + std::to_string(user_id) + , TrexException::T_FLOW_STAT_NON_EXIST_ID); } c_user_id->add_started_stream(); @@ -313,9 +327,8 @@ int CFlowStatUserIdMap::stop_stream(uint32_t user_id) { c_user_id = find_user_id(user_id); if (! c_user_id) { - fprintf(stderr, "%s Error: Trying to stop stream on pg_id %d but it does not exist\n" - , __func__, user_id); - throw TrexException("Trying to stop stream with non exist packet group id"); + throw TrexFStatEx("Trying to stop stream with non exist packet group id" + std::to_string(user_id) + , TrexException::T_FLOW_STAT_NON_EXIST_ID); } return c_user_id->stop_started_stream(); @@ -418,6 +431,7 @@ void CFlowStatHwIdMap::unmap(uint16_t hw_id) { CFlowStatRuleMgr::CFlowStatRuleMgr() { m_api = NULL; m_max_hw_id = -1; + m_max_hw_id_payload = -1; m_num_started_streams = 0; m_ring_to_rx = NULL; m_capabilities = 0; @@ -453,6 +467,7 @@ void CFlowStatRuleMgr::create() { std::ostream& operator<<(std::ostream& os, const CFlowStatRuleMgr& cf) { os << "Flow stat rule mgr (" << cf.m_num_ports << ") ports:" << std::endl; os << cf.m_hw_id_map; + os << cf.m_hw_id_map_payload; os << cf.m_user_id_map; return os; } @@ -466,23 +481,24 @@ int CFlowStatRuleMgr::compile_stream(const TrexStream * stream, CFlowStatParser if (parser->parse(stream->m_pkt.binary, stream->m_pkt.len) != 0) { // if we could not parse the packet, but no stat count needed, it is probably OK. if (stream->m_rx_check.m_enabled) { - fprintf(stderr, "Error: %s - Compilation failed\n", __func__); - throw TrexException("Failed parsing given packet for flow stat. Probably bad packet format."); + throw TrexFStatEx("Failed parsing given packet for flow stat. Probably bad packet format." + , TrexException::T_FLOW_STAT_BAD_PKT_FORMAT); } else { return 0; } } if (!parser->is_stat_supported()) { - if (stream->m_stream_id <= 0) { + if (! stream->m_rx_check.m_enabled) { // flow stat not needed. Do nothing. return 0; } else { // flow stat needed, but packet format is not supported - fprintf(stderr, "Error: %s - Unsupported packet format for flow stat\n", __func__); - throw TrexException("Unsupported packet format for flow stat on given interface type"); + throw TrexFStatEx("Unsupported packet format for flow stat on given interface type" + , TrexException::T_FLOW_STAT_UNSUPP_PKT_FORMAT); } } + return 0; } @@ -511,24 +527,41 @@ int CFlowStatRuleMgr::add_stream(TrexStream * stream) { //??? put back assert(stream->m_rx_check.m_hw_id == HW_ID_INIT); - uint16_t rule_type = TrexPlatformApi::IF_STAT_IPV4_ID; // In the future need to get it from the stream; + uint16_t rule_type = stream->m_rx_check.m_rule_type; if ((m_capabilities & rule_type) == 0) { - fprintf(stderr, "Error: %s - rule type not supported by interface\n", __func__); - throw TrexException("Interface does not support given rule type"); + throw TrexFStatEx("Interface does not support given rule type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE_FOR_IF); } // compile_stream throws exception if something goes wrong compile_stream(stream, m_parser); - uint8_t l4_proto; - if (m_parser->get_l4_proto(l4_proto) < 0) { - fprintf(stderr, "Error: %s failed finding l4 proto\n", __func__); - throw TrexException("Failed determining l4 proto for packet"); - } + switch(rule_type) { + case TrexPlatformApi::IF_STAT_IPV4_ID: + uint8_t l4_proto; + if (m_parser->get_l4_proto(l4_proto) < 0) { + throw TrexFStatEx("Failed determining l4 proto for packet", TrexException::T_FLOW_STAT_FAILED_FIND_L4); + } - // throws exception if there is error - m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, l4_proto); + // throws exception if there is error + m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, l4_proto); + break; + case TrexPlatformApi::IF_STAT_PAYLOAD: + uint16_t payload_len; + if (m_parser->get_payload_len(stream->m_pkt.binary, stream->m_pkt.len, payload_len) < 0) { + throw TrexFStatEx("Failed getting payload len", TrexException::T_FLOW_STAT_BAD_PKT_FORMAT); + } + if (payload_len < sizeof(struct flow_stat_payload_header)) { + throw TrexFStatEx("Need at least " + std::to_string(sizeof(struct latency_header)) + + " payload bytes for payload rules. Packet only has " + std::to_string(payload_len) + " bytes" + , TrexException::T_FLOW_STAT_PAYLOAD_TOO_SHORT); + } + m_user_id_map.add_stream(stream->m_rx_check.m_pg_id, PAYLOAD_RULE_PROTO); + break; + default: + throw TrexFStatEx("Wrong rule_type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE); + break; + } stream->m_rx_check.m_hw_id = HW_ID_FREE; return 0; @@ -545,11 +578,22 @@ int CFlowStatRuleMgr::del_stream(TrexStream * stream) { } if (! m_api) - throw TrexException("Called del_stream, but no stream was added"); + throw TrexFStatEx("Called del_stream, but no stream was added", TrexException::T_FLOW_STAT_NO_STREAMS_EXIST); + + uint16_t rule_type = stream->m_rx_check.m_rule_type; + switch(rule_type) { + case TrexPlatformApi::IF_STAT_IPV4_ID: + break; + case TrexPlatformApi::IF_STAT_PAYLOAD: + break; + default: + throw TrexFStatEx("Wrong rule_type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE); + break; + } // we got del_stream command for a stream which has valid hw_id. // Probably someone forgot to call stop - if(stream->m_rx_check.m_hw_id < MAX_FLOW_STATS) { + if(stream->m_rx_check.m_hw_id < MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD) { stop_stream(stream); } @@ -557,8 +601,7 @@ int CFlowStatRuleMgr::del_stream(TrexStream * stream) { if(stream->m_rx_check.m_hw_id == HW_ID_INIT) { return 0; } - // Throws exception in case of error - m_user_id_map.del_stream(stream->m_rx_check.m_pg_id); + m_user_id_map.del_stream(stream->m_rx_check.m_pg_id); // Throws exception in case of error stream->m_rx_check.m_hw_id = HW_ID_INIT; return 0; @@ -587,7 +630,7 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { if (! stream->m_rx_check.m_enabled) { try { compile_stream(stream, m_parser); - } catch (TrexException) { + } catch (TrexFStatEx) { // If no statistics needed, and we can't parse the stream, that's OK. return 0; } @@ -599,7 +642,8 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { // verify no reserved IP_ID used, and change if needed if (ip_id >= IP_ID_RESERVE_BASE) { if (m_parser->set_ip_id(ip_id & 0xefff) < 0) { - throw TrexException("Stream IP ID in reserved range. Failed changing it"); + throw TrexFStatEx("Stream IP ID in reserved range. Failed changing it" + , TrexException::T_FLOW_STAT_FAILED_CHANGE_IP_ID); } } return 0; @@ -612,15 +656,15 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { add_stream(stream); } - if (stream->m_rx_check.m_hw_id < MAX_FLOW_STATS) { - throw TrexException("Starting a stream which was already started"); + if (stream->m_rx_check.m_hw_id < MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD) { + throw TrexFStatEx("Starting a stream which was already started" + , TrexException::T_FLOW_STAT_ALREADY_STARTED); } - uint16_t rule_type = TrexPlatformApi::IF_STAT_IPV4_ID; // In the future, need to get it from the stream; + uint16_t rule_type = stream->m_rx_check.m_rule_type; if ((m_capabilities & rule_type) == 0) { - fprintf(stderr, "Error: %s - rule type not supported by interface\n", __func__); - throw TrexException("Interface does not support given rule type"); + throw TrexFStatEx("Interface does not support given rule type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE_FOR_IF); } // compile_stream throws exception if something goes wrong @@ -629,22 +673,43 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { uint16_t hw_id; + switch(rule_type) { + case TrexPlatformApi::IF_STAT_IPV4_ID: + break; + case TrexPlatformApi::IF_STAT_PAYLOAD: + break; + default: + throw TrexFStatEx("Wrong rule_type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE); + break; + } + if (m_user_id_map.is_started(stream->m_rx_check.m_pg_id)) { m_user_id_map.start_stream(stream->m_rx_check.m_pg_id); // just increase ref count; hw_id = m_user_id_map.get_hw_id(stream->m_rx_check.m_pg_id); // can't fail if we got here } else { - hw_id = m_hw_id_map.find_free_hw_id(); + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + hw_id = m_hw_id_map.find_free_hw_id(); + } else { + hw_id = m_hw_id_map_payload.find_free_hw_id(); + } if (hw_id == HW_ID_FREE) { - printf("Error: %s failed finding free hw_id\n", __func__); - throw TrexException("Failed allocating statistic counter. Probably all are used."); + throw TrexFStatEx("Failed allocating statistic counter. Probably all are used for this rule type." + , TrexException::T_FLOW_STAT_NO_FREE_HW_ID); } else { - if (hw_id > m_max_hw_id) { - m_max_hw_id = hw_id; - } uint32_t user_id = stream->m_rx_check.m_pg_id; m_user_id_map.start_stream(user_id, hw_id); - m_hw_id_map.map(hw_id, user_id); - add_hw_rule(hw_id, m_user_id_map.l4_proto(user_id)); + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + if (hw_id > m_max_hw_id) { + m_max_hw_id = hw_id; + } + m_hw_id_map.map(hw_id, user_id); + add_hw_rule(hw_id, m_user_id_map.l4_proto(user_id)); + } else { + if (hw_id > m_max_hw_id_payload) { + m_max_hw_id_payload = hw_id; + } + m_hw_id_map_payload.map(hw_id, user_id); + } // clear hardware counters. Just in case we have garbage from previous iteration rx_per_flow_t rx_counter; tx_per_flow_t tx_counter; @@ -657,7 +722,12 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { m_parser->set_ip_id(IP_ID_RESERVE_BASE + hw_id); // saving given hw_id on stream for use by tx statistics count - stream->m_rx_check.m_hw_id = hw_id; + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + stream->m_rx_check.m_hw_id = hw_id; + } else { + // for payload rules, we use the range right after ip id rules + stream->m_rx_check.m_hw_id = hw_id + MAX_FLOW_STATS; + } #ifdef __DEBUG_FUNC_ENTRY__ std::cout << "exit:" << __METHOD_NAME__ << " hw_id:" << hw_id << std::endl; @@ -675,13 +745,14 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { delay(1); count++; if (count == 100) { - throw TrexException("Critical error!! - RX core failed to start"); + throw TrexFStatEx("Critical error!! - RX core failed to start", TrexException::T_FLOW_STAT_RX_CORE_START_FAIL); } } } } else { // make sure rx core is working. If not, we got really confused somehow. - assert(m_rx_core->is_working()); + if (m_rx_core) + assert(m_rx_core->is_working()); } m_num_started_streams++; return 0; @@ -705,13 +776,24 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) { } if (! m_api) - throw TrexException("Called stop_stream, but no stream was added"); + throw TrexFStatEx("Called stop_stream, but no stream was added", TrexException::T_FLOW_STAT_NO_STREAMS_EXIST); - if (stream->m_rx_check.m_hw_id >= MAX_FLOW_STATS) { + if (stream->m_rx_check.m_hw_id >= MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD) { // We allow stopping while already stopped. Will not hurt us. return 0; } + uint16_t rule_type = stream->m_rx_check.m_rule_type; + switch(rule_type) { + case TrexPlatformApi::IF_STAT_IPV4_ID: + break; + case TrexPlatformApi::IF_STAT_PAYLOAD: + break; + default: + throw TrexFStatEx("Wrong rule_type", TrexException::T_FLOW_STAT_BAD_RULE_TYPE); + break; + } + stream->m_rx_check.m_hw_id = HW_ID_FREE; if (m_user_id_map.stop_stream(stream->m_rx_check.m_pg_id) == 0) { @@ -720,16 +802,24 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) { uint8_t proto = m_user_id_map.l4_proto(stream->m_rx_check.m_pg_id); uint16_t hw_id = m_user_id_map.get_hw_id(stream->m_rx_check.m_pg_id); if (hw_id >= MAX_FLOW_STATS) { - fprintf(stderr, "Error: %s got wrong hw_id %d from unmap\n", __func__, hw_id); - throw TrexException("Internal error in stop_stream. Got bad hw_id"); + throw TrexFStatEx("Internal error in stop_stream. Got bad hw_id" + std::to_string(hw_id) + , TrexException::T_FLOW_STAT_BAD_HW_ID); } else { + CFlowStatUserIdInfo *p_user_id; // update counters, and reset before unmapping - CFlowStatUserIdInfo *p_user_id = m_user_id_map.find_user_id(m_hw_id_map.get_user_id(hw_id)); + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + p_user_id = m_user_id_map.find_user_id(m_hw_id_map.get_user_id(hw_id)); + } else { + p_user_id = m_user_id_map.find_user_id(m_hw_id_map_payload.get_user_id(hw_id)); + hw_id += MAX_FLOW_STATS; + } assert(p_user_id != NULL); rx_per_flow_t rx_counter; tx_per_flow_t tx_counter; for (uint8_t port = 0; port < m_num_ports; port++) { - m_api->del_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id); + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + m_api->del_rx_flow_stat_rule(port, FLOW_STAT_RULE_TYPE_IPV4_ID, proto, hw_id); + } m_api->get_flow_stats(port, &rx_counter, (void *)&tx_counter, hw_id, hw_id, true); // when stopping, always send counters for stopped stream one last time p_user_id->set_rx_counter(port, rx_counter); @@ -738,7 +828,11 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) { p_user_id->set_need_to_send_tx(port); } m_user_id_map.unmap(stream->m_rx_check.m_pg_id); - m_hw_id_map.unmap(hw_id); + if (rule_type == TrexPlatformApi::IF_STAT_IPV4_ID) { + m_hw_id_map.unmap(hw_id); + } else { + m_hw_id_map_payload.unmap(hw_id); + } } } m_num_started_streams--; @@ -774,7 +868,9 @@ void CFlowStatRuleMgr::send_start_stop_msg_to_rx(bool is_start) { // return false if no counters changed since last run. true otherwise bool CFlowStatRuleMgr::dump_json(std::string & json, bool baseline) { rx_per_flow_t rx_stats[MAX_FLOW_STATS]; + rx_per_flow_t rx_stats_payload[MAX_FLOW_STATS]; tx_per_flow_t tx_stats[MAX_FLOW_STATS]; + tx_per_flow_t tx_stats_payload[MAX_FLOW_STATS_PAYLOAD]; Json::FastWriter writer; Json::Value root; @@ -797,7 +893,9 @@ bool CFlowStatRuleMgr::dump_json(std::string & json, bool baseline) { // read hw counters, and update for (uint8_t port = 0; port < m_num_ports; port++) { m_api->get_flow_stats(port, rx_stats, (void *)tx_stats, 0, m_max_hw_id, false); + m_api->get_flow_stats(port, rx_stats_payload, (void *)tx_stats_payload, MAX_FLOW_STATS, MAX_FLOW_STATS + m_max_hw_id_payload, false); for (int i = 0; i <= m_max_hw_id; i++) { + //??? add rx for payload rules if (rx_stats[i].get_pkts() != 0) { rx_per_flow_t rx_pkts = rx_stats[i]; CFlowStatUserIdInfo *p_user_id = m_user_id_map.find_user_id(m_hw_id_map.get_user_id(i)); @@ -825,6 +923,22 @@ bool CFlowStatRuleMgr::dump_json(std::string & json, bool baseline) { } } } + // payload rules + for (int i = 0; i <= m_max_hw_id_payload; i++) { + if (tx_stats_payload[i].get_pkts() != 0) { + tx_per_flow_t tx_pkts = tx_stats_payload[i]; + CFlowStatUserIdInfo *p_user_id = m_user_id_map.find_user_id(m_hw_id_map_payload.get_user_id(i)); + if (likely(p_user_id != NULL)) { + if (p_user_id->get_tx_counter(port) != tx_pkts) { + p_user_id->set_tx_counter(port, tx_pkts); + p_user_id->set_need_to_send_tx(port); + } + } else { + std::cerr << __METHOD_NAME__ << i << ":Could not count " << tx_pkts << " tx packets on port " + << (uint16_t)port << ", because no mapping was found." << std::endl; + } + } + } } // build json report diff --git a/src/flow_stat.h b/src/flow_stat.h index 06b54d70..91ee76d5 100644 --- a/src/flow_stat.h +++ b/src/flow_stat.h @@ -25,6 +25,7 @@ #include #include #include "trex_defs.h" +#include "trex_exception.h" #include "trex_stream.h" #include "msg_manager.h" #include @@ -39,6 +40,20 @@ typedef std::map::iterator flow_stat_map_it_t; class CRxCoreStateless; +struct flow_stat_payload_header { + uint64_t time_stamp; + uint16_t hw_id; + uint16_t magic; + uint32_t seq; +}; + + +class TrexFStatEx : public TrexException { + public: + TrexFStatEx(const std::string &what, enum TrexExceptionTypes_t type): TrexException(what, type) { + } +}; + class tx_per_flow_t_ { public: tx_per_flow_t_() { @@ -111,6 +126,7 @@ class CFlowStatParser; class CFlowStatUserIdInfo { public: CFlowStatUserIdInfo(uint8_t proto); + virtual ~CFlowStatUserIdInfo() {}; friend std::ostream& operator<<(std::ostream& os, const CFlowStatUserIdInfo& cf); void set_rx_counter(uint8_t port, rx_per_flow_t val) {m_rx_counter[port] = val;} rx_per_flow_t get_rx_counter(uint8_t port) {return m_rx_counter[port] + m_rx_counter_base[port];} @@ -122,7 +138,7 @@ class CFlowStatUserIdInfo { bool is_hw_id() {return (m_hw_id != UINT16_MAX);} uint64_t get_proto() {return m_proto;} uint8_t get_ref_count() {return m_ref_count;} - int add_stream(uint8_t proto); + virtual void add_stream(uint8_t proto); int del_stream() {m_ref_count--; return m_ref_count;} void add_started_stream() {m_trans_ref_count++;} int stop_started_stream() {m_trans_ref_count--; return m_trans_ref_count;} @@ -155,6 +171,12 @@ class CFlowStatUserIdInfo { typedef std::map flow_stat_user_id_map_t; typedef std::map::iterator flow_stat_user_id_map_it_t; +class CFlowStatUserIdInfoPayload : public CFlowStatUserIdInfo { + public: + CFlowStatUserIdInfoPayload(uint8_t proto) : CFlowStatUserIdInfo(proto){}; + virtual void add_stream(uint8_t proto); +}; + class CFlowStatUserIdMap { public: CFlowStatUserIdMap(); @@ -163,7 +185,7 @@ class CFlowStatUserIdMap { uint16_t get_hw_id(uint32_t user_id); CFlowStatUserIdInfo * find_user_id(uint32_t user_id); CFlowStatUserIdInfo * add_user_id(uint32_t user_id, uint8_t proto); - int add_stream(uint32_t user_id, uint8_t proto); + void add_stream(uint32_t user_id, uint8_t proto); int del_stream(uint32_t user_id); int start_stream(uint32_t user_id, uint16_t hw_id); int start_stream(uint32_t user_id); @@ -219,11 +241,15 @@ class CFlowStatRuleMgr { private: CFlowStatHwIdMap m_hw_id_map; // map hw ids to user ids + // ??? need to make CFlowStatHwIdMap class adjustable per size. For now it is working since we allow same number + // of IP ID and pyaload rules + CFlowStatHwIdMap m_hw_id_map_payload; // map hw id numbers of payload rules to user ids CFlowStatUserIdMap m_user_id_map; // map user ids to hw ids uint8_t m_num_ports; // How many ports are being used const TrexPlatformApi *m_api; const CRxCoreStateless *m_rx_core; int m_max_hw_id; // max hw id we ever used + int m_max_hw_id_payload; // max hw id we ever used for payload rules uint32_t m_num_started_streams; // How many started (transmitting) streams we have CNodeRing *m_ring_to_rx; // handle for sending messages to Rx core CFlowStatParser *m_parser; diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index 50f8e5ec..18a09a2c 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -3676,7 +3676,7 @@ TEST_F(basic_stl, pcap_remote_duration) { /********************************************* Itay Tests End *************************************/ -class rx_stat_pkt_parse : public testing::Test { +class flow_stat_pkt_parse : public testing::Test { protected: virtual void SetUp() { } @@ -3686,12 +3686,110 @@ class rx_stat_pkt_parse : public testing::Test { }; -TEST_F(rx_stat_pkt_parse, x710_parser) { +TEST_F(flow_stat_pkt_parse, x710_parser) { CFlowStatParser parser; parser.test(); } +class flow_stat : public testing::Test { + protected: + virtual void SetUp() { + } + virtual void TearDown() { + } + public: +}; + +static const uint8_t TEST_L4_PROTO = IPPROTO_TCP; + +TEST_F(flow_stat, add_del_stream) { + CFlowStatRuleMgr rule_mgr; + uint8_t test_pkt[] = { + // ether header + 0x74, 0xa2, 0xe6, 0xd5, 0x39, 0x25, + 0xa0, 0x36, 0x9f, 0x38, 0xa4, 0x02, + 0x81, 0x00, + 0x0a, 0xbc, 0x08, 0x00, // vlan + // IP header + 0x45,0x02,0x00,0x30, + 0x01,0x02,0x40,0x00, + 0xff, TEST_L4_PROTO, 0xbd,0x04, + 0x10,0x0,0x0,0x1, + 0x30,0x0,0x0,0x1, + // TCP heaader + 0xab, 0xcd, 0x00, 0x80, // src, dst ports + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // seq num, ack num + 0x50, 0x00, 0xff, 0xff, // Header size, flags, window size + 0x00, 0x00, 0x00, 0x00, // checksum ,urgent pointer + // some extra bytes + 0x1, 0x2, 0x3, 0x4 + }; + + TrexStream stream(TrexStream::stSINGLE_BURST, 0, 0); + TrexStream stream2(TrexStream::stSINGLE_BURST, 0, 0); + + stream.m_rx_check.m_enabled = true; + stream.m_rx_check.m_rule_type = 7; + stream.m_rx_check.m_pg_id = 5; + stream.m_pkt.binary = (uint8_t *)test_pkt; + stream.m_pkt.len = sizeof(test_pkt); + rule_mgr.init_stream(&stream); + + try { + rule_mgr.del_stream(&stream); + } catch (TrexFStatEx e) { + assert(e.type() == TrexException::T_FLOW_STAT_NO_STREAMS_EXIST); + } + + try { + rule_mgr.add_stream(&stream); + } catch (TrexFStatEx e) { + assert(e.type() == TrexException::T_FLOW_STAT_BAD_RULE_TYPE); + } + + stream.m_rx_check.m_rule_type = TrexPlatformApi::IF_STAT_PAYLOAD; + try { + rule_mgr.add_stream(&stream); + } catch (TrexFStatEx e) { + assert(e.type() == TrexException::T_FLOW_STAT_PAYLOAD_TOO_SHORT); + } + + // change to UDP packet so it will be fine to work with + test_pkt[27] = IPPROTO_UDP; + int ret = rule_mgr.add_stream(&stream); + assert (ret == 0); + ret = rule_mgr.del_stream(&stream); + assert (ret == 0); + + stream2.m_rx_check.m_rule_type = TrexPlatformApi::IF_STAT_IPV4_ID; + stream2.m_rx_check.m_pg_id = 5; // ??? same as first stream + stream2.m_pkt.binary = (uint8_t *)test_pkt; + stream2.m_pkt.len = sizeof(test_pkt); + ret = rule_mgr.add_stream(&stream2); + assert (ret == 0); + + + ret = rule_mgr.del_stream(&stream2); + assert (ret == 0); + try { + rule_mgr.del_stream(&stream2); + } catch (TrexFStatEx e) { + assert(e.type() == TrexException::T_FLOW_STAT_DEL_NON_EXIST); + } + + // do not want the constructor to try to free it + stream.m_pkt.binary = NULL; + stream2.m_pkt.binary = NULL; +} + +TEST_F(flow_stat, start_stop_stream) { + // try starting with no add + // try starting more than 128 streams + // check that ip_id is changed for streams with no flow stat + // check that ip_id is changed for streams with flow stat IP_ID, PAYLOAD + +} diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index b8f40df2..fd6cdaf8 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -219,7 +219,7 @@ public: virtual void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const { } - virtual void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const {num_counters=128; capabilities=TrexPlatformApi::IF_STAT_IPV4_ID; } + virtual void get_interface_stat_info(uint8_t interface_id, uint16_t &num_counters, uint16_t &capabilities) const {num_counters=128; capabilities=TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD; } virtual void port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const { for (int i = 0; i < m_dp_core_count; i++) { diff --git a/src/latency.h b/src/latency.h index 259bb221..cfa523e4 100644 --- a/src/latency.h +++ b/src/latency.h @@ -247,6 +247,9 @@ public: uint64_t m_no_ipv4_option; uint64_t m_length_error; rx_per_flow_t m_rx_pg_stat[MAX_FLOW_STATS]; + rx_per_flow_t m_rx_pg_payload_stats[MAX_FLOW_STATS_PAYLOAD]; + CTimeHistogram m_per_flow_hist[MAX_FLOW_STATS_PAYLOAD]; /* all window */ + CJitter m_per_flow_jitter[MAX_FLOW_STATS_PAYLOAD]; CTimeHistogram m_hist; /* all window */ CJitter m_jitter; }; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index fd5a1de8..669389a2 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -101,6 +101,7 @@ extern "C" void i40e_set_trex_mode(int mode); #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); @@ -191,7 +192,10 @@ public: virtual void clear_extended_stats(CPhyEthIF * _if); virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) {return 0;} virtual int get_stat_counters_num() {return MAX_FLOW_STATS;} - virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;} + 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(); }; @@ -244,7 +248,10 @@ public: virtual int wait_for_stable_link(); virtual int get_stat_counters_num() {return MAX_FLOW_STATS;} - virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;} + virtual int get_rx_stat_capabilities() { + return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT + | TrexPlatformApi::IF_STAT_PAYLOAD; + } }; @@ -281,7 +288,10 @@ public: virtual void clear_extended_stats(CPhyEthIF * _if); virtual int wait_for_stable_link(); virtual int get_stat_counters_num() {return MAX_FLOW_STATS;} - virtual int get_rx_stat_capabilities() {return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_RX_BYTES_COUNT;} + 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(); }; @@ -329,7 +339,9 @@ public: 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;} + virtual int get_rx_stat_capabilities() { + return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD; + } virtual int wait_for_stable_link(); // disabling flow control on 40G using DPDK API causes the interface to malfunction virtual bool flow_control_disable_supported(){return false;} @@ -2025,10 +2037,22 @@ int CCoreEthIFStateless::send_node(CGenNode * no) { if (unlikely(node_sl->is_stat_needed())) { uint16_t hw_id = node_sl->get_stat_hw_id(); - if (hw_id > max_stat_hw_id_seen) { - max_stat_hw_id_seen = hw_id; + tx_per_flow_t *lp_s; + if (hw_id >= MAX_FLOW_STATS) { + // payload rule + // 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; + } + //??? add seq num (m_seq_num[..], timestamp + } else { + // ip id rule + if (hw_id > max_stat_hw_id_seen) { + max_stat_hw_id_seen = hw_id; + } } - tx_per_flow_t *lp_s = &lp_stats->m_tx_per_flow[hw_id]; + lp_s = &lp_stats->m_tx_per_flow[hw_id]; lp_s->add_pkts(1); lp_s->add_bytes(m->pkt_len); } @@ -2300,8 +2324,8 @@ public: uint64_t ibytes; uint64_t ierrors; uint64_t oerrors; - tx_per_flow_t m_tx_per_flow[MAX_FLOW_STATS]; - tx_per_flow_t m_prev_tx_per_flow[MAX_FLOW_STATS]; + 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; @@ -3509,10 +3533,14 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ 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(); + } } uint64_t total_open_flows=0; @@ -3560,12 +3588,21 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){ 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; @@ -4125,6 +4162,7 @@ int CPhyEthIF::get_flow_stats(rx_per_flow_t *rx_stats, tx_per_flow_t *tx_stats, uint32_t diff_bytes[MAX_FLOW_STATS]; bool hw_rx_stat_supported = get_ex_drv()->hw_rx_stat_supported(); + // ???? if 40G, but payload rules, need to read from software 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) { @@ -4939,7 +4977,9 @@ int CTRexExtendedDriverBase10G::configure_rx_filter_rules_stateless(CPhyEthIF * uint8_t port_id = _if->get_rte_port_id(); int ip_id_lsb; - for (ip_id_lsb = 0; ip_id_lsb < MAX_FLOW_STATS; 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; @@ -5214,6 +5254,7 @@ int CTRexExtendedDriverBase40G::configure_rx_filter_rules_statfull(CPhyEthIF * _ const uint32_t TEMP_FDIR_HW_ID = 511; int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) { if (get_is_stateless()) { + //??? if we add here one rule for IP/TCP/OTHER, it lowers our IP_ID support to 127 rules rte_eth_fdir_stats_reset(_if->get_port_id(), NULL, TEMP_FDIR_HW_ID, 1); return 0; // Rules are configured dynamically in stateless } else { diff --git a/src/stateless/cp/trex_exception.h b/src/stateless/cp/trex_exception.h index e184fa30..ff3179f9 100644 --- a/src/stateless/cp/trex_exception.h +++ b/src/stateless/cp/trex_exception.h @@ -34,7 +34,8 @@ class TrexException : public std::runtime_error public: enum TrexExceptionTypes_t { T_FLOW_STAT_PG_ID_DIFF_L4, - T_FLOW_STAT_ADD_FAIL, + T_FLOW_STAT_DUP_PG_ID, + T_FLOW_STAT_ALLOC_FAIL, T_FLOW_STAT_DEL_NON_EXIST, T_FLOW_STAT_ASSOC_NON_EXIST_ID, T_FLOW_STAT_ASSOC_OCC_ID, diff --git a/src/stateless/cp/trex_stream.cpp b/src/stateless/cp/trex_stream.cpp index 5a24e2b3..9021ebf4 100644 --- a/src/stateless/cp/trex_stream.cpp +++ b/src/stateless/cp/trex_stream.cpp @@ -144,7 +144,7 @@ TrexStream::TrexStream(uint8_t type, m_expected_pkt_len = 0; m_rx_check.m_enabled = false; - + m_rx_check.m_rule_type = TrexPlatformApi::IF_STAT_PAYLOAD; // default for now. Should come from user??? m_burst_total_pkts=0; m_num_bursts=1; diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index ba5fa214..a0daac9e 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -551,6 +551,7 @@ public: bool m_enabled; bool m_seq_enabled; bool m_latency; + uint8_t m_rule_type; uint32_t m_pg_id; uint16_t m_hw_id; } m_rx_check; diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp index 3d99e0fd..833fb6e1 100644 --- a/src/stateless/dp/trex_stateless_dp_core.cpp +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -804,7 +804,7 @@ TrexStatelessDpCore::add_stream(TrexStatelessDpPerPort * lp_port, if (stream->m_rx_check.m_enabled) { node->set_stat_needed(); uint8_t hw_id = stream->m_rx_check.m_hw_id; - assert (hw_id < MAX_FLOW_STATS); + assert (hw_id < MAX_FLOW_STATS + MAX_FLOW_STATS_PAYLOAD); node->set_stat_hw_id(hw_id); } diff --git a/src/trex_defs.h b/src/trex_defs.h index 50bb282a..665c1edc 100644 --- a/src/trex_defs.h +++ b/src/trex_defs.h @@ -20,7 +20,10 @@ limitations under the License. #define TREX_MAX_PORTS 12 +// maximum number of IP ID type flow stats we support #define MAX_FLOW_STATS 127 +// maximum number of payload type flow stats we support +#define MAX_FLOW_STATS_PAYLOAD 128 #ifndef UINT8_MAX #define UINT8_MAX 255 -- cgit 1.2.3-korg