Merge branch 'net-make-rss-rxnfc-semantics-more-explicit'

Edward Cree says:

====================
net: make RSS+RXNFC semantics more explicit

The original semantics of ntuple filters with FLOW_RSS were not
 fully understood by all drivers, some ignoring the ring_cookie from
 the flow rule.  Require this support to be explicitly declared by
 the driver for filters relying on it to be inserted, and add self-
 test coverage for this functionality.
Also teach ethtool_check_max_channel() about this.
====================

Link: https://patch.msgid.link/cover.1731499021.git.ecree.xilinx@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-11-14 19:53:43 -08:00
commit dfc1466479
6 changed files with 113 additions and 19 deletions

View File

@ -59,6 +59,7 @@ const struct ethtool_ops ef100_ethtool_ops = {
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
.rxfh_per_ctx_key = true,
.cap_rss_rxnfc_adds = true,
.rxfh_priv_size = sizeof(struct efx_rss_context_priv),
.get_rxfh = efx_ethtool_get_rxfh,
.set_rxfh = efx_ethtool_set_rxfh,

View File

@ -263,6 +263,7 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
.rxfh_per_ctx_key = true,
.cap_rss_rxnfc_adds = true,
.rxfh_priv_size = sizeof(struct efx_rss_context_priv),
.get_rxfh = efx_ethtool_get_rxfh,
.set_rxfh = efx_ethtool_set_rxfh,

View File

@ -734,6 +734,9 @@ struct kernel_ethtool_ts_info {
* @rxfh_per_ctx_key: device supports setting different RSS key for each
* additional context. Netlink API should report hfunc, key, and input_xfrm
* for every context, not just context 0.
* @cap_rss_rxnfc_adds: device supports nonzero ring_cookie in filters with
* %FLOW_RSS flag; the queue ID from the filter is added to the value from
* the indirection table to determine the delivery queue.
* @rxfh_indir_space: max size of RSS indirection tables, if indirection table
* size as returned by @get_rxfh_indir_size may change during lifetime
* of the device. Leave as 0 if the table size is constant.
@ -956,6 +959,7 @@ struct ethtool_ops {
u32 cap_rss_ctx_supported:1;
u32 cap_rss_sym_xor_supported:1;
u32 rxfh_per_ctx_key:1;
u32 cap_rss_rxnfc_adds:1;
u32 rxfh_indir_space;
u16 rxfh_key_space;
u16 rxfh_priv_size;

View File

@ -538,6 +538,20 @@ static int ethtool_get_rxnfc_rule_count(struct net_device *dev)
return info.rule_cnt;
}
/* Max offset for one RSS context */
static u32 ethtool_get_rss_ctx_max_channel(struct ethtool_rxfh_context *ctx)
{
u32 max_ring = 0;
u32 i, *tbl;
if (WARN_ON_ONCE(!ctx))
return 0;
tbl = ethtool_rxfh_context_indir(ctx);
for (i = 0; i < ctx->indir_size; i++)
max_ring = max(max_ring, tbl[i]);
return max_ring;
}
static int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
@ -574,10 +588,18 @@ static int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max)
if (rule_info.fs.ring_cookie != RX_CLS_FLOW_DISC &&
rule_info.fs.ring_cookie != RX_CLS_FLOW_WAKE &&
!(rule_info.flow_type & FLOW_RSS) &&
!ethtool_get_flow_spec_ring_vf(rule_info.fs.ring_cookie))
max_ring =
max_t(u64, max_ring, rule_info.fs.ring_cookie);
!ethtool_get_flow_spec_ring_vf(rule_info.fs.ring_cookie)) {
u64 ring = rule_info.fs.ring_cookie;
if (rule_info.flow_type & FLOW_RSS) {
struct ethtool_rxfh_context *ctx;
ctx = xa_load(&dev->ethtool->rss_ctx,
rule_info.rss_context);
ring += ethtool_get_rss_ctx_max_channel(ctx);
}
max_ring = max_t(u64, max_ring, ring);
}
}
kvfree(info);
@ -589,6 +611,7 @@ static int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max)
return err;
}
/* Max offset across all of a device's RSS contexts */
static u32 ethtool_get_max_rss_ctx_channel(struct net_device *dev)
{
struct ethtool_rxfh_context *ctx;
@ -596,13 +619,8 @@ static u32 ethtool_get_max_rss_ctx_channel(struct net_device *dev)
u32 max_ring = 0;
mutex_lock(&dev->ethtool->rss_lock);
xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
u32 i, *tbl;
tbl = ethtool_rxfh_context_indir(ctx);
for (i = 0; i < ctx->indir_size; i++)
max_ring = max(max_ring, tbl[i]);
}
xa_for_each(&dev->ethtool->rss_ctx, context, ctx)
max_ring = max(max_ring, ethtool_get_rss_ctx_max_channel(ctx));
mutex_unlock(&dev->ethtool->rss_lock);
return max_ring;
@ -611,7 +629,7 @@ static u32 ethtool_get_max_rss_ctx_channel(struct net_device *dev)
static u32 ethtool_get_max_rxfh_channel(struct net_device *dev)
{
struct ethtool_rxfh_param rxfh = {};
u32 dev_size, current_max;
u32 dev_size, current_max = 0;
int ret;
/* While we do track whether RSS context has an indirection

View File

@ -992,6 +992,11 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
if (rc)
return rc;
/* Nonzero ring with RSS only makes sense if NIC adds them together */
if (info.flow_type & FLOW_RSS && !ops->cap_rss_rxnfc_adds &&
ethtool_get_flow_spec_ring(info.fs.ring_cookie))
return -EINVAL;
if (ops->get_rxfh) {
struct ethtool_rxfh_param rxfh = {};

View File

@ -215,7 +215,7 @@ def test_rss_queue_reconfigure(cfg, main_ctx=True):
defer(ethtool, f"-X {cfg.ifname} default")
else:
other_key = 'noise'
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"
ntuple = ethtool_create(cfg, "-N", flow)
defer(ethtool, f"-N {cfg.ifname} delete {ntuple}")
@ -238,6 +238,32 @@ def test_rss_queue_reconfigure(cfg, main_ctx=True):
else:
raise Exception(f"Driver didn't prevent us from deactivating a used queue (context {ctx_id})")
if not main_ctx:
ethtool(f"-L {cfg.ifname} combined 4")
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id} action 1"
try:
# this targets queue 4, which doesn't exist
ntuple2 = ethtool_create(cfg, "-N", flow)
except CmdExitFailure:
pass
else:
raise Exception(f"Driver didn't prevent us from targeting a nonexistent queue (context {ctx_id})")
# change the table to target queues 0 and 2
ethtool(f"-X {cfg.ifname} {ctx_ref} weight 1 0 1 0")
# ntuple rule therefore targets queues 1 and 3
ntuple2 = ethtool_create(cfg, "-N", flow)
# should replace existing filter
ksft_eq(ntuple, ntuple2)
_send_traffic_check(cfg, port, ctx_ref, { 'target': (1, 3),
'noise' : (0, 2) })
# Setting queue count to 3 should fail, queue 3 is used
try:
ethtool(f"-L {cfg.ifname} combined 3")
except CmdExitFailure:
pass
else:
raise Exception(f"Driver didn't prevent us from deactivating a used queue (context {ctx_id})")
def test_rss_resize(cfg):
"""Test resizing of the RSS table.
@ -429,7 +455,7 @@ def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):
ksft_eq(max(data['rss-indirection-table']), 2 + i * 2 + 1, "Unexpected context cfg: " + str(data))
ports.append(rand_port())
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {ports[i]} context {ctx_id}"
ntuple = ethtool_create(cfg, "-N", flow)
defer(ethtool, f"-N {cfg.ifname} delete {ntuple}")
@ -516,7 +542,7 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
ctx.append(defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete"))
ports.append(rand_port())
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {ports[i]} context {ctx_id}"
ntuple_id = ethtool_create(cfg, "-N", flow)
ntuple.append(defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}"))
@ -569,7 +595,7 @@ def test_rss_context_overlap(cfg, other_ctx=0):
port = rand_port()
if other_ctx:
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {other_ctx}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {other_ctx}"
ntuple_id = ethtool_create(cfg, "-N", flow)
ntuple = defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")
@ -587,7 +613,7 @@ def test_rss_context_overlap(cfg, other_ctx=0):
# Now create a rule for context 1 and make sure traffic goes to a subset
if other_ctx:
ntuple.exec()
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"
ntuple_id = ethtool_create(cfg, "-N", flow)
defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")
@ -620,7 +646,7 @@ def test_delete_rss_context_busy(cfg):
# utilize context from ntuple filter
port = rand_port()
flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}"
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id}"
ntuple_id = ethtool_create(cfg, "-N", flow)
defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")
@ -633,6 +659,45 @@ def test_delete_rss_context_busy(cfg):
pass
def test_rss_ntuple_addition(cfg):
"""
Test that the queue offset (ring_cookie) of an ntuple rule is added
to the queue number read from the indirection table.
"""
require_ntuple(cfg)
queue_cnt = len(_get_rx_cnts(cfg))
if queue_cnt < 4:
try:
ksft_pr(f"Increasing queue count {queue_cnt} -> 4")
ethtool(f"-L {cfg.ifname} combined 4")
defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}")
except:
raise KsftSkipEx("Not enough queues for the test")
# Use queue 0 for normal traffic
ethtool(f"-X {cfg.ifname} equal 1")
defer(ethtool, f"-X {cfg.ifname} default")
# create additional rss context
ctx_id = ethtool_create(cfg, "-X", "context new equal 2")
defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")
# utilize context from ntuple filter
port = rand_port()
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port {port} context {ctx_id} action 2"
try:
ntuple_id = ethtool_create(cfg, "-N", flow)
except CmdExitFailure:
raise KsftSkipEx("Ntuple filter with RSS and nonzero action not supported")
defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")
_send_traffic_check(cfg, port, f"context {ctx_id}", { 'target': (2, 3),
'empty' : (1,),
'noise' : (0,) })
def main() -> None:
with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
cfg.ethnl = EthtoolFamily()
@ -644,7 +709,7 @@ def main() -> None:
test_rss_context_dump, test_rss_context_queue_reconfigure,
test_rss_context_overlap, test_rss_context_overlap2,
test_rss_context_out_of_order, test_rss_context4_create_with_cfg,
test_delete_rss_context_busy],
test_delete_rss_context_busy, test_rss_ntuple_addition],
args=(cfg, ))
ksft_exit()