mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
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:
commit
dfc1466479
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 = {};
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user