Skip to content

Commit

Permalink
dpdk/rss: add rte_flow rss for more NIC drivers
Browse files Browse the repository at this point in the history
Configure RSS through rte_flow API for NIC drivers mlx5, ixgbe and
ice. So far only i40e supported this way of RSS configuration.
This replaces the usage of legacy RSS API. This way subsequent
rules can be added in the future with rte_flow (for exmaple
encapsulation stripping). Currently the RSS hashes symmetricaly
based on ipv4 / ipv6 addresses for all drivers.

Ticket  OISF#7337
  • Loading branch information
Adam Kiripolsky committed Nov 12, 2024
1 parent 278dc24 commit ebddcb9
Show file tree
Hide file tree
Showing 13 changed files with 531 additions and 217 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,9 @@ 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 \
util-enum.h \
util-error.h \
Expand Down Expand Up @@ -1019,7 +1021,9 @@ 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 \
util-enum.c \
util-error.c \
Expand Down
1 change: 0 additions & 1 deletion src/runmode-dpdk.c
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,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)
Expand Down
15 changes: 11 additions & 4 deletions src/source-dpdk.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -88,6 +88,9 @@ 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-mlx5.h"
#include "util-dpdk-bonding.h"
#include <numa.h>

Expand Down Expand Up @@ -194,10 +197,14 @@ static void DevicePostStartPMDSpecificActions(DPDKThreadVars *ptv, const char *d
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);
if (strcmp(driver_name, "net_ice") == 0)
iceDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev);
if (strcmp(driver_name, "net_ixgbe") == 0)
ixgbeDeviceSetRSS(ptv->port_id, ptv->threads, ptv->livedev->dev);
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)
Expand Down
203 changes: 25 additions & 178 deletions src/util-dpdk-i40e.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Open Information Security Foundation
/* Copyright (C) 2021-2024s 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
Expand Down Expand Up @@ -34,6 +34,7 @@
#include "util-dpdk.h"
#include "util-debug.h"
#include "util-dpdk-bonding.h"
#include "util-dpdk-rss.h"

#ifdef HAVE_DPDK

Expand Down Expand Up @@ -166,199 +167,54 @@ 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 } };

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);
int ret = DeviceSetRSSFlowIPv4(port_id, port_name, rss_conf);

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 } };

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);
int ret = DeviceSetRSSFlowIPv6(port_id, port_name, rss_conf);

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_flow_action_rss rss_action_conf = { 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);
int 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 (nb_rx_queues < 1) {
FatalError("The number of queues for RSS configuration must be "
"configured with a positive number");
}

rss_action_conf = DeviceInitRSSAction(
rss_conf, nb_rx_queues, queues, RTE_ETH_HASH_FUNCTION_DEFAULT, false);

retval = DeviceSetRSSFlowQueues(port_id, port_name, rss_action_conf);

rss_action_conf = DeviceInitRSSAction(
rss_conf, 0, queues, RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ, 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) {
Expand All @@ -373,18 +229,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);
Expand Down
4 changes: 2 additions & 2 deletions src/util-dpdk-i40e.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 */
Expand Down
Loading

0 comments on commit ebddcb9

Please sign in to comment.