From a43d922db832fdf47a2a129a24b985c46b85c9a3 Mon Sep 17 00:00:00 2001 From: Adam Kiripolsky Date: Thu, 12 Dec 2024 13:28:02 +0100 Subject: [PATCH 1/4] dpdk/rss: move and change rss rte_flow functions Move and adjust the base of RSS configuration from util-dpdk-i40e.c to a new file that can be later utilized by other cards. RSS configuration can be configured via rte_flow rules. This is useful for possible future features such as specific header offload (vxlan, nvgre) also implemented via rte_flow rules, as rte_flow rules can be chained via groups and priorities. i40e uses multiple different rte_flow rules to setup RSS. At first, function DeviceSetRSSFlowQueues() is used to setup rx queues. This rule matches all types of traffic, so the equivalent to dpdk-testpmd pattern would be "pattern end" This rule can not contain hash types (ipv4, ipv6 etc.) nor hash key. The hash function used here is RTE_ETH_HASH_FUNCTION_DEFAULT. The syntax in dpdk-testpmd for this rule with attributes: port index == 0 used rx queue indices == 0 1 2 3 is as follows: "flow create 0 ingress pattern end actions rss queues 0 1 2 3 end func default / end" The other rules configured by i40eDeviceSetRSSFlowIPv4() and i40eDeviceSetRSSFlowIPv6() match specific type of traffic by l4 protocol (none, TCP, UDP, SCTP). For example, pattern to match l3 ipv4 with l4 tcp traffic in dpdk-testpmd syntax would be equivalent of "pattern eth / ipv4 / tcp / end". These rules can not have rx queues configured, but have hash types (ipv4 src dst / ipv6 src dst) and a symmetric hash key configured. The length of the key is dependent on DPDK version. The hash function (either RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ or RTE_ETH_HASH_FUNCTION_TOEPLITZ, depending on DPKD version) used in these rules hashes symmetricaly due to the symmetric hash key. The syntax in dpdk-testpmd for rule to match ipv4-tcp traffic with attributes: port index == 0 == 6d5a symmetric hash key is as follows: "flow create 0 ingress pattern eth / ipv4 / tcp / end actions rss types ipv4 end queues end key key_len 52 func toeplitz / end" (queues need to be set to NULL) Ticket: 7337 --- src/Makefile.am | 2 + src/runmode-dpdk.c | 15 +-- src/source-dpdk.c | 17 ++-- src/util-dpdk-i40e.c | 226 ++++++++++++++++++------------------------- src/util-dpdk-i40e.h | 4 +- src/util-dpdk-rss.c | 169 ++++++++++++++++++++++++++++++++ src/util-dpdk-rss.h | 47 +++++++++ 7 files changed, 323 insertions(+), 157 deletions(-) create mode 100644 src/util-dpdk-rss.c create mode 100644 src/util-dpdk-rss.h diff --git a/src/Makefile.am b/src/Makefile.am index 615816953542..a52ce8930d40 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -481,6 +481,7 @@ noinst_HEADERS = \ util-dpdk-ice.h \ util-dpdk-ixgbe.h \ util-dpdk-bonding.h \ + util-dpdk-rss.h \ util-ebpf.h \ util-enum.h \ util-error.h \ @@ -1031,6 +1032,7 @@ libsuricata_c_a_SOURCES = \ util-dpdk-ice.c \ util-dpdk-ixgbe.c \ util-dpdk-bonding.c \ + util-dpdk-rss.c \ util-ebpf.c \ util-enum.c \ util-error.c \ diff --git a/src/runmode-dpdk.c b/src/runmode-dpdk.c index 5df81f685883..cf6141209883 100644 --- a/src/runmode-dpdk.c +++ b/src/runmode-dpdk.c @@ -41,10 +41,11 @@ #include "util-debug.h" #include "util-device.h" #include "util-dpdk.h" +#include "util-dpdk-bonding.h" #include "util-dpdk-i40e.h" #include "util-dpdk-ice.h" #include "util-dpdk-ixgbe.h" -#include "util-dpdk-bonding.h" +#include "util-dpdk-rss.h" #include "util-time.h" #include "util-conf.h" #include "suricata.h" @@ -52,15 +53,6 @@ #ifdef HAVE_DPDK -#define RSS_HKEY_LEN 40 -// General purpose RSS key for symmetric bidirectional flow distribution -uint8_t rss_hkey[] = { - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 40 - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 52 -}; - // Calculates the closest multiple of y from x #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) @@ -883,7 +875,6 @@ static DPDKIfaceConfig *ConfigParse(const char *iface) static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const char *driver_name) { - // RSS is configured in a specific way for a driver i40e and DPDK version <= 19.xx if (strcmp(driver_name, "net_i40e") == 0) i40eDeviceSetRSSConf(rss_conf); if (strcmp(driver_name, "net_ice") == 0) @@ -1157,7 +1148,7 @@ static void PortConfSetRSSConf(const DPDKIfaceConfig *iconf, if (iconf->nb_rx_queues > 1) { SCLogConfig("%s: RSS enabled for %d queues", iconf->iface, iconf->nb_rx_queues); port_conf->rx_adv_conf.rss_conf = (struct rte_eth_rss_conf){ - .rss_key = rss_hkey, + .rss_key = RSS_HKEY, .rss_key_len = RSS_HKEY_LEN, .rss_hf = iconf->rss_hf, }; diff --git a/src/source-dpdk.c b/src/source-dpdk.c index 1453b3ba7a13..7f777142a49c 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -88,6 +88,8 @@ TmEcode NoDPDKSupportExit(ThreadVars *tv, const void *initdata, void **data) #include "util-affinity.h" #include "util-dpdk.h" #include "util-dpdk-i40e.h" +#include "util-dpdk-ice.h" +#include "util-dpdk-ixgbe.h" #include "util-dpdk-bonding.h" #include @@ -190,14 +192,11 @@ static inline void DPDKFreeMbufArray( static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) { - if (strcmp(driver_name, "net_bonding") == 0) { + if (strcmp(driver_name, "net_bonding") == 0) driver_name = BondingDeviceDriverGet(ptv->port_id); - } - - // The PMD Driver i40e has a special way to set the RSS, it can be set via rte_flow rules - // and only after the start of the port if (strcmp(driver_name, "net_i40e") == 0) - i40eDeviceSetRSS(ptv->port_id, ptv->threads); + i40eDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); + } static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) @@ -207,7 +206,7 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr } if (strcmp(driver_name, "net_i40e") == 0) { -#if RTE_VERSION > RTE_VERSION_NUM(20, 0, 0, 0) +#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) // Flush the RSS rules that have been inserted in the post start section struct rte_flow_error flush_error = { 0 }; int32_t retval = rte_flow_flush(ptv->port_id, &flush_error); @@ -215,7 +214,7 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", ptv->livedev->dev, rte_strerror(-retval), flush_error.message); } -#endif /* RTE_VERSION > RTE_VERSION_NUM(20, 0, 0, 0) */ +#endif /* RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) */ } } diff --git a/src/util-dpdk-i40e.c b/src/util-dpdk-i40e.c index 2484b45c793b..38e063789f52 100644 --- a/src/util-dpdk-i40e.c +++ b/src/util-dpdk-i40e.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -34,10 +34,17 @@ #include "util-dpdk.h" #include "util-debug.h" #include "util-dpdk-bonding.h" +#include "util-dpdk-rss.h" #ifdef HAVE_DPDK -#define I40E_RSS_HKEY_LEN 52 +#if RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) +#define I40E_RSS_HKEY_LEN 40 +#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ +#else +#define I40E_RSS_HKEY_LEN 52 +#define I40E_RSS_HASH_FUNCTION RTE_ETH_HASH_FUNCTION_TOEPLITZ +#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) #if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0) static int i40eDeviceEnableSymHash( @@ -54,7 +61,7 @@ static int i40eDeviceEnableSymHash( retval = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH); #pragma GCC diagnostic pop if (retval < 0) { - SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name); + SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name); return retval; } @@ -72,7 +79,7 @@ static int i40eDeviceEnableSymHash( #pragma GCC diagnostic pop if (retval < 0) { - SCLogError("Cannot set global hash configurations on port %s", port_name); + SCLogError("%s: cannot set global hash configurations", port_name); return retval; } @@ -92,7 +99,7 @@ static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable) #pragma GCC diagnostic pop if (ret < 0) { - SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name); + SCLogError("%s: RTE_ETH_FILTER_HASH not supported", port_name); return ret; } @@ -104,7 +111,7 @@ static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable) #pragma GCC diagnostic pop if (ret < 0) { - SCLogError("Cannot set symmetric hash enable per port on port %s", port_name); + SCLogError("%s: cannot set symmetric hash enable per port", port_name); return ret; } @@ -166,203 +173,163 @@ static int32_t i40eDeviceSetRSSWithFilter(int port_id, const char *port_name) #else -static int i40eDeviceSetRSSFlowQueues( - int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf, int nb_rx_queues) -{ - struct rte_flow_action_rss rss_action_conf = { 0 }; - struct rte_flow_attr attr = { 0 }; - struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } }; - struct rte_flow_action action[] = { { 0 }, { 0 } }; - struct rte_flow *flow; - struct rte_flow_error flow_error = { 0 }; - uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; - - for (int i = 0; i < nb_rx_queues; ++i) - queues[i] = i; - - rss_action_conf.func = RTE_ETH_HASH_FUNCTION_DEFAULT; - rss_action_conf.level = 0; - rss_action_conf.types = 0; // queues region can not be configured with types - rss_action_conf.key_len = 0; - rss_action_conf.key = NULL; - - if (nb_rx_queues < 1) { - FatalError("The number of queues for RSS configuration must be " - "configured with a positive number"); - } - - rss_action_conf.queue_num = nb_rx_queues; - rss_action_conf.queue = queues; - - attr.ingress = 1; - pattern[0].type = RTE_FLOW_ITEM_TYPE_END; - action[0].type = RTE_FLOW_ACTION_TYPE_RSS; - action[0].conf = &rss_action_conf; - action[1].type = RTE_FLOW_ACTION_TYPE_END; - - flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); - if (flow == NULL) { - SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message); - int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error); - SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name, - rte_strerror(-ret), flow_error.message); - return ret; - } else { - SCLogInfo("RTE_FLOW queue region created for port %s", port_name); - } - return 0; -} - -static int i40eDeviceCreateRSSFlow(int port_id, const char *port_name, - struct rte_eth_rss_conf rss_conf, uint64_t rss_type, struct rte_flow_item *pattern) -{ - struct rte_flow_action_rss rss_action_conf = { 0 }; - struct rte_flow_attr attr = { 0 }; - struct rte_flow_action action[] = { { 0 }, { 0 } }; - struct rte_flow *flow; - struct rte_flow_error flow_error = { 0 }; - - rss_action_conf.func = RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ; - rss_action_conf.level = 0; - rss_action_conf.types = rss_type; - rss_action_conf.key_len = rss_conf.rss_key_len; - rss_action_conf.key = rss_conf.rss_key; - rss_action_conf.queue_num = 0; - rss_action_conf.queue = NULL; - - attr.ingress = 1; - action[0].type = RTE_FLOW_ACTION_TYPE_RSS; - action[0].conf = &rss_action_conf; - action[1].type = RTE_FLOW_ACTION_TYPE_END; - - flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); - if (flow == NULL) { - SCLogError("Error when creating rte_flow rule on %s: %s", port_name, flow_error.message); - int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error); - SCLogError("Error on rte_flow validation for port %s: %s errmsg: %s", port_name, - rte_strerror(-ret), flow_error.message); - return ret; - } else { - SCLogInfo("RTE_FLOW flow rule created for port %s", port_name); - } - - return 0; -} - static int i40eDeviceSetRSSFlowIPv4( - int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf) + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) { int ret = 0; struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } }; +#if RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; pattern[2].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern); memset(pattern, 0, sizeof(pattern)); - +#else pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; pattern[2].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV4, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_OTHER, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_UDP, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_TCP, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV4_SCTP, pattern); + memset(pattern, 0, sizeof(pattern)); +#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) return ret; } static int i40eDeviceSetRSSFlowIPv6( - int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf) + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) { int ret = 0; struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 }, { 0 } }; - +#if RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; pattern[2].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, pattern); memset(pattern, 0, sizeof(pattern)); pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP; pattern[3].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow( - port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern); memset(pattern, 0, sizeof(pattern)); - +#else pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; pattern[2].type = RTE_FLOW_ITEM_TYPE_END; - ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV6, pattern); + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_OTHER, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; + pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_UDP, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; + pattern[2].type = RTE_FLOW_ITEM_TYPE_TCP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_TCP, pattern); + memset(pattern, 0, sizeof(pattern)); + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; + pattern[2].type = RTE_FLOW_ITEM_TYPE_SCTP; + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + ret |= DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_NONFRAG_IPV6_SCTP, pattern); + memset(pattern, 0, sizeof(pattern)); +#endif // RTE_VERSION < RTE_VERSION_NUM(21, 0, 0, 0) return ret; } static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_rx_queues) { - int retval; - uint8_t rss_key[I40E_RSS_HKEY_LEN]; + uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; struct rte_flow_error flush_error = { 0 }; struct rte_eth_rss_conf rss_conf = { - .rss_key = rss_key, + .rss_key = RSS_HKEY, .rss_key_len = I40E_RSS_HKEY_LEN, }; - retval = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf); - if (retval != 0) { - SCLogError("Unable to get RSS hash configuration of port %s", port_name); - return retval; + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); } - retval = 0; - retval |= i40eDeviceSetRSSFlowQueues(port_id, port_name, rss_conf, nb_rx_queues); - retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_conf); - retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_conf); + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, false); + + int retval = DPDKSetRSSFlowQueues(port_id, port_name, rss_action_conf); + + memset(&rss_action_conf, 0, sizeof(struct rte_flow_action_rss)); + rss_action_conf = DPDKInitRSSAction(rss_conf, 0, queues, I40E_RSS_HASH_FUNCTION, true); + + retval |= i40eDeviceSetRSSFlowIPv4(port_id, port_name, rss_action_conf); + retval |= i40eDeviceSetRSSFlowIPv6(port_id, port_name, rss_action_conf); if (retval != 0) { retval = rte_flow_flush(port_id, &flush_error); if (retval != 0) { - SCLogError("Unable to flush rte_flow rules of %s: %s Flush error msg: %s", port_name, + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, rte_strerror(-retval), flush_error.message); } return retval; @@ -373,18 +340,9 @@ static int i40eDeviceSetRSSWithFlows(int port_id, const char *port_name, int nb_ #endif /* RTE_VERSION < RTE_VERSION_NUM(20,0,0,0) */ -int i40eDeviceSetRSS(int port_id, int nb_rx_queues) +int i40eDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) { - int retval; (void)nb_rx_queues; // avoid unused variable warnings - char port_name[RTE_ETH_NAME_MAX_LEN]; - - retval = rte_eth_dev_get_name_by_port(port_id, port_name); - if (unlikely(retval != 0)) { - SCLogError("Failed to convert port id %d to the interface name: %s", port_id, - strerror(-retval)); - return retval; - } #if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) i40eDeviceSetRSSWithFlows(port_id, port_name, nb_rx_queues); diff --git a/src/util-dpdk-i40e.h b/src/util-dpdk-i40e.h index 6133aed5d771..964d68074752 100644 --- a/src/util-dpdk-i40e.h +++ b/src/util-dpdk-i40e.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -30,7 +30,7 @@ #include "util-dpdk.h" -int i40eDeviceSetRSS(int port_id, int nb_rx_queues); +int i40eDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); void i40eDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-dpdk-rss.c b/src/util-dpdk-rss.c new file mode 100644 index 000000000000..0c4ad457a50e --- /dev/null +++ b/src/util-dpdk-rss.c @@ -0,0 +1,169 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \defgroup dpdk DPDK rte_flow RSS helpers functions + * + * @{ + */ + +/** + * \file + * + * \author Adam Kiripolsky + * + * DPDK rte_flow RSS helper functions + * + */ + +#include "util-dpdk-rss.h" +#include "util-dpdk.h" +#include "util-debug.h" + +#ifdef HAVE_DPDK + +uint8_t RSS_HKEY[] = { + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 40 + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 52 +}; + +/** + * \brief Initialize RSS action configuration for + * RTE_FLOW RSS rule based on input arguments + * + * \param rss_conf RSS configuration + * \param nb_rx_queues number of rx queues + * \param queues array of queue indexes + * \param func RSS hash function + * \param set_key flag to set RSS hash key and its length + * \return struct rte_flow_action_rss RSS action configuration + * to be used in a rule + */ +struct rte_flow_action_rss DPDKInitRSSAction(struct rte_eth_rss_conf rss_conf, int nb_rx_queues, + uint16_t *queues, enum rte_eth_hash_function func, bool set_key) +{ + struct rte_flow_action_rss rss_action_conf = { 0 }; + rss_action_conf.func = func; + rss_action_conf.level = 0; + + if (set_key) { + rss_action_conf.key = rss_conf.rss_key; + rss_action_conf.key_len = rss_conf.rss_key_len; + } else { + rss_action_conf.key = NULL; + rss_action_conf.key_len = 0; + } + + if (nb_rx_queues != 0) { + for (int i = 0; i < nb_rx_queues; ++i) + queues[i] = i; + + rss_action_conf.queue = queues; + } else { + rss_action_conf.queue = NULL; + } + rss_action_conf.queue_num = nb_rx_queues; + + return rss_action_conf; +} + +/** + * \brief Create RTE_FLOW RSS rule configured with pattern and rss_type + * but with no rx_queues configured. This is specific way of setting RTE_FLOW RSS rule + * for some drivers (mostly Intel NICs). This function's call must be preceded by + * call to function DeviceSetRSSFlowQueues(). + * + * \param port_id The port identifier of the Ethernet device + * \param port_name The port name of the Ethernet device + * \param rss_conf RSS configuration + * \param rss_type RSS hash type - only this type is used when creating hash with RSS hash function + * \param pattern pattern to match incoming traffic + * \return int 0 on success, a negative errno value otherwise + */ +int DPDKCreateRSSFlow(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf, + uint64_t rss_type, struct rte_flow_item *pattern) +{ + struct rte_flow_attr attr = { 0 }; + struct rte_flow_action action[] = { { 0 }, { 0 } }; + struct rte_flow_error flow_error = { 0 }; + + rss_conf.types = rss_type; + + attr.ingress = 1; + action[0].type = RTE_FLOW_ACTION_TYPE_RSS; + action[0].conf = &rss_conf; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); + if (flow == NULL) { + SCLogError("%s: rte_flow rule creation error: %s", port_name, flow_error.message); + int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error); + SCLogError("%s: rte_flow rule validation error: %s, errmsg: %s", port_name, + rte_strerror(-ret), flow_error.message); + return ret; + } else { + SCLogDebug("%s: rte_flow rule created", port_name); + } + + return 0; +} + +/** + * \brief Some drivers (mostly Intel NICs) require specific way of setting RTE_FLOW RSS rules + * with one rule that sets up only queues and other rules that specify patterns to match with + * queues configured (created with function DeviceCreateRSSFlow() that should follow after + * this function's call). + * + * \param port_id The port identifier of the Ethernet device + * \param port_name The port name of the Ethernet device + * \param rss_conf RSS configuration + * \return int 0 on success, a negative errno value otherwise + */ +int DPDKSetRSSFlowQueues(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_attr attr = { 0 }; + struct rte_flow_item pattern[] = { { 0 } }; + struct rte_flow_action action[] = { { 0 }, { 0 } }; + struct rte_flow_error flow_error = { 0 }; + + rss_conf.types = 0; // queues region can not be configured with types + + attr.ingress = 1; + pattern[0].type = RTE_FLOW_ITEM_TYPE_END; + action[0].type = RTE_FLOW_ACTION_TYPE_RSS; + action[0].conf = &rss_conf; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); + if (flow == NULL) { + SCLogError("%s: rte_flow rule creation error: %s", port_name, flow_error.message); + int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error); + SCLogError("%s: rte_flow rule validation error: %s, errmsg: %s", port_name, + rte_strerror(-ret), flow_error.message); + return ret; + } else { + SCLogDebug("%s: rte_flow rule created", port_name); + } + return 0; +} + +#endif /* HAVE_DPDK */ +/** + * @} + */ diff --git a/src/util-dpdk-rss.h b/src/util-dpdk-rss.h new file mode 100644 index 000000000000..8d5483f64a39 --- /dev/null +++ b/src/util-dpdk-rss.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Adam Kiripolsky + */ + +#ifndef UTIL_DPDK_RSS +#define UTIL_DPDK_RSS + +#include "suricata-common.h" + +#ifdef HAVE_DPDK + +#include "util-dpdk.h" + +#define RSS_HKEY_LEN 40 + +extern uint8_t RSS_HKEY[]; + +struct rte_flow_action_rss DPDKInitRSSAction(struct rte_eth_rss_conf rss_conf, int nb_rx_queues, + uint16_t *queues, enum rte_eth_hash_function func, bool set_key); +int DeviceCreateRSSFlowGeneric( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf); +int DPDKSetRSSFlowQueues(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf); +int DPDKCreateRSSFlow(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf, + uint64_t rss_type, struct rte_flow_item *pattern); + +#endif /* HAVE_DPDK */ + +#endif /* UTIL_DPDK_RSS */ From 6057a71022955aa1ce9f05b8e242a13799e90d07 Mon Sep 17 00:00:00 2001 From: Adam Kiripolsky Date: Tue, 10 Dec 2024 16:26:49 +0100 Subject: [PATCH 2/4] dpdk/rss: add rte_flow rss support for ixgbe ixgbe driver requires different configuration of RSS rte_flow rule than i40e, with just one generic rule matching all traffic. The generic rule configured by DeviceCreateRSSFlowGeneric() has pattern equivalent to "pattern eth / end" in dpdk-testpmd syntax. The rule must have rx queues configured. The rule hashes traffic to different queues based on ipv4 and ipv6 hash types (ipv4 src dst / ipv6 src dst). The hash key is 40 bytes long symmetric hash key. ixgbe does not support any other hash function than RTE_ETH_HASH_FUNCTION_DEFAULT. The syntax in dpdk-testpmd for this rule with attributes: port index == 0 used rx queue indices == 0 1 2 3 == 6d5a symmetric hash key is as follows: "flow create 0 ingress pattern eth / end actions rss types ipv4 ipv6 end queues 0 1 2 3 end key key_len 40 func default / end" Ticket: 7337 --- src/source-dpdk.c | 13 ++++++++++++- src/util-dpdk-ixgbe.c | 37 ++++++++++++++++++++++++++++++++++++- src/util-dpdk-ixgbe.h | 3 ++- src/util-dpdk-rss.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/util-dpdk-rss.h | 2 +- 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/source-dpdk.c b/src/source-dpdk.c index 7f777142a49c..a9573bb905af 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -196,7 +196,8 @@ static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *d driver_name = BondingDeviceDriverGet(ptv->port_id); if (strcmp(driver_name, "net_i40e") == 0) i40eDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); - + else if (strcmp(driver_name, "net_ixgbe") == 0) + ixgbeDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); } static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) @@ -216,6 +217,16 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr } #endif /* RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) */ } + + if (strcmp(driver_name, "net_ixgbe") == 0) { + // Flush the RSS rules that have been inserted in the post start section + struct rte_flow_error flush_error = { 0 }; + int32_t retval = rte_flow_flush(ptv->port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", + ptv->livedev->dev, rte_strerror(-retval), flush_error.message); + } + } } /** diff --git a/src/util-dpdk-ixgbe.c b/src/util-dpdk-ixgbe.c index 5627c45270d1..09f750274e81 100644 --- a/src/util-dpdk-ixgbe.c +++ b/src/util-dpdk-ixgbe.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -32,14 +32,49 @@ #include "util-dpdk-ixgbe.h" #include "util-dpdk.h" +#include "util-debug.h" +#include "util-dpdk-bonding.h" +#include "util-dpdk-rss.h" #ifdef HAVE_DPDK +#define IXGBE_RSS_HKEY_LEN 40 + void ixgbeDeviceSetRSSHashFunction(uint64_t *rss_hf) { *rss_hf = RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_IPV6_EX; } +int ixgbeDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; + struct rte_flow_error flush_error = { 0 }; + struct rte_eth_rss_conf rss_conf = { + .rss_key = RSS_HKEY, + .rss_key_len = IXGBE_RSS_HKEY_LEN, + }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, true); + + int retval = DPDKCreateRSSFlowGeneric(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + #endif /* HAVE_DPDK */ /** * @} diff --git a/src/util-dpdk-ixgbe.h b/src/util-dpdk-ixgbe.h index 3cb18fa55d75..79b7d5edb87c 100644 --- a/src/util-dpdk-ixgbe.h +++ b/src/util-dpdk-ixgbe.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -28,6 +28,7 @@ #ifdef HAVE_DPDK +int ixgbeDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); void ixgbeDeviceSetRSSHashFunction(uint64_t *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-dpdk-rss.c b/src/util-dpdk-rss.c index 0c4ad457a50e..48711e5e076f 100644 --- a/src/util-dpdk-rss.c +++ b/src/util-dpdk-rss.c @@ -83,6 +83,47 @@ struct rte_flow_action_rss DPDKInitRSSAction(struct rte_eth_rss_conf rss_conf, i return rss_action_conf; } +/** + * \brief Creates RTE_FLOW RSS rule used by NIC drivers + * to redistribute packets to different queues based + * on IP adresses. + * + * \param port_id The port identifier of the Ethernet device + * \param port_name The port name of the Ethernet device + * \param rss_conf RSS configuration + * \return int 0 on success, a negative errno value otherwise + */ +int DPDKCreateRSSFlowGeneric( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_attr attr = { 0 }; + struct rte_flow_action action[] = { { 0 }, { 0 } }; + struct rte_flow_error flow_error = { 0 }; + struct rte_flow_item pattern[] = { { 0 }, { 0 } }; + + rss_conf.types = RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6; + + attr.ingress = 1; + action[0].type = RTE_FLOW_ACTION_TYPE_RSS; + action[0].conf = &rss_conf; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + pattern[0].type = RTE_FLOW_ITEM_TYPE_END; + + struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); + if (flow == NULL) { + SCLogError("%s: rte_flow rule creation error: %s", port_name, flow_error.message); + int ret = rte_flow_validate(port_id, &attr, pattern, action, &flow_error); + SCLogError("%s: rte_flow rule validation error: %s, errmsg: %s", port_name, + rte_strerror(-ret), flow_error.message); + return ret; + } else { + SCLogDebug("%s: rte_flow rule created", port_name); + } + + return 0; +} + /** * \brief Create RTE_FLOW RSS rule configured with pattern and rss_type * but with no rx_queues configured. This is specific way of setting RTE_FLOW RSS rule diff --git a/src/util-dpdk-rss.h b/src/util-dpdk-rss.h index 8d5483f64a39..6be7914a0130 100644 --- a/src/util-dpdk-rss.h +++ b/src/util-dpdk-rss.h @@ -36,7 +36,7 @@ extern uint8_t RSS_HKEY[]; struct rte_flow_action_rss DPDKInitRSSAction(struct rte_eth_rss_conf rss_conf, int nb_rx_queues, uint16_t *queues, enum rte_eth_hash_function func, bool set_key); -int DeviceCreateRSSFlowGeneric( +int DPDKCreateRSSFlowGeneric( int port_id, const char *port_name, struct rte_flow_action_rss rss_conf); int DPDKSetRSSFlowQueues(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf); int DPDKCreateRSSFlow(int port_id, const char *port_name, struct rte_flow_action_rss rss_conf, From 3f866823a0f7e88daa7a02927c5a8b126d01c0b5 Mon Sep 17 00:00:00 2001 From: Adam Kiripolsky Date: Tue, 10 Dec 2024 16:27:23 +0100 Subject: [PATCH 3/4] dpdk/rss: add rte_flow rss support for ice ice driver requires 2 rte_flow rules for matching and redistributing incoming traffic with RSS. The rules set up by iceDeviceSetRSSFlowIPv4() and iceDeviceSetRSSFlowIPv6() are different only in the pattern ("pattern eth / ipv4 / end" or "pattern eth / ipv6 / end" in dpdk-testpmd syntax) and in the hash type (ipv4 src dst / ipv6 src dst). ice will match all ipv4 or ipv6 traffic independently of following l4 protocol. The rules can not have queues configured, implicitly they will use all queues available. The hash function is set to RTE_ETH_HASH_FUNCTION_TOEPLITZ. The hash key can not be set. The syntax in dpdk-testpmd for rule to match all ipv4 traffic with attributes: port index == 0 is as follows: "flow create 0 ingress pattern eth / ipv4 / end actions rss types ipv4 end queues end func toeplitz / end" (queues need to be set to NULL) Ticket: 7337 --- src/source-dpdk.c | 6 ++-- src/util-dpdk-ice.c | 74 ++++++++++++++++++++++++++++++++++++++++++++- src/util-dpdk-ice.h | 3 +- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/source-dpdk.c b/src/source-dpdk.c index a9573bb905af..b2cc5586dc9d 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -198,6 +198,8 @@ static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *d i40eDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); else if (strcmp(driver_name, "net_ixgbe") == 0) ixgbeDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); + else if (strcmp(driver_name, "net_ice") == 0) + iceDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); } static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) @@ -217,8 +219,8 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr } #endif /* RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) */ } - - if (strcmp(driver_name, "net_ixgbe") == 0) { + + if (strcmp(driver_name, "net_ixgbe") == 0 || strcmp(driver_name, "net_ice") == 0) { // Flush the RSS rules that have been inserted in the post start section struct rte_flow_error flush_error = { 0 }; int32_t retval = rte_flow_flush(ptv->port_id, &flush_error); diff --git a/src/util-dpdk-ice.c b/src/util-dpdk-ice.c index 8cd6b777e02f..ab90cb0240a4 100644 --- a/src/util-dpdk-ice.c +++ b/src/util-dpdk-ice.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -32,6 +32,9 @@ #include "util-dpdk-ice.h" #include "util-dpdk.h" +#include "util-dpdk-rss.h" +#include "util-debug.h" +#include "util-dpdk-bonding.h" #ifdef HAVE_DPDK @@ -46,6 +49,75 @@ static void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) #endif } +/** + * \brief Creates RTE_FLOW pattern to match ipv4 traffic + * + * \param port_id The port identifier of the Ethernet device + * \param port_name The port name of the Ethernet device + * \param rss_conf RSS configuration + * \return int 0 on success, a negative errno value otherwise + */ +static int iceDeviceSetRSSFlowIPv4( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 } }; + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].type = RTE_FLOW_ITEM_TYPE_END; + + return DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_IPV4, pattern); +} + +/** + * \brief Creates RTE_FLOW pattern to match ipv6 traffic + * + * \param port_id The port identifier of the Ethernet device + * \param port_name The port name of the Ethernet device + * \param rss_conf RSS configuration + * \return int 0 on success, a negative errno value otherwise + */ + +static int iceDeviceSetRSSFlowIPv6( + int port_id, const char *port_name, struct rte_flow_action_rss rss_conf) +{ + struct rte_flow_item pattern[] = { { 0 }, { 0 }, { 0 } }; + + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; + pattern[2].type = RTE_FLOW_ITEM_TYPE_END; + + return DPDKCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_IPV6, pattern); +} + +int iceDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; + struct rte_flow_error flush_error = { 0 }; + struct rte_eth_rss_conf rss_conf = { 0 }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, 0, queues, RTE_ETH_HASH_FUNCTION_TOEPLITZ, false); + + int retval = iceDeviceSetRSSFlowIPv4(port_id, port_name, rss_action_conf); + retval |= iceDeviceSetRSSFlowIPv6(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf) { iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); diff --git a/src/util-dpdk-ice.h b/src/util-dpdk-ice.h index d535fa092f9f..1cf36bfecb7d 100644 --- a/src/util-dpdk-ice.h +++ b/src/util-dpdk-ice.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 2021-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -30,6 +30,7 @@ #include "util-dpdk.h" +int iceDeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf); #endif /* HAVE_DPDK */ From 538646047dc9ad6dba56d20b224afc552faea53e Mon Sep 17 00:00:00 2001 From: Adam Kiripolsky Date: Tue, 14 Jan 2025 12:58:47 +0100 Subject: [PATCH 4/4] dpdk/rss: add rte_flow rss support for mlx5 The configuration of this rule is the same as for ixgbe driver except the hash function is not RTE_ETH_HASH_FUNCTION_DEFAULT but RTE_ETH_HASH_FUNCTION_TOEPLITZ. The syntax in dpdk-testpmd for this rule with attributes: port index == 0 used rx queue indices == 0 1 2 3 == 6d5a symmetric hash key is as follows: "flow create 0 ingress pattern eth / end actions rss types ipv4 ipv6 end queues 0 1 2 3 end key key_len 40 func toeplitz / end" Ticket: 7337 --- src/Makefile.am | 2 ++ src/source-dpdk.c | 8 +++-- src/util-dpdk-mlx5.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ src/util-dpdk-mlx5.h | 35 ++++++++++++++++++++ src/util-dpdk-rss.c | 3 +- 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/util-dpdk-mlx5.c create mode 100644 src/util-dpdk-mlx5.h diff --git a/src/Makefile.am b/src/Makefile.am index a52ce8930d40..818a398f4f05 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -480,6 +480,7 @@ noinst_HEADERS = \ util-dpdk-i40e.h \ util-dpdk-ice.h \ util-dpdk-ixgbe.h \ + util-dpdk-mlx5.h \ util-dpdk-bonding.h \ util-dpdk-rss.h \ util-ebpf.h \ @@ -1031,6 +1032,7 @@ libsuricata_c_a_SOURCES = \ util-dpdk-i40e.c \ util-dpdk-ice.c \ util-dpdk-ixgbe.c \ + util-dpdk-mlx5.c \ util-dpdk-bonding.c \ util-dpdk-rss.c \ util-ebpf.c \ diff --git a/src/source-dpdk.c b/src/source-dpdk.c index b2cc5586dc9d..62953e9382b7 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -90,6 +90,7 @@ TmEcode NoDPDKSupportExit(ThreadVars *tv, const void *initdata, void **data) #include "util-dpdk-i40e.h" #include "util-dpdk-ice.h" #include "util-dpdk-ixgbe.h" +#include "util-dpdk-mlx5.h" #include "util-dpdk-bonding.h" #include @@ -200,6 +201,8 @@ static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *d ixgbeDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); else if (strcmp(driver_name, "net_ice") == 0) iceDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); + else if (strcmp(driver_name, "mlx5_pci") == 0) + mlx5DeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev); } static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *driver_name) @@ -219,8 +222,9 @@ static void DevicePreClosePMDSpecificActions(DPDKThreadVars *ptv, const char *dr } #endif /* RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0) */ } - - if (strcmp(driver_name, "net_ixgbe") == 0 || strcmp(driver_name, "net_ice") == 0) { + + if (strcmp(driver_name, "net_ixgbe") == 0 || strcmp(driver_name, "net_ice") == 0 || + strcmp(driver_name, "mlx5_pci") == 0) { // Flush the RSS rules that have been inserted in the post start section struct rte_flow_error flush_error = { 0 }; int32_t retval = rte_flow_flush(ptv->port_id, &flush_error); diff --git a/src/util-dpdk-mlx5.c b/src/util-dpdk-mlx5.c new file mode 100644 index 000000000000..463b054d2e31 --- /dev/null +++ b/src/util-dpdk-mlx5.c @@ -0,0 +1,76 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \defgroup dpdk DPDK NVIDIA mlx5 driver helpers functions + * + * @{ + */ + +/** + * \file + * + * \author Adam Kiripolsky + * + * DPDK driver's helper functions + * + */ + +#include "util-debug.h" +#include "util-dpdk.h" +#include "util-dpdk-bonding.h" +#include "util-dpdk-mlx5.h" +#include "util-dpdk-rss.h" + +#ifdef HAVE_DPDK + +#define MLX5_RSS_HKEY_LEN 40 + +int mlx5DeviceSetRSS(int port_id, int nb_rx_queues, char *port_name) +{ + uint16_t queues[RTE_MAX_QUEUES_PER_PORT]; + struct rte_flow_error flush_error = { 0 }; + struct rte_eth_rss_conf rss_conf = { + .rss_key = RSS_HKEY, + .rss_key_len = MLX5_RSS_HKEY_LEN, + }; + + if (nb_rx_queues < 1) { + FatalError("The number of queues for RSS configuration must be " + "configured with a positive number"); + } + + struct rte_flow_action_rss rss_action_conf = + DPDKInitRSSAction(rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_TOEPLITZ, true); + + int retval = DPDKCreateRSSFlowGeneric(port_id, port_name, rss_action_conf); + if (retval != 0) { + retval = rte_flow_flush(port_id, &flush_error); + if (retval != 0) { + SCLogError("%s: unable to flush rte_flow rules: %s Flush error msg: %s", port_name, + rte_strerror(-retval), flush_error.message); + } + return retval; + } + + return 0; +} + +#endif /* HAVE_DPDK */ +/** + * @} + */ diff --git a/src/util-dpdk-mlx5.h b/src/util-dpdk-mlx5.h new file mode 100644 index 000000000000..c7b871405c01 --- /dev/null +++ b/src/util-dpdk-mlx5.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Adam Kiripolsky + */ + +#ifndef UTIL_DPDK_MLX5_H +#define UTIL_DPDK_MLX5_H + +#include "suricata-common.h" + +#ifdef HAVE_DPDK + +int mlx5DeviceSetRSS(int port_id, int nb_rx_queues, char *port_name); + +#endif /* HAVE_DPDK */ + +#endif /* UTIL_DPDK_MLX5_H */ diff --git a/src/util-dpdk-rss.c b/src/util-dpdk-rss.c index 48711e5e076f..70d49e3e6e15 100644 --- a/src/util-dpdk-rss.c +++ b/src/util-dpdk-rss.c @@ -108,7 +108,8 @@ int DPDKCreateRSSFlowGeneric( action[0].conf = &rss_conf; action[1].type = RTE_FLOW_ACTION_TYPE_END; - pattern[0].type = RTE_FLOW_ITEM_TYPE_END; + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[1].type = RTE_FLOW_ITEM_TYPE_END; struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, action, &flow_error); if (flow == NULL) {