summaryrefslogtreecommitdiffstats
path: root/src/util-dpdk-i40e.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/util-dpdk-i40e.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/src/util-dpdk-i40e.c b/src/util-dpdk-i40e.c
new file mode 100644
index 0000000..2484b45
--- /dev/null
+++ b/src/util-dpdk-i40e.c
@@ -0,0 +1,416 @@
+/* Copyright (C) 2021 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 Intel I40E driver helpers functions
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Lukas Sismis <lukas.sismis@gmail.com>
+ *
+ * DPDK driver's helper functions
+ *
+ */
+
+#include "util-dpdk-i40e.h"
+#include "util-dpdk.h"
+#include "util-debug.h"
+#include "util-dpdk-bonding.h"
+
+#ifdef HAVE_DPDK
+
+#define I40E_RSS_HKEY_LEN 52
+
+#if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0)
+static int i40eDeviceEnableSymHash(
+ int port_id, const char *port_name, uint32_t ftype, enum rte_eth_hash_function function)
+{
+ struct rte_eth_hash_filter_info info;
+ int retval;
+ uint32_t idx, offset;
+
+ memset(&info, 0, sizeof(info));
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ 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);
+ return retval;
+ }
+
+ info.info_type = RTE_ETH_HASH_FILTER_GLOBAL_CONFIG;
+ info.info.global_conf.hash_func = function;
+
+ idx = ftype / UINT64_BIT;
+ offset = ftype % UINT64_BIT;
+ info.info.global_conf.valid_bit_mask[idx] |= (1ULL << offset);
+ info.info.global_conf.sym_hash_enable_mask[idx] |= (1ULL << offset);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ retval = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
+#pragma GCC diagnostic pop
+
+ if (retval < 0) {
+ SCLogError("Cannot set global hash configurations on port %s", port_name);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int i40eDeviceSetSymHash(int port_id, const char *port_name, int enable)
+{
+ int ret;
+ struct rte_eth_hash_filter_info info;
+
+ memset(&info, 0, sizeof(info));
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_HASH);
+#pragma GCC diagnostic pop
+
+ if (ret < 0) {
+ SCLogError("RTE_ETH_FILTER_HASH not supported on port: %s", port_name);
+ return ret;
+ }
+
+ info.info_type = RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT;
+ info.info.enable = enable;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_HASH, RTE_ETH_FILTER_SET, &info);
+#pragma GCC diagnostic pop
+
+ if (ret < 0) {
+ SCLogError("Cannot set symmetric hash enable per port on port %s", port_name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i40eDeviceApplyRSSFilter(int port_id, const char *port_name)
+{
+ int retval = 0;
+
+ // Behavior of RTE_FLOW in DPDK version 19.xx and less is different than on versions
+ // above. For that reason RSS on i40e driver is set differently.
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_FRAG_IPV4, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_FRAG_IPV6, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_SCTP, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+ retval |= i40eDeviceEnableSymHash(
+ port_id, port_name, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, RTE_ETH_HASH_FUNCTION_TOEPLITZ);
+
+ retval |= i40eDeviceSetSymHash(port_id, port_name, 1);
+ return retval;
+}
+
+static int32_t i40eDeviceSetRSSWithFilter(int port_id, const char *port_name)
+{
+ int32_t ret = BondingIsBond(port_id);
+ if (ret < 0)
+ return -ret;
+
+ if (ret == 1) { // regular device
+ i40eDeviceApplyRSSFilter(port_id, port_name);
+ } else if (ret == 0) { // the device is Bond PMD
+ uint16_t bonded_devs[RTE_MAX_ETHPORTS];
+ ret = BondingMemberDevicesGet(port_id, bonded_devs, RTE_MAX_ETHPORTS);
+ for (int i = 0; i < ret; i++) {
+ i40eDeviceApplyRSSFilter(bonded_devs[i], port_name);
+ }
+ } else {
+ FatalError("Unknown return value from BondingIsBond()");
+ }
+
+ return 0;
+}
+
+#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 ret = 0;
+ struct rte_flow_item pattern[] = { { 0 }, { 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);
+ 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);
+ 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);
+ 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);
+ 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_END;
+ ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV4, pattern);
+
+ return ret;
+}
+
+static int i40eDeviceSetRSSFlowIPv6(
+ int port_id, const char *port_name, struct rte_eth_rss_conf rss_conf)
+{
+ int ret = 0;
+ struct rte_flow_item pattern[] = { { 0 }, { 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);
+ 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);
+ 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);
+ 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);
+ 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_END;
+ ret |= i40eDeviceCreateRSSFlow(port_id, port_name, rss_conf, RTE_ETH_RSS_FRAG_IPV6, pattern);
+
+ 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];
+ struct rte_flow_error flush_error = { 0 };
+ struct rte_eth_rss_conf rss_conf = {
+ .rss_key = rss_key,
+ .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;
+ }
+
+ 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);
+ 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,
+ rte_strerror(-retval), flush_error.message);
+ }
+ return retval;
+ }
+
+ return 0;
+}
+
+#endif /* RTE_VERSION < RTE_VERSION_NUM(20,0,0,0) */
+
+int i40eDeviceSetRSS(int port_id, int nb_rx_queues)
+{
+ 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);
+#else
+ i40eDeviceSetRSSWithFilter(port_id, port_name);
+#endif
+ return 0;
+}
+
+void i40eDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf)
+{
+#if RTE_VERSION >= RTE_VERSION_NUM(20, 0, 0, 0)
+ rss_conf->rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER |
+ RTE_ETH_RSS_FRAG_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_OTHER;
+ rss_conf->rss_key = NULL;
+ rss_conf->rss_key_len = 0;
+#else
+ rss_conf->rss_hf =
+ RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_TCP | RTE_ETH_RSS_NONFRAG_IPV4_UDP |
+ RTE_ETH_RSS_NONFRAG_IPV4_SCTP | RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 |
+ RTE_ETH_RSS_NONFRAG_IPV6_TCP | RTE_ETH_RSS_NONFRAG_IPV6_UDP |
+ RTE_ETH_RSS_NONFRAG_IPV6_SCTP | RTE_ETH_RSS_NONFRAG_IPV6_OTHER | RTE_ETH_RSS_SCTP;
+#endif
+}
+
+#endif /* HAVE_DPDK */
+/**
+ * @}
+ */