* [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink
@ 2025-07-14 22:27 Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling) Jakub Kicinski
` (10 more replies)
0 siblings, 11 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Support configuring RSS settings via Netlink.
Creating and removing contexts remains for the following series.
v2:
- commit message changes
- make sure driver implements the set_rxfh op
- add comment about early return when lacking get
- set IFF_RXFH_CONFIGURED even if user sets the table to identical
to default
- use ethnl_update_binary()
- make sure we free indir if key parsing fails
- tests: fix existing rxfh_input_xfrm test for string decode
- tests: make defer() cleanup more intelligent WRT ordering
v1: https://lore.kernel.org/20250711015303.3688717-1-kuba@kernel.org
Jakub Kicinski (11):
ethtool: rss: initial RSS_SET (indirection table handling)
selftests: drv-net: rss_api: factor out checking min queue count
tools: ynl: support packing binary arrays of scalars
selftests: drv-net: rss_api: test setting indirection table via
Netlink
ethtool: rss: support setting hfunc via Netlink
ethtool: rss: support setting hkey via Netlink
selftests: drv-net: rss_api: test setting hashing key via Netlink
netlink: specs: define input-xfrm enum in the spec
ethtool: rss: support setting input-xfrm via Netlink
ethtool: rss: support setting flow hashing fields
selftests: drv-net: rss_api: test input-xfrm and hash fields
Documentation/netlink/specs/ethtool.yaml | 39 ++
Documentation/networking/ethtool-netlink.rst | 29 +-
.../uapi/linux/ethtool_netlink_generated.h | 1 +
net/ethtool/common.h | 1 +
net/ethtool/netlink.h | 1 +
net/ethtool/common.c | 15 +
net/ethtool/ioctl.c | 4 +-
net/ethtool/netlink.c | 8 +
net/ethtool/rss.c | 389 ++++++++++++++++++
tools/net/ynl/pyynl/lib/ynl.py | 7 +-
.../selftests/drivers/net/hw/rss_api.py | 289 ++++++++++++-
.../drivers/net/hw/rss_input_xfrm.py | 2 +-
12 files changed, 767 insertions(+), 18 deletions(-)
--
2.50.1
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling)
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-15 7:20 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 02/11] selftests: drv-net: rss_api: factor out checking min queue count Jakub Kicinski
` (9 subsequent siblings)
10 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Add initial support for RSS_SET, for now only operations on
the indirection table are supported.
Unlike the ioctl don't check if at least one parameter is
being changed. This is how other ethtool-nl ops behave,
so pick the ethtool-nl consistency vs copying ioctl behavior.
There are two special cases here:
1) resetting the table to defaults;
2) support for tables of different size.
For (1) I use an empty Netlink attribute (array of size 0).
(2) may require some background. AFAICT a lot of modern devices
allow allocating RSS tables of different sizes. mlx5 can upsize
its tables, bnxt has some "table size calculation", and Intel
folks asked about RSS table sizing in context of resource allocation
in the past. The ethtool IOCTL API has a concept of table size,
but right now the user is expected to provide a table exactly
the size the device requests. Some drivers may change the table
size at runtime (in response to queue count changes) but the
user is not in control of this. What's not great is that all
RSS contexts share the same table size. For example a device
with 128 queues enabled, 16 RSS contexts 8 queues in each will
likely have 256 entry tables for each of the 16 contexts,
while 32 would be more than enough given each context only has
8 queues. To address this the Netlink API should avoid enforcing
table size at the uAPI level, and should allow the user to express
the min table size they expect.
To fully solve (2) we will need more driver plumbing but
at the uAPI level this patch allows the user to specify
a table size smaller than what the device advertises. The device
table size must be a multiple of the user requested table size.
We then replicate the user-provided table to fill the full device
size table. This addresses the "allow the user to express the min
table size" objective, while not enforcing any fixed size.
From Netlink perspective .get_rxfh_indir_size() is now de facto
the "max" table size supported by the device.
We may choose to support table replication in ethtool, too,
when we actually plumb this thru the device APIs.
Initially I was considering moving full pattern generation
to the kernel (which queues to use, at which frequency and
what min sequence length). I don't think this complexity
would buy us much and most if not all devices have pow-2
table sizes, which simplifies the replication a lot.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- make sure driver implements the set_rxfh op
- add comment about early return when lacking get
- explain why we accept empty request in commit msg
- set IFF_RXFH_CONFIGURED even if user sets the table to identical
to default
---
Documentation/netlink/specs/ethtool.yaml | 12 ++
Documentation/networking/ethtool-netlink.rst | 26 ++-
.../uapi/linux/ethtool_netlink_generated.h | 1 +
net/ethtool/netlink.h | 1 +
net/ethtool/netlink.c | 8 +
| 195 ++++++++++++++++++
6 files changed, 242 insertions(+), 1 deletion(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index c38c03c624f0..1eca88a508a0 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -2643,6 +2643,18 @@ c-version-name: ethtool-genl-version
attributes:
- header
- events
+ -
+ name: rss-set
+ doc: Set RSS params.
+
+ attribute-set: rss
+
+ do:
+ request:
+ attributes:
+ - header
+ - context
+ - indir
-
name: rss-ntf
doc: |
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 248bc3d93da9..27db7540e60e 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -239,6 +239,7 @@ All constants identifying message types use ``ETHTOOL_CMD_`` prefix and suffix
``ETHTOOL_MSG_PHY_GET`` get Ethernet PHY information
``ETHTOOL_MSG_TSCONFIG_GET`` get hw timestamping configuration
``ETHTOOL_MSG_TSCONFIG_SET`` set hw timestamping configuration
+ ``ETHTOOL_MSG_RSS_SET`` set RSS settings
===================================== =================================
Kernel to userspace:
@@ -292,6 +293,7 @@ All constants identifying message types use ``ETHTOOL_CMD_`` prefix and suffix
``ETHTOOL_MSG_TSCONFIG_GET_REPLY`` hw timestamping configuration
``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration
``ETHTOOL_MSG_PSE_NTF`` PSE events notification
+ ``ETHTOOL_MSG_RSS_NTF`` RSS settings notification
======================================== =================================
``GET`` requests are sent by userspace applications to retrieve device
@@ -1989,6 +1991,28 @@ hfunc. Current supported options are symmetric-xor and symmetric-or-xor.
ETHTOOL_A_RSS_FLOW_HASH carries per-flow type bitmask of which header
fields are included in the hash calculation.
+RSS_SET
+=======
+
+Request contents:
+
+===================================== ====== ==============================
+ ``ETHTOOL_A_RSS_HEADER`` nested request header
+ ``ETHTOOL_A_RSS_CONTEXT`` u32 context number
+ ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
+===================================== ====== ==============================
+
+``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and
+the device driver may replicate the table if its smaller than smallest table
+size supported by the device. For example if user requests ``[0, 1]`` but the
+device needs at least 8 entries - the real table in use will end up being
+``[0, 1, 0, 1, 0, 1, 0, 1]``. Most devices require the table size to be power
+of 2, so tables which size is not a power of 2 will likely be rejected.
+Using table of size 0 will reset the indirection table to the default.
+
+Note that, at present, only a subset of RSS configuration can be accomplished
+over Netlink.
+
PLCA_GET_CFG
============
@@ -2455,7 +2479,7 @@ are netlink only.
``ETHTOOL_GRXNTUPLE`` n/a
``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET``
``ETHTOOL_GRXFHINDIR`` ``ETHTOOL_MSG_RSS_GET``
- ``ETHTOOL_SRXFHINDIR`` n/a
+ ``ETHTOOL_SRXFHINDIR`` ``ETHTOOL_MSG_RSS_SET``
``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET``
``ETHTOOL_GCHANNELS`` ``ETHTOOL_MSG_CHANNELS_GET``
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 96027e26ffba..130bdf5c3516 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -840,6 +840,7 @@ enum {
ETHTOOL_MSG_PHY_GET,
ETHTOOL_MSG_TSCONFIG_GET,
ETHTOOL_MSG_TSCONFIG_SET,
+ ETHTOOL_MSG_RSS_SET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 94a7eb402022..620dd1ab9b3b 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -484,6 +484,7 @@ extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MO
extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1];
extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1];
extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_START_CONTEXT + 1];
+extern const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1];
extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1];
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index b1f8999c1adc..0ae0d7a9667c 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -405,6 +405,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_PSE_GET] = ðnl_pse_request_ops,
[ETHTOOL_MSG_PSE_SET] = ðnl_pse_request_ops,
[ETHTOOL_MSG_RSS_GET] = ðnl_rss_request_ops,
+ [ETHTOOL_MSG_RSS_SET] = ðnl_rss_request_ops,
[ETHTOOL_MSG_PLCA_GET_CFG] = ðnl_plca_cfg_request_ops,
[ETHTOOL_MSG_PLCA_SET_CFG] = ðnl_plca_cfg_request_ops,
[ETHTOOL_MSG_PLCA_GET_STATUS] = ðnl_plca_status_request_ops,
@@ -1504,6 +1505,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_tsconfig_set_policy,
.maxattr = ARRAY_SIZE(ethnl_tsconfig_set_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_RSS_SET,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = ethnl_default_set_doit,
+ .policy = ethnl_rss_set_policy,
+ .maxattr = ARRAY_SIZE(ethnl_rss_set_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
--git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 41ab9fc67652..989cdb93a6e8 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -218,6 +218,10 @@ rss_prepare(const struct rss_req_info *request, struct net_device *dev,
{
rss_prepare_flow_hash(request, dev, data, info);
+ /* Coming from RSS_SET, driver may only have flow_hash_fields ops */
+ if (!dev->ethtool_ops->get_rxfh)
+ return 0;
+
if (request->rss_context)
return rss_prepare_ctx(request, dev, data, info);
return rss_prepare_get(request, dev, data, info);
@@ -466,6 +470,193 @@ void ethtool_rss_notify(struct net_device *dev, u32 rss_context)
ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base);
}
+/* RSS_SET */
+
+const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] = {
+ [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
+ [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
+};
+
+static int
+ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
+{
+ const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
+ struct rss_req_info *request = RSS_REQINFO(req_info);
+ struct nlattr **tb = info->attrs;
+ struct nlattr *bad_attr = NULL;
+
+ if (request->rss_context && !ops->create_rxfh_context)
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
+
+ if (bad_attr) {
+ NL_SET_BAD_ATTR(info->extack, bad_attr);
+ return -EOPNOTSUPP;
+ }
+
+ return 1;
+}
+
+static int
+rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
+ struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
+ bool *reset, bool *mod)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct netlink_ext_ack *extack = info->extack;
+ struct nlattr **tb = info->attrs;
+ struct ethtool_rxnfc rx_rings;
+ size_t alloc_size;
+ u32 user_size;
+ int i, err;
+
+ if (!tb[ETHTOOL_A_RSS_INDIR])
+ return 0;
+ if (!data->indir_size)
+ return -EOPNOTSUPP;
+
+ rx_rings.cmd = ETHTOOL_GRXRINGS;
+ err = ops->get_rxnfc(dev, &rx_rings, NULL);
+ if (err)
+ return err;
+
+ if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) {
+ NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]);
+ return -EINVAL;
+ }
+ user_size = nla_len(tb[ETHTOOL_A_RSS_INDIR]) / 4;
+ if (!user_size) {
+ if (rxfh->rss_context) {
+ NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_RSS_INDIR],
+ "can't reset table for a context");
+ return -EINVAL;
+ }
+ *reset = true;
+ } else if (data->indir_size % user_size) {
+ NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
+ "size (%d) mismatch with device indir table (%d)",
+ user_size, data->indir_size);
+ return -EINVAL;
+ }
+
+ rxfh->indir_size = data->indir_size;
+ alloc_size = array_size(data->indir_size, sizeof(rxfh->indir[0]));
+ rxfh->indir = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rxfh->indir)
+ return -ENOMEM;
+
+ nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size);
+ for (i = 0; i < user_size; i++) {
+ if (rxfh->indir[i] < rx_rings.data)
+ continue;
+
+ NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
+ "entry %d: queue out of range (%d)",
+ i, rxfh->indir[i]);
+ err = -EINVAL;
+ goto err_free;
+ }
+
+ if (user_size) {
+ /* Replicate the user-provided table to fill the device table */
+ for (i = user_size; i < data->indir_size; i++)
+ rxfh->indir[i] = rxfh->indir[i % user_size];
+ } else {
+ for (i = 0; i < data->indir_size; i++)
+ rxfh->indir[i] =
+ ethtool_rxfh_indir_default(i, rx_rings.data);
+ }
+
+ *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
+
+ return 0;
+
+err_free:
+ kfree(rxfh->indir);
+ rxfh->indir = NULL;
+ return err;
+}
+
+static void
+rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
+ struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
+{
+ int i;
+
+ if (rxfh->indir) {
+ for (i = 0; i < data->indir_size; i++)
+ ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i];
+ ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]);
+ }
+}
+
+static int
+ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
+{
+ struct rss_req_info *request = RSS_REQINFO(req_info);
+ struct ethtool_rxfh_context *ctx = NULL;
+ struct net_device *dev = req_info->dev;
+ struct ethtool_rxfh_param rxfh = {};
+ bool indir_reset = false, indir_mod;
+ struct nlattr **tb = info->attrs;
+ struct rss_reply_data data = {};
+ const struct ethtool_ops *ops;
+ bool mod = false;
+ int ret;
+
+ ops = dev->ethtool_ops;
+ data.base.dev = dev;
+
+ ret = rss_prepare(request, dev, &data, info);
+ if (ret)
+ return ret;
+
+ rxfh.rss_context = request->rss_context;
+
+ ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod);
+ if (ret)
+ goto exit_clean_data;
+ indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
+
+ rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
+ rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
+
+ mutex_lock(&dev->ethtool->rss_lock);
+ if (request->rss_context) {
+ ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
+ if (!ctx) {
+ ret = -ENOENT;
+ goto exit_unlock;
+ }
+ }
+
+ if (!mod)
+ ret = 0; /* nothing to tell the driver */
+ else if (!ops->set_rxfh)
+ ret = -EOPNOTSUPP;
+ else if (!rxfh.rss_context)
+ ret = ops->set_rxfh(dev, &rxfh, info->extack);
+ else
+ ret = ops->modify_rxfh_context(dev, ctx, &rxfh, info->extack);
+ if (ret)
+ goto exit_unlock;
+
+ if (ctx)
+ rss_set_ctx_update(ctx, tb, &data, &rxfh);
+ else if (indir_reset)
+ dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+ else if (indir_mod)
+ dev->priv_flags |= IFF_RXFH_CONFIGURED;
+
+exit_unlock:
+ mutex_unlock(&dev->ethtool->rss_lock);
+ kfree(rxfh.indir);
+exit_clean_data:
+ rss_cleanup_data(&data.base);
+
+ return ret ?: mod;
+}
+
const struct ethnl_request_ops ethnl_rss_request_ops = {
.request_cmd = ETHTOOL_MSG_RSS_GET,
.reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY,
@@ -478,4 +669,8 @@ const struct ethnl_request_ops ethnl_rss_request_ops = {
.reply_size = rss_reply_size,
.fill_reply = rss_fill_reply,
.cleanup_data = rss_cleanup_data,
+
+ .set_validate = ethnl_rss_set_validate,
+ .set = ethnl_rss_set,
+ .set_ntf_cmd = ETHTOOL_MSG_RSS_NTF,
};
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 02/11] selftests: drv-net: rss_api: factor out checking min queue count
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling) Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 03/11] tools: ynl: support packing binary arrays of scalars Jakub Kicinski
` (8 subsequent siblings)
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Multiple tests check min queue count, create a helper.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
| 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
--git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py
index 6ae908bed1a4..2c76fbdb2617 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_api.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_api.py
@@ -13,6 +13,13 @@ from lib.py import EthtoolFamily
from lib.py import NetDrvEnv
+def _require_2qs(cfg):
+ qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*"))
+ if qcnt < 2:
+ raise KsftSkipEx(f"Local has only {qcnt} queues")
+ return qcnt
+
+
def _ethtool_create(cfg, act, opts):
output = ethtool(f"{act} {cfg.ifname} {opts}").stdout
# Output will be something like: "New RSS context is 1" or
@@ -57,10 +64,7 @@ from lib.py import NetDrvEnv
Check that Netlink notifications are generated when RSS indirection
table was modified.
"""
-
- qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*"))
- if qcnt < 2:
- raise KsftSkipEx(f"Local has only {qcnt} queues")
+ _require_2qs(cfg)
ethnl = EthtoolFamily()
ethnl.ntf_subscribe("monitor")
@@ -88,10 +92,7 @@ from lib.py import NetDrvEnv
Check that Netlink notifications are generated when RSS indirection
table was modified on an additional RSS context.
"""
-
- qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*"))
- if qcnt < 2:
- raise KsftSkipEx(f"Local has only {qcnt} queues")
+ _require_2qs(cfg)
ctx_id = _ethtool_create(cfg, "-X", "context new")
defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 03/11] tools: ynl: support packing binary arrays of scalars
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling) Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 02/11] selftests: drv-net: rss_api: factor out checking min queue count Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 04/11] selftests: drv-net: rss_api: test setting indirection table via Netlink Jakub Kicinski
` (7 subsequent siblings)
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
We support decoding a binary type with a scalar subtype already,
add support for sending such arrays to the kernel. While at it
also support using "None" to indicate that the binary attribute
should be empty. I couldn't decide whether empty binary should
be [] or None, but there should be no harm in supporting both.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: donald.hunter@gmail.com
---
tools/net/ynl/pyynl/lib/ynl.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py
index 006b359294a4..8244a5f440b2 100644
--- a/tools/net/ynl/pyynl/lib/ynl.py
+++ b/tools/net/ynl/pyynl/lib/ynl.py
@@ -575,7 +575,9 @@ genl_family_name_to_id = None
elif attr["type"] == 'string':
attr_payload = str(value).encode('ascii') + b'\x00'
elif attr["type"] == 'binary':
- if isinstance(value, bytes):
+ if value is None:
+ attr_payload = b''
+ elif isinstance(value, bytes):
attr_payload = value
elif isinstance(value, str):
if attr.display_hint:
@@ -584,6 +586,9 @@ genl_family_name_to_id = None
attr_payload = bytes.fromhex(value)
elif isinstance(value, dict) and attr.struct_name:
attr_payload = self._encode_struct(attr.struct_name, value)
+ elif isinstance(value, list) and attr.sub_type in NlAttr.type_formats:
+ format = NlAttr.get_format(attr.sub_type)
+ attr_payload = b''.join([format.pack(x) for x in value])
else:
raise Exception(f'Unknown type for binary attribute, value: {value}')
elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar:
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 04/11] selftests: drv-net: rss_api: test setting indirection table via Netlink
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (2 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 03/11] tools: ynl: support packing binary arrays of scalars Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc " Jakub Kicinski
` (6 subsequent siblings)
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Test setting indirection table via Netlink.
# ./tools/testing/selftests/drivers/net/hw/rss_api.py
TAP version 13
1..6
ok 1 rss_api.test_rxfh_nl_set_fail
ok 2 rss_api.test_rxfh_nl_set_indir
ok 3 rss_api.test_rxfh_nl_set_indir_ctx
ok 4 rss_api.test_rxfh_indir_ntf
ok 5 rss_api.test_rxfh_indir_ctx_ntf
ok 6 rss_api.test_rxfh_fields
# Totals: pass:6 fail:0 xfail:0 xpass:0 skip:0 error:0
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- test set on lo which doesn't have any ops (expected to fail)
---
| 96 ++++++++++++++++++-
1 file changed, 93 insertions(+), 3 deletions(-)
--git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py
index 2c76fbdb2617..07079da6a311 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_api.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_api.py
@@ -6,10 +6,10 @@ API level tests for RSS (mostly Netlink vs IOCTL).
"""
import glob
-from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises
from lib.py import KsftSkipEx, KsftFailEx
-from lib.py import defer, ethtool
-from lib.py import EthtoolFamily
+from lib.py import defer, ethtool, CmdExitFailure
+from lib.py import EthtoolFamily, NlError
from lib.py import NetDrvEnv
@@ -59,6 +59,95 @@ from lib.py import NetDrvEnv
return ret
+def test_rxfh_nl_set_fail(cfg):
+ """
+ Test error path of Netlink SET.
+ """
+ _require_2qs(cfg)
+
+ ethnl = EthtoolFamily()
+ ethnl.ntf_subscribe("monitor")
+
+ with ksft_raises(NlError):
+ ethnl.rss_set({"header": {"dev-name": "lo"},
+ "indir": None})
+
+ with ksft_raises(NlError):
+ ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "indir": [100000]})
+ ntf = next(ethnl.poll_ntf(duration=0.2), None)
+ ksft_is(ntf, None)
+
+
+def test_rxfh_nl_set_indir(cfg):
+ """
+ Test setting indirection table via Netlink.
+ """
+ qcnt = _require_2qs(cfg)
+
+ # Test some SETs with a value
+ reset = defer(cfg.ethnl.rss_set,
+ {"header": {"dev-index": cfg.ifindex}, "indir": None})
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "indir": [1]})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(set(rss.get("indir", [-1])), {1})
+
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "indir": [0, 1]})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(set(rss.get("indir", [-1])), {0, 1})
+
+ # Make sure we can't set the queue count below max queue used
+ with ksft_raises(CmdExitFailure):
+ ethtool(f"-L {cfg.ifname} combined 0 rx 1")
+ with ksft_raises(CmdExitFailure):
+ ethtool(f"-L {cfg.ifname} combined 1 rx 0")
+
+ # Test reset back to default
+ reset.exec()
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(set(rss.get("indir", [-1])), set(range(qcnt)))
+
+
+def test_rxfh_nl_set_indir_ctx(cfg):
+ """
+ Test setting indirection table via Netlink.
+ """
+ _require_2qs(cfg)
+
+ # Get setting for ctx 0, we'll make sure they don't get clobbered
+ dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+
+ # Create context
+ ctx_id = _ethtool_create(cfg, "-X", "context new")
+ defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")
+
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "context": ctx_id, "indir": [1]})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex},
+ "context": ctx_id})
+ ksft_eq(set(rss.get("indir", [-1])), {1})
+
+ ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(ctx0, dflt)
+
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "context": ctx_id, "indir": [0, 1]})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex},
+ "context": ctx_id})
+ ksft_eq(set(rss.get("indir", [-1])), {0, 1})
+
+ ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(ctx0, dflt)
+
+ # Make sure we can't set the queue count below max queue used
+ with ksft_raises(CmdExitFailure):
+ ethtool(f"-L {cfg.ifname} combined 0 rx 1")
+ with ksft_raises(CmdExitFailure):
+ ethtool(f"-L {cfg.ifname} combined 1 rx 0")
+
+
def test_rxfh_indir_ntf(cfg):
"""
Check that Netlink notifications are generated when RSS indirection
@@ -129,6 +218,7 @@ from lib.py import NetDrvEnv
""" Ksft boiler plate main """
with NetDrvEnv(__file__, nsim_test=False) as cfg:
+ cfg.ethnl = EthtoolFamily()
ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, ))
ksft_exit()
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc via Netlink
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (3 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 04/11] selftests: drv-net: rss_api: test setting indirection table via Netlink Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-15 7:24 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 06/11] ethtool: rss: support setting hkey " Jakub Kicinski
` (5 subsequent siblings)
10 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Support setting RSS hash function / algo via ethtool Netlink.
Like IOCTL we don't validate that the function is within the
range known to the kernel. The drivers do a pretty good job
validating the inputs, and the IDs are technically "dynamically
queried" rather than part of uAPI.
Only change should be that in Netlink we don't support user
explicitly passing ETH_RSS_HASH_NO_CHANGE (0), if no change
is requested the attribute should be absent.
The ETH_RSS_HASH_NO_CHANGE is retained in driver-facing
API for consistency (not that I see a strong reason for it).
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
Documentation/netlink/specs/ethtool.yaml | 1 +
Documentation/networking/ethtool-netlink.rst | 1 +
| 12 +++++++++++-
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 1eca88a508a0..0d02d8342e4c 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -2654,6 +2654,7 @@ c-version-name: ethtool-genl-version
attributes:
- header
- context
+ - hfunc
- indir
-
name: rss-ntf
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 27db7540e60e..f6e4439caa94 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1999,6 +1999,7 @@ RSS_SET
===================================== ====== ==============================
``ETHTOOL_A_RSS_HEADER`` nested request header
``ETHTOOL_A_RSS_CONTEXT`` u32 context number
+ ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func
``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
===================================== ====== ==============================
--git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 989cdb93a6e8..03e42aac8c36 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -475,6 +475,7 @@ void ethtool_rss_notify(struct net_device *dev, u32 rss_context)
const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] = {
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
+ [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
};
@@ -489,6 +490,9 @@ ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
if (request->rss_context && !ops->create_rxfh_context)
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
+ if (request->rss_context && !ops->rxfh_per_ctx_key)
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC];
+
if (bad_attr) {
NL_SET_BAD_ATTR(info->extack, bad_attr);
return -EOPNOTSUPP;
@@ -588,6 +592,8 @@ rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i];
ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]);
}
+ if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE)
+ ctx->hfunc = rxfh->hfunc;
}
static int
@@ -618,7 +624,11 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
goto exit_clean_data;
indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
- rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
+ rxfh.hfunc = data.hfunc;
+ ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+ if (rxfh.hfunc == data.hfunc)
+ rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
+
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
mutex_lock(&dev->ethtool->rss_lock);
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 06/11] ethtool: rss: support setting hkey via Netlink
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (4 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc " Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-15 7:30 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 07/11] selftests: drv-net: rss_api: test setting hashing key " Jakub Kicinski
` (4 subsequent siblings)
10 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Support setting RSS hashing key via ethtool Netlink.
Use the Netlink policy to make sure user doesn't pass
an empty key, "resetting" the key is not a thing.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- use ethnl_update_binary()
- make sure we free indir if key parsing fails
---
Documentation/netlink/specs/ethtool.yaml | 1 +
Documentation/networking/ethtool-netlink.rst | 1 +
| 41 +++++++++++++++++++-
3 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 0d02d8342e4c..aa55fc9068e1 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -2656,6 +2656,7 @@ c-version-name: ethtool-genl-version
- context
- hfunc
- indir
+ - hkey
-
name: rss-ntf
doc: |
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index f6e4439caa94..1830354495ae 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -2001,6 +2001,7 @@ RSS_SET
``ETHTOOL_A_RSS_CONTEXT`` u32 context number
``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func
``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
+ ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes
===================================== ====== ==============================
``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and
--git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 03e42aac8c36..416ad428e61e 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -477,6 +477,7 @@ const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] =
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
+ [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
};
static int
@@ -490,8 +491,10 @@ ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
if (request->rss_context && !ops->create_rxfh_context)
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
- if (request->rss_context && !ops->rxfh_per_ctx_key)
+ if (request->rss_context && !ops->rxfh_per_ctx_key) {
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC];
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY];
+ }
if (bad_attr) {
NL_SET_BAD_ATTR(info->extack, bad_attr);
@@ -581,6 +584,31 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
return err;
}
+static int
+rss_set_prep_hkey(struct net_device *dev, struct genl_info *info,
+ struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
+ bool *mod)
+{
+ struct nlattr **tb = info->attrs;
+
+ if (!tb[ETHTOOL_A_RSS_HKEY])
+ return 0;
+
+ if (nla_len(tb[ETHTOOL_A_RSS_HKEY]) != data->hkey_size) {
+ NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_HKEY]);
+ return -EINVAL;
+ }
+
+ rxfh->key_size = data->hkey_size;
+ rxfh->key = kmemdup(data->hkey, data->hkey_size, GFP_KERNEL);
+ if (!rxfh->key)
+ return -ENOMEM;
+
+ ethnl_update_binary(rxfh->key, rxfh->key_size, tb[ETHTOOL_A_RSS_HKEY],
+ mod);
+ return 0;
+}
+
static void
rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
@@ -592,6 +620,11 @@ rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i];
ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]);
}
+ if (rxfh->key) {
+ memcpy(ethtool_rxfh_context_key(ctx), rxfh->key,
+ data->hkey_size);
+ ctx->key_configured = !!rxfh->key_size;
+ }
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE)
ctx->hfunc = rxfh->hfunc;
}
@@ -629,6 +662,10 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
if (rxfh.hfunc == data.hfunc)
rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
+ ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
+ if (ret)
+ goto exit_free_indir;
+
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
mutex_lock(&dev->ethtool->rss_lock);
@@ -660,6 +697,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
exit_unlock:
mutex_unlock(&dev->ethtool->rss_lock);
+ kfree(rxfh.key);
+exit_free_indir:
kfree(rxfh.indir);
exit_clean_data:
rss_cleanup_data(&data.base);
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 07/11] selftests: drv-net: rss_api: test setting hashing key via Netlink
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (5 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 06/11] ethtool: rss: support setting hkey " Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec Jakub Kicinski
` (3 subsequent siblings)
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Test setting hashing key via Netlink.
# ./tools/testing/selftests/drivers/net/hw/rss_api.py
TAP version 13
1..7
ok 1 rss_api.test_rxfh_nl_set_fail
ok 2 rss_api.test_rxfh_nl_set_indir
ok 3 rss_api.test_rxfh_nl_set_indir_ctx
ok 4 rss_api.test_rxfh_indir_ntf
ok 5 rss_api.test_rxfh_indir_ctx_ntf
ok 6 rss_api.test_rxfh_nl_set_key
ok 7 rss_api.test_rxfh_fields
# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
| 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
--git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py
index 07079da6a311..4de566edb313 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_api.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_api.py
@@ -6,6 +6,7 @@ API level tests for RSS (mostly Netlink vs IOCTL).
"""
import glob
+import random
from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises
from lib.py import KsftSkipEx, KsftFailEx
from lib.py import defer, ethtool, CmdExitFailure
@@ -199,6 +200,38 @@ from lib.py import NetDrvEnv
ksft_eq(set(ntf["msg"]["indir"]), {1})
+def test_rxfh_nl_set_key(cfg):
+ """
+ Test setting hashing key via Netlink.
+ """
+
+ dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ defer(cfg.ethnl.rss_set,
+ {"header": {"dev-index": cfg.ifindex},
+ "hkey": dflt["hkey"], "indir": None})
+
+ # Empty key should error out
+ with ksft_raises(NlError) as cm:
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "hkey": None})
+ ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.hkey')
+
+ # Set key to random
+ mod = random.randbytes(len(dflt["hkey"]))
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "hkey": mod})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(rss.get("hkey", [-1]), mod)
+
+ # Set key to random and indir tbl to something at once
+ mod = random.randbytes(len(dflt["hkey"]))
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "indir": [0, 1], "hkey": mod})
+ rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(rss.get("hkey", [-1]), mod)
+ ksft_eq(set(rss.get("indir", [-1])), {0, 1})
+
+
def test_rxfh_fields(cfg):
"""
Test reading Rx Flow Hash over Netlink.
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (6 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 07/11] selftests: drv-net: rss_api: test setting hashing key " Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-15 8:35 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink Jakub Kicinski
` (2 subsequent siblings)
10 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Help YNL decode the values for input-xfrm by defining
the possible values in the spec. Don't define "no change"
as it's an IOCTL artifact with no use in Netlink.
With this change on mlx5 input-xfrm gets decoded:
# ynl --family ethtool --dump rss-get
[{'header': {'dev-index': 2, 'dev-name': 'eth0'},
'hfunc': 1,
'hkey': b'V\xa8\xf9\x9 ...',
'indir': [0, 1, ... ],
'input-xfrm': 'sym-or-xor', <<<
'flow-hash': {'ah4': {'ip-dst', 'ip-src'},
'ah6': {'ip-dst', 'ip-src'},
'esp4': {'ip-dst', 'ip-src'},
'esp6': {'ip-dst', 'ip-src'},
'ip4': {'ip-dst', 'ip-src'},
'ip6': {'ip-dst', 'ip-src'},
'tcp4': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
'tcp6': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
'udp4': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
'udp6': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'}}
}]
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- adjust the test to decoded values
---
Documentation/netlink/specs/ethtool.yaml | 23 +++++++++++++++++++
| 2 +-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index aa55fc9068e1..3a0453a92300 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -158,6 +158,28 @@ c-version-name: ethtool-genl-version
-
name: pse-event-sw-pw-control-error
doc: PSE faced an error managing the power control from software
+ -
+ name: input-xfrm
+ doc: RSS hash function transformations.
+ type: enum
+ enum-name:
+ name-prefix: rxh-xfrm-
+ header: linux/ethtool.h
+ entries:
+ -
+ name: sym-xor
+ value: 1
+ doc: >-
+ XOR the corresponding source and destination fields of each specified
+ protocol. Both copies of the XOR'ed fields are fed into the RSS and
+ RXHASH calculation. Note that this XORing reduces the input set
+ entropy and could be exploited to reduce the RSS queue spread.
+ -
+ name: sym-or-xor
+ value: 2
+ doc: >-
+ Similar to SYM_XOR, except that one copy of the XOR'ed fields is
+ replaced by an OR of the same fields.
-
name: rxfh-fields
name-prefix: rxh-
@@ -1621,6 +1643,7 @@ c-version-name: ethtool-genl-version
-
name: input-xfrm
type: u32
+ enum: input-xfrm
-
name: start-context
type: u32
--git a/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
index 648ff50bc1c3..d647a66fa30e 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
@@ -41,7 +41,7 @@ from lib.py import rand_port
{'header': {'dev-name': cfg.ifname}}).get('input-xfrm')
# Check for symmetric xor/or-xor
- if not input_xfrm or (input_xfrm != 1 and input_xfrm != 2):
+ if not input_xfrm or 'sym' not in input_xfrm:
raise KsftSkipEx("Symmetric RSS hash not requested")
cpus = set()
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (7 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-15 8:40 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 10/11] ethtool: rss: support setting flow hashing fields Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 11/11] selftests: drv-net: rss_api: test input-xfrm and hash fields Jakub Kicinski
10 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Support configuring symmetric hashing via Netlink.
We have the flow field config prepared as part of SET handling,
so scan it for conflicts instead of querying the driver again.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- improve comment on xfrm_sym calculation
---
Documentation/netlink/specs/ethtool.yaml | 1 +
Documentation/networking/ethtool-netlink.rst | 4 +-
net/ethtool/common.h | 1 +
net/ethtool/common.c | 15 ++++++
net/ethtool/ioctl.c | 4 +-
| 54 +++++++++++++++++++-
6 files changed, 71 insertions(+), 8 deletions(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 3a0453a92300..15cc3f2184eb 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -2680,6 +2680,7 @@ c-version-name: ethtool-genl-version
- hfunc
- indir
- hkey
+ - input-xfrm
-
name: rss-ntf
doc: |
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 1830354495ae..2214d2ce346a 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -2002,6 +2002,7 @@ RSS_SET
``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func
``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes
+ ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation
===================================== ====== ==============================
``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and
@@ -2012,9 +2013,6 @@ device needs at least 8 entries - the real table in use will end up being
of 2, so tables which size is not a power of 2 will likely be rejected.
Using table of size 0 will reset the indirection table to the default.
-Note that, at present, only a subset of RSS configuration can be accomplished
-over Netlink.
-
PLCA_GET_CFG
============
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index c41db1595621..b2718afe38b5 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -44,6 +44,7 @@ int ethtool_check_max_channel(struct net_device *dev,
struct ethtool_channels channels,
struct genl_info *info);
int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context);
+int ethtool_rxfh_config_is_sym(u64 rxfh);
void ethtool_ringparam_get_cfg(struct net_device *dev,
struct ethtool_ringparam *param,
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index d62dc56f2f5b..b02067882594 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -806,6 +806,21 @@ int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context)
return rc;
}
+/* Check if fields configured for flow hash are symmetric - if src is included
+ * so is dst and vice versa.
+ */
+int ethtool_rxfh_config_is_sym(u64 rxfh)
+{
+ bool sym;
+
+ sym = rxfh == (rxfh & (RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3));
+ sym &= !!(rxfh & RXH_IP_SRC) == !!(rxfh & RXH_IP_DST);
+ sym &= !!(rxfh & RXH_L4_B_0_1) == !!(rxfh & RXH_L4_B_2_3);
+
+ return sym;
+}
+
int ethtool_check_ops(const struct ethtool_ops *ops)
{
if (WARN_ON(ops->set_coalesce && !ops->supported_coalesce_params))
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index cccb4694f5e1..d6c008b93fbb 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1027,9 +1027,7 @@ static int ethtool_check_xfrm_rxfh(u32 input_xfrm, u64 rxfh)
*/
if ((input_xfrm != RXH_XFRM_NO_CHANGE &&
input_xfrm & (RXH_XFRM_SYM_XOR | RXH_XFRM_SYM_OR_XOR)) &&
- ((rxfh & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) ||
- (!!(rxfh & RXH_IP_SRC) ^ !!(rxfh & RXH_IP_DST)) ||
- (!!(rxfh & RXH_L4_B_0_1) ^ !!(rxfh & RXH_L4_B_2_3))))
+ !ethtool_rxfh_config_is_sym(rxfh))
return -EINVAL;
return 0;
--git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 416ad428e61e..7b1faedaf559 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -478,6 +478,8 @@ const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] =
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
+ [ETHTOOL_A_RSS_INPUT_XFRM] =
+ NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR),
};
static int
@@ -487,6 +489,7 @@ ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
struct rss_req_info *request = RSS_REQINFO(req_info);
struct nlattr **tb = info->attrs;
struct nlattr *bad_attr = NULL;
+ u32 input_xfrm;
if (request->rss_context && !ops->create_rxfh_context)
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
@@ -494,8 +497,13 @@ ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
if (request->rss_context && !ops->rxfh_per_ctx_key) {
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC];
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY];
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
}
+ input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0);
+ if (input_xfrm & ~ops->supported_input_xfrm)
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
+
if (bad_attr) {
NL_SET_BAD_ATTR(info->extack, bad_attr);
return -EOPNOTSUPP;
@@ -609,6 +617,33 @@ rss_set_prep_hkey(struct net_device *dev, struct genl_info *info,
return 0;
}
+static int
+rss_check_rxfh_fields_sym(struct net_device *dev, struct genl_info *info,
+ struct rss_reply_data *data, bool xfrm_sym)
+{
+ struct nlattr **tb = info->attrs;
+ int i;
+
+ if (!xfrm_sym)
+ return 0;
+ if (!data->has_flow_hash) {
+ NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+ "hash field config not reported");
+ return -EINVAL;
+ }
+
+ for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++)
+ if (data->flow_hash[i] >= 0 &&
+ !ethtool_rxfh_config_is_sym(data->flow_hash[i])) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_RSS_INPUT_XFRM],
+ "hash field config is not symmetric");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static void
rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
@@ -627,16 +662,18 @@ rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
}
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE)
ctx->hfunc = rxfh->hfunc;
+ if (rxfh->input_xfrm != RXH_XFRM_NO_CHANGE)
+ ctx->input_xfrm = rxfh->input_xfrm;
}
static int
ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
{
+ bool indir_reset = false, indir_mod, xfrm_sym = false;
struct rss_req_info *request = RSS_REQINFO(req_info);
struct ethtool_rxfh_context *ctx = NULL;
struct net_device *dev = req_info->dev;
struct ethtool_rxfh_param rxfh = {};
- bool indir_reset = false, indir_mod;
struct nlattr **tb = info->attrs;
struct rss_reply_data data = {};
const struct ethtool_ops *ops;
@@ -666,7 +703,20 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
if (ret)
goto exit_free_indir;
- rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
+ rxfh.input_xfrm = data.input_xfrm;
+ ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+ /* For drivers which don't support input_xfrm it will be set to 0xff
+ * in the RSS context info. In all other case input_xfrm != 0 means
+ * symmetric hashing is requested.
+ */
+ if (!request->rss_context || ops->rxfh_per_ctx_key)
+ xfrm_sym = !!rxfh.input_xfrm;
+ if (rxfh.input_xfrm == data.input_xfrm)
+ rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
+
+ ret = rss_check_rxfh_fields_sym(dev, info, &data, xfrm_sym);
+ if (ret)
+ goto exit_clean_data;
mutex_lock(&dev->ethtool->rss_lock);
if (request->rss_context) {
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 10/11] ethtool: rss: support setting flow hashing fields
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (8 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 11/11] selftests: drv-net: rss_api: test input-xfrm and hash fields Jakub Kicinski
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Add support for ETHTOOL_SRXFH (setting hashing fields) in RSS_SET.
The tricky part is dealing with symmetric hashing. In netlink user
can change the hashing fields and symmetric hash in one request,
in IOCTL the two used to be set via different uAPI requests.
Since fields and hash function config are still separate driver
callbacks - changes to the two are not atomic. Keep things simple
and validate the settings against both pre- and post- change ones.
Meaning that we will reject the config request if user tries
to correct the flow fields and set input_xfrm in one request,
or disables input_xfrm and makes flow fields non-symmetric.
We can adjust it later if there's a real need. Starting simple feels
right, and potentially partially applying the settings isn't nice,
either.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- improve commit msg
---
Documentation/netlink/specs/ethtool.yaml | 1 +
Documentation/networking/ethtool-netlink.rst | 3 +-
net/ethtool/netlink.h | 2 +-
| 111 +++++++++++++++++--
4 files changed, 107 insertions(+), 10 deletions(-)
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 15cc3f2184eb..c9e95d96945e 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -2681,6 +2681,7 @@ c-version-name: ethtool-genl-version
- indir
- hkey
- input-xfrm
+ - flow-hash
-
name: rss-ntf
doc: |
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 2214d2ce346a..056832c77ffd 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -2003,6 +2003,7 @@ RSS_SET
``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes
``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation
+ ``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash
===================================== ====== ==============================
``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and
@@ -2464,7 +2465,7 @@ are netlink only.
``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET``
``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET``
``ETHTOOL_GRXFH`` ``ETHTOOL_MSG_RSS_GET``
- ``ETHTOOL_SRXFH`` n/a
+ ``ETHTOOL_SRXFH`` ``ETHTOOL_MSG_RSS_SET``
``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET``
``ETHTOOL_GRXRINGS`` n/a
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 620dd1ab9b3b..ddb2fb00f929 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -484,7 +484,7 @@ extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MO
extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1];
extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1];
extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_START_CONTEXT + 1];
-extern const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1];
+extern const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1];
extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1];
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
--git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 7b1faedaf559..0ee8306e656e 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -472,7 +472,41 @@ void ethtool_rss_notify(struct net_device *dev, u32 rss_context)
/* RSS_SET */
-const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] = {
+#define RFH_MASK (RXH_L2DA | RXH_VLAN | RXH_IP_SRC | RXH_IP_DST | \
+ RXH_L3_PROTO | RXH_L4_B_0_1 | RXH_L4_B_2_3 | \
+ RXH_GTP_TEID | RXH_DISCARD)
+
+static const struct nla_policy ethnl_rss_flows_policy[] = {
+ [ETHTOOL_A_FLOW_ETHER] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_IP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_IP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_TCP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_UDP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_SCTP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_AH_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_TCP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_UDP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_SCTP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_AH_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_AH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_AH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPC4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPC6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPC_TEID4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPC_TEID6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_EH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_EH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_UL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_UL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_DL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+ [ETHTOOL_A_FLOW_GTPU_DL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
+};
+
+const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
@@ -480,6 +514,7 @@ const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] =
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
[ETHTOOL_A_RSS_INPUT_XFRM] =
NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR),
+ [ETHTOOL_A_RSS_FLOW_HASH] = NLA_POLICY_NESTED(ethnl_rss_flows_policy),
};
static int
@@ -504,6 +539,12 @@ ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
if (input_xfrm & ~ops->supported_input_xfrm)
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
+ if (tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->set_rxfh_fields)
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH];
+ if (request->rss_context &&
+ tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->rxfh_per_ctx_fields)
+ bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH];
+
if (bad_attr) {
NL_SET_BAD_ATTR(info->extack, bad_attr);
return -EOPNOTSUPP;
@@ -644,6 +685,59 @@ rss_check_rxfh_fields_sym(struct net_device *dev, struct genl_info *info,
return 0;
}
+static int
+ethnl_set_rss_fields(struct net_device *dev, struct genl_info *info,
+ u32 rss_context, struct rss_reply_data *data,
+ bool xfrm_sym, bool *mod)
+{
+ struct nlattr *flow_nest = info->attrs[ETHTOOL_A_RSS_FLOW_HASH];
+ struct nlattr *flows[ETHTOOL_A_FLOW_MAX + 1];
+ const struct ethtool_ops *ops;
+ int i, ret;
+
+ ops = dev->ethtool_ops;
+
+ ret = rss_check_rxfh_fields_sym(dev, info, data, xfrm_sym);
+ if (ret)
+ return ret;
+
+ if (!flow_nest)
+ return 0;
+
+ ret = nla_parse_nested(flows, ARRAY_SIZE(ethnl_rss_flows_policy) - 1,
+ flow_nest, ethnl_rss_flows_policy, info->extack);
+ if (ret < 0)
+ return ret;
+
+ for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
+ struct ethtool_rxfh_fields fields = {
+ .flow_type = ethtool_rxfh_ft_nl2ioctl[i],
+ .rss_context = rss_context,
+ };
+
+ if (!flows[i])
+ continue;
+
+ fields.data = nla_get_u32(flows[i]);
+ if (data->has_flow_hash && data->flow_hash[i] == fields.data)
+ continue;
+
+ if (xfrm_sym && !ethtool_rxfh_config_is_sym(fields.data)) {
+ NL_SET_ERR_MSG_ATTR(info->extack, flows[i],
+ "conflict with xfrm-input");
+ return -EINVAL;
+ }
+
+ ret = ops->set_rxfh_fields(dev, &fields, info->extack);
+ if (ret)
+ return ret;
+
+ *mod = true;
+ }
+
+ return 0;
+}
+
static void
rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
@@ -673,11 +767,11 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
struct rss_req_info *request = RSS_REQINFO(req_info);
struct ethtool_rxfh_context *ctx = NULL;
struct net_device *dev = req_info->dev;
+ bool mod = false, fields_mod = false;
struct ethtool_rxfh_param rxfh = {};
struct nlattr **tb = info->attrs;
struct rss_reply_data data = {};
const struct ethtool_ops *ops;
- bool mod = false;
int ret;
ops = dev->ethtool_ops;
@@ -710,14 +804,10 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
* symmetric hashing is requested.
*/
if (!request->rss_context || ops->rxfh_per_ctx_key)
- xfrm_sym = !!rxfh.input_xfrm;
+ xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
if (rxfh.input_xfrm == data.input_xfrm)
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
- ret = rss_check_rxfh_fields_sym(dev, info, &data, xfrm_sym);
- if (ret)
- goto exit_clean_data;
-
mutex_lock(&dev->ethtool->rss_lock);
if (request->rss_context) {
ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
@@ -727,6 +817,11 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
}
}
+ ret = ethnl_set_rss_fields(dev, info, request->rss_context,
+ &data, xfrm_sym, &fields_mod);
+ if (ret)
+ goto exit_unlock;
+
if (!mod)
ret = 0; /* nothing to tell the driver */
else if (!ops->set_rxfh)
@@ -753,7 +848,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
exit_clean_data:
rss_cleanup_data(&data.base);
- return ret ?: mod;
+ return ret ?: mod || fields_mod;
}
const struct ethnl_request_ops ethnl_rss_request_ops = {
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 11/11] selftests: drv-net: rss_api: test input-xfrm and hash fields
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
` (9 preceding siblings ...)
2025-07-14 22:27 ` [PATCH net-next v2 10/11] ethtool: rss: support setting flow hashing fields Jakub Kicinski
@ 2025-07-14 22:27 ` Jakub Kicinski
10 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-14 22:27 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx, gal,
Jakub Kicinski
Test configuring input-xfrm and hash fields with all the limitations.
Tested on mlx5 (CX6):
# ./ksft-net-drv/drivers/net/hw/rss_api.py
TAP version 13
1..10
ok 1 rss_api.test_rxfh_nl_set_fail
ok 2 rss_api.test_rxfh_nl_set_indir
ok 3 rss_api.test_rxfh_nl_set_indir_ctx
ok 4 rss_api.test_rxfh_indir_ntf
ok 5 rss_api.test_rxfh_indir_ctx_ntf
ok 6 rss_api.test_rxfh_nl_set_key
ok 7 rss_api.test_rxfh_fields
ok 8 rss_api.test_rxfh_fields_set
ok 9 rss_api.test_rxfh_fields_set_xfrm
ok 10 rss_api.test_rxfh_fields_ntf
# Totals: pass:10 fail:0 xfail:0 xpass:0 skip:0 error:0
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- make defer() cleanup more intelligent WRT ordering
---
| 143 ++++++++++++++++++
1 file changed, 143 insertions(+)
--git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py
index 4de566edb313..7a1a1c3b5f6e 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_api.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_api.py
@@ -247,6 +247,149 @@ from lib.py import NetDrvEnv
comment="Config for " + fl_type)
+def test_rxfh_fields_set(cfg):
+ """ Test configuring Rx Flow Hash over Netlink. """
+
+ flow_types = ["tcp4", "tcp6", "udp4", "udp6"]
+
+ # Collect current settings
+ cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ # symmetric hashing is config-order-sensitive make sure we leave
+ # symmetric mode, or make the flow-hash sym-compatible first
+ changes = [{"flow-hash": cfg_old["flow-hash"],},
+ {"input-xfrm": cfg_old.get("input-xfrm", 0),}]
+ if cfg_old.get("input-xfrm"):
+ changes = list(reversed(changes))
+ for old in changes:
+ defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old)
+
+ # symmetric hashing prevents some of the configs below
+ if cfg_old.get("input-xfrm", None):
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "input-xfrm": 0})
+
+ for fl_type in flow_types:
+ cur = _ethtool_get_cfg(cfg, fl_type)
+ if cur == "sdfn":
+ change_nl = {"ip-src", "ip-dst"}
+ change_ic = "sd"
+ else:
+ change_nl = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"}
+ change_ic = "sdfn"
+
+ cfg.ethnl.rss_set({
+ "header": {"dev-index": cfg.ifindex},
+ "flow-hash": {fl_type: change_nl}
+ })
+ reset = defer(ethtool, f"--disable-netlink -N {cfg.ifname} "
+ f"rx-flow-hash {fl_type} {cur}")
+
+ cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(change_nl, cfg_nl["flow-hash"][fl_type],
+ comment=f"Config for {fl_type} over Netlink")
+ cfg_ic = _ethtool_get_cfg(cfg, fl_type)
+ ksft_eq(change_ic, cfg_ic,
+ comment=f"Config for {fl_type} over IOCTL")
+
+ reset.exec()
+ cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ ksft_eq(cfg_old["flow-hash"][fl_type], cfg_nl["flow-hash"][fl_type],
+ comment=f"Un-config for {fl_type} over Netlink")
+ cfg_ic = _ethtool_get_cfg(cfg, fl_type)
+ ksft_eq(cur, cfg_ic, comment=f"Un-config for {fl_type} over IOCTL")
+
+ # Try to set multiple at once, the defer was already installed at the start
+ change = {"ip-src"}
+ if change == cfg_old["flow-hash"]["tcp4"]:
+ change = {"ip-dst"}
+ cfg.ethnl.rss_set({
+ "header": {"dev-index": cfg.ifindex},
+ "flow-hash": {x: change for x in flow_types}
+ })
+
+ cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ for fl_type in flow_types:
+ ksft_eq(change, cfg_nl["flow-hash"][fl_type],
+ comment=f"multi-config for {fl_type} over Netlink")
+
+
+def test_rxfh_fields_set_xfrm(cfg):
+ """ Test changing Rx Flow Hash vs xfrm_input at once. """
+
+ def set_rss(cfg, xfrm, fh):
+ cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex},
+ "input-xfrm": xfrm, "flow-hash": fh})
+
+ # Install the reset handler
+ cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ # symmetric hashing is config-order-sensitive make sure we leave
+ # symmetric mode, or make the flow-hash sym-compatible first
+ changes = [{"flow-hash": cfg_old["flow-hash"],},
+ {"input-xfrm": cfg_old.get("input-xfrm", 0),}]
+ if cfg_old.get("input-xfrm"):
+ changes = list(reversed(changes))
+ for old in changes:
+ defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old)
+
+ # Make sure we start with input-xfrm off, and tcp4 config non-sym
+ set_rss(cfg, 0, {})
+ set_rss(cfg, 0, {"tcp4": {"ip-src"}})
+
+ # Setting sym and fixing tcp4 config not expected to pass right now
+ with ksft_raises(NlError):
+ set_rss(cfg, "sym-xor", {"tcp4": {"ip-src", "ip-dst"}})
+ # One at a time should work, hopefully
+ set_rss(cfg, 0, {"tcp4": {"ip-src", "ip-dst"}})
+ no_support = False
+ try:
+ set_rss(cfg, "sym-xor", {})
+ except NlError:
+ try:
+ set_rss(cfg, "sym-or-xor", {})
+ except NlError:
+ no_support = True
+ if no_support:
+ raise KsftSkipEx("no input-xfrm supported")
+ # Disabling two at once should not work either without kernel changes
+ with ksft_raises(NlError):
+ set_rss(cfg, 0, {"tcp4": {"ip-src"}})
+
+
+def test_rxfh_fields_ntf(cfg):
+ """ Test Rx Flow Hash notifications. """
+
+ cur = _ethtool_get_cfg(cfg, "tcp4")
+ if cur == "sdfn":
+ change = {"ip-src", "ip-dst"}
+ else:
+ change = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"}
+
+ ethnl = EthtoolFamily()
+ ethnl.ntf_subscribe("monitor")
+
+ ethnl.rss_set({
+ "header": {"dev-index": cfg.ifindex},
+ "flow-hash": {"tcp4": change}
+ })
+ reset = defer(ethtool,
+ f"--disable-netlink -N {cfg.ifname} rx-flow-hash tcp4 {cur}")
+
+ ntf = next(ethnl.poll_ntf(duration=0.2), None)
+ if ntf is None:
+ raise KsftFailEx("No notification received after IOCTL change")
+ ksft_eq(ntf["name"], "rss-ntf")
+ ksft_eq(ntf["msg"]["flow-hash"]["tcp4"], change)
+ ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None)
+
+ reset.exec()
+ ntf = next(ethnl.poll_ntf(duration=0.2), None)
+ if ntf is None:
+ raise KsftFailEx("No notification received after Netlink change")
+ ksft_eq(ntf["name"], "rss-ntf")
+ ksft_ne(ntf["msg"]["flow-hash"]["tcp4"], change)
+ ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None)
+
+
def main() -> None:
""" Ksft boiler plate main """
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling)
2025-07-14 22:27 ` [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling) Jakub Kicinski
@ 2025-07-15 7:20 ` Gal Pressman
2025-07-15 14:39 ` Jakub Kicinski
0 siblings, 1 reply; 19+ messages in thread
From: Gal Pressman @ 2025-07-15 7:20 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx
On 15/07/2025 1:27, Jakub Kicinski wrote:
> +static int
> +rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
> + struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
> + bool *reset, bool *mod)
> +{
> + const struct ethtool_ops *ops = dev->ethtool_ops;
> + struct netlink_ext_ack *extack = info->extack;
> + struct nlattr **tb = info->attrs;
> + struct ethtool_rxnfc rx_rings;
> + size_t alloc_size;
> + u32 user_size;
> + int i, err;
> +
> + if (!tb[ETHTOOL_A_RSS_INDIR])
> + return 0;
> + if (!data->indir_size)
> + return -EOPNOTSUPP;
> +
> + rx_rings.cmd = ETHTOOL_GRXRINGS;
> + err = ops->get_rxnfc(dev, &rx_rings, NULL);
Do we need to check for NULL op?
> + if (err)
> + return err;
> +
> + if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) {
> + NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]);
> + return -EINVAL;
> + }
> + user_size = nla_len(tb[ETHTOOL_A_RSS_INDIR]) / 4;
> + if (!user_size) {
> + if (rxfh->rss_context) {
> + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_RSS_INDIR],
> + "can't reset table for a context");
> + return -EINVAL;
> + }
> + *reset = true;
> + } else if (data->indir_size % user_size) {
> + NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
> + "size (%d) mismatch with device indir table (%d)",
> + user_size, data->indir_size);
> + return -EINVAL;
> + }
> +
> + rxfh->indir_size = data->indir_size;
> + alloc_size = array_size(data->indir_size, sizeof(rxfh->indir[0]));
> + rxfh->indir = kzalloc(alloc_size, GFP_KERNEL);
> + if (!rxfh->indir)
> + return -ENOMEM;
> +
> + nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size);
> + for (i = 0; i < user_size; i++) {
> + if (rxfh->indir[i] < rx_rings.data)
> + continue;
> +
> + NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
> + "entry %d: queue out of range (%d)",
> + i, rxfh->indir[i]);
> + err = -EINVAL;
> + goto err_free;
> + }
> +
> + if (user_size) {
> + /* Replicate the user-provided table to fill the device table */
> + for (i = user_size; i < data->indir_size; i++)
> + rxfh->indir[i] = rxfh->indir[i % user_size];
> + } else {
> + for (i = 0; i < data->indir_size; i++)
> + rxfh->indir[i] =
> + ethtool_rxfh_indir_default(i, rx_rings.data);
> + }
> +
> + *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
> +
> + return 0;
> +
> +err_free:
> + kfree(rxfh->indir);
> + rxfh->indir = NULL;
> + return err;
> +}
> +
> +static int
> +ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
> +{
> + struct rss_req_info *request = RSS_REQINFO(req_info);
> + struct ethtool_rxfh_context *ctx = NULL;
> + struct net_device *dev = req_info->dev;
> + struct ethtool_rxfh_param rxfh = {};
> + bool indir_reset = false, indir_mod;
> + struct nlattr **tb = info->attrs;
> + struct rss_reply_data data = {};
> + const struct ethtool_ops *ops;
> + bool mod = false;
> + int ret;
> +
> + ops = dev->ethtool_ops;
> + data.base.dev = dev;
> +
> + ret = rss_prepare(request, dev, &data, info);
> + if (ret)
> + return ret;
> +
> + rxfh.rss_context = request->rss_context;
> +
> + ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod);
> + if (ret)
> + goto exit_clean_data;
> + indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
> +
> + rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
> + rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
> +
> + mutex_lock(&dev->ethtool->rss_lock);
> + if (request->rss_context) {
> + ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
> + if (!ctx) {
> + ret = -ENOENT;
> + goto exit_unlock;
> + }
> + }
> +
> + if (!mod)
> + ret = 0; /* nothing to tell the driver */
> + else if (!ops->set_rxfh)
Why not do it in validate?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc via Netlink
2025-07-14 22:27 ` [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc " Jakub Kicinski
@ 2025-07-15 7:24 ` Gal Pressman
0 siblings, 0 replies; 19+ messages in thread
From: Gal Pressman @ 2025-07-15 7:24 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx
On 15/07/2025 1:27, Jakub Kicinski wrote:
> Support setting RSS hash function / algo via ethtool Netlink.
> Like IOCTL we don't validate that the function is within the
> range known to the kernel. The drivers do a pretty good job
> validating the inputs, and the IDs are technically "dynamically
> queried" rather than part of uAPI.
>
> Only change should be that in Netlink we don't support user
> explicitly passing ETH_RSS_HASH_NO_CHANGE (0), if no change
> is requested the attribute should be absent.
>
> The ETH_RSS_HASH_NO_CHANGE is retained in driver-facing
> API for consistency (not that I see a strong reason for it).
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Gal Pressman <gal@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 06/11] ethtool: rss: support setting hkey via Netlink
2025-07-14 22:27 ` [PATCH net-next v2 06/11] ethtool: rss: support setting hkey " Jakub Kicinski
@ 2025-07-15 7:30 ` Gal Pressman
0 siblings, 0 replies; 19+ messages in thread
From: Gal Pressman @ 2025-07-15 7:30 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx
On 15/07/2025 1:27, Jakub Kicinski wrote:
> Support setting RSS hashing key via ethtool Netlink.
> Use the Netlink policy to make sure user doesn't pass
> an empty key, "resetting" the key is not a thing.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Gal Pressman <gal@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec
2025-07-14 22:27 ` [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec Jakub Kicinski
@ 2025-07-15 8:35 ` Gal Pressman
2025-07-15 14:45 ` Jakub Kicinski
0 siblings, 1 reply; 19+ messages in thread
From: Gal Pressman @ 2025-07-15 8:35 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx
On 15/07/2025 1:27, Jakub Kicinski wrote:
> Help YNL decode the values for input-xfrm by defining
> the possible values in the spec. Don't define "no change"
> as it's an IOCTL artifact with no use in Netlink.
>
> With this change on mlx5 input-xfrm gets decoded:
>
> # ynl --family ethtool --dump rss-get
> [{'header': {'dev-index': 2, 'dev-name': 'eth0'},
> 'hfunc': 1,
> 'hkey': b'V\xa8\xf9\x9 ...',
> 'indir': [0, 1, ... ],
> 'input-xfrm': 'sym-or-xor', <<<
> 'flow-hash': {'ah4': {'ip-dst', 'ip-src'},
> 'ah6': {'ip-dst', 'ip-src'},
> 'esp4': {'ip-dst', 'ip-src'},
> 'esp6': {'ip-dst', 'ip-src'},
> 'ip4': {'ip-dst', 'ip-src'},
> 'ip6': {'ip-dst', 'ip-src'},
> 'tcp4': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
> 'tcp6': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
> 'udp4': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'},
> 'udp6': {'l4-b-0-1', 'ip-dst', 'l4-b-2-3', 'ip-src'}}
> }]
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
We kinda use input_xfrm as an enum, but in theory it's a bitmask, so
while this is OK today, I'm not sure this patch is future-proof.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink
2025-07-14 22:27 ` [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink Jakub Kicinski
@ 2025-07-15 8:40 ` Gal Pressman
0 siblings, 0 replies; 19+ messages in thread
From: Gal Pressman @ 2025-07-15 8:40 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
shuah, kory.maincent, maxime.chevallier, sdf, ecree.xilinx
On 15/07/2025 1:27, Jakub Kicinski wrote:
> Support configuring symmetric hashing via Netlink.
> We have the flow field config prepared as part of SET handling,
> so scan it for conflicts instead of querying the driver again.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Gal Pressman <gal@nvidia.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling)
2025-07-15 7:20 ` Gal Pressman
@ 2025-07-15 14:39 ` Jakub Kicinski
0 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-15 14:39 UTC (permalink / raw)
To: Gal Pressman
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, shuah, kory.maincent, maxime.chevallier, sdf,
ecree.xilinx
On Tue, 15 Jul 2025 10:20:38 +0300 Gal Pressman wrote:
> > + rx_rings.cmd = ETHTOOL_GRXRINGS;
> > + err = ops->get_rxnfc(dev, &rx_rings, NULL);
>
> Do we need to check for NULL op?
Hm, yes.
> > + mutex_lock(&dev->ethtool->rss_lock);
> > + if (request->rss_context) {
> > + ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
> > + if (!ctx) {
> > + ret = -ENOENT;
> > + goto exit_unlock;
> > + }
> > + }
> > +
> > + if (!mod)
> > + ret = 0; /* nothing to tell the driver */
> > + else if (!ops->set_rxfh)
>
> Why not do it in validate?
Because of the silly drivers which only support setting hash fields but
not the RSS basics. We'd need to duplicate the list of which fields end
up fed to ->set_rxfh in validate.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec
2025-07-15 8:35 ` Gal Pressman
@ 2025-07-15 14:45 ` Jakub Kicinski
0 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2025-07-15 14:45 UTC (permalink / raw)
To: Gal Pressman
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, shuah, kory.maincent, maxime.chevallier, sdf,
ecree.xilinx
On Tue, 15 Jul 2025 11:35:08 +0300 Gal Pressman wrote:
> We kinda use input_xfrm as an enum, but in theory it's a bitmask, so
> while this is OK today, I'm not sure this patch is future-proof.
Yeah, a little unclear at this stage if it's a bitmask or an enum since
we only have values 0 1 2 defined, and the defined values cannot be
composed. Adding an entry that'd compose would be painful if we go with
the string. OTOH I can't think of any composable transform and it's
extra effort to extract the entry form a one-element set each time.
I guess we should go with future-proofness when in doubt.
I'll make it into flags in v3.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2025-07-15 14:45 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-14 22:27 [PATCH net-next v2 00/11] ethtool: rss: support RSS_SET via Netlink Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 01/11] ethtool: rss: initial RSS_SET (indirection table handling) Jakub Kicinski
2025-07-15 7:20 ` Gal Pressman
2025-07-15 14:39 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 02/11] selftests: drv-net: rss_api: factor out checking min queue count Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 03/11] tools: ynl: support packing binary arrays of scalars Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 04/11] selftests: drv-net: rss_api: test setting indirection table via Netlink Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 05/11] ethtool: rss: support setting hfunc " Jakub Kicinski
2025-07-15 7:24 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 06/11] ethtool: rss: support setting hkey " Jakub Kicinski
2025-07-15 7:30 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 07/11] selftests: drv-net: rss_api: test setting hashing key " Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 08/11] netlink: specs: define input-xfrm enum in the spec Jakub Kicinski
2025-07-15 8:35 ` Gal Pressman
2025-07-15 14:45 ` Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 09/11] ethtool: rss: support setting input-xfrm via Netlink Jakub Kicinski
2025-07-15 8:40 ` Gal Pressman
2025-07-14 22:27 ` [PATCH net-next v2 10/11] ethtool: rss: support setting flow hashing fields Jakub Kicinski
2025-07-14 22:27 ` [PATCH net-next v2 11/11] selftests: drv-net: rss_api: test input-xfrm and hash fields Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).