ethtool: rss: report which fields are configured for hashing

Implement ETHTOOL_GRXFH over Netlink. The number of flow types is
reasonable (around 20) so report all of them at once for simplicity.

Do not maintain the flow ID mapping with ioctl at the uAPI level.
This gives us a chance to clean up the confusion that come from
RxNFC vs RxFH (flow direction vs hashing) in the ioctl.
Try to align with the names used in ethtool CLI, they seem to have
stood the test of time just fine. One annoyance is that we still
call L4 ports the weird names, but I guess they also apply to IPSec
(where they cover the SPI) so it is what it is.

 $ ynl --family ethtool --dump rss-get
 {
    "header": {
	"dev-index": 1,
	"dev-name": "enp1s0"
    },
    "hfunc": 1,
    "hkey": b"...",
    "indir": [0, 1, ...],
    "flow-hash": {
        "ether": {"l2da"},
	"ah-esp4": {"ip-src", "ip-dst"},
        "ah-esp6": {"ip-src", "ip-dst"},
        "ah4": {"ip-src", "ip-dst"},
        "ah6": {"ip-src", "ip-dst"},
        "esp4": {"ip-src", "ip-dst"},
        "esp6": {"ip-src", "ip-dst"},
        "ip4": {"ip-src", "ip-dst"},
        "ip6": {"ip-src", "ip-dst"},
        "sctp4": {"ip-src", "ip-dst"},
        "sctp6": {"ip-src", "ip-dst"},
        "udp4": {"ip-src", "ip-dst"},
        "udp6": {"ip-src", "ip-dst"}
        "tcp4": {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"},
        "tcp6": {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"},
    },
 }

Link: https://patch.msgid.link/20250708220640.2738464-5-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-07-08 15:06:39 -07:00
parent d7974697de
commit 178331743c
5 changed files with 291 additions and 14 deletions

View File

@@ -158,6 +158,35 @@ definitions:
-
name: pse-event-sw-pw-control-error
doc: PSE faced an error managing the power control from software
-
name: rxfh-fields
name-prefix: rxh-
enum-name:
header: linux/ethtool.h
type: flags
entries:
-
name: l2da
value: 1
-
name: vlan
-
name: l3-proto
-
name: ip-src
-
name: ip-dst
-
name: l4-b-0-1
doc: src port in case of TCP/UDP/SCTP
-
name: l4-b-2-3
doc: dst port in case of TCP/UDP/SCTP
-
name: gtp-teid
-
name: discard
value: 31
attribute-sets:
-
@@ -1447,6 +1476,123 @@ attribute-sets:
name: pse-prio
type: u32
name-prefix: ethtool-a-
-
name: flow
attr-cnt-name: --ethtool-a-flow-cnt
doc: |
Flow types, corresponding to those defined in the old
ethtool header for RXFH and RXNFC as ${PROTO}_FLOW.
The values are not matching the old ones to avoid carrying
into Netlink the IP_USER_FLOW vs IPV4_FLOW vs IPV4_USER_FLOW confusion.
attributes:
-
name: ether
type: uint
enum: rxfh-fields
-
name: ip4
type: uint
enum: rxfh-fields
-
name: ip6
type: uint
enum: rxfh-fields
-
name: tcp4
type: uint
enum: rxfh-fields
-
name: tcp6
type: uint
enum: rxfh-fields
-
name: udp4
type: uint
enum: rxfh-fields
-
name: udp6
type: uint
enum: rxfh-fields
-
name: sctp4
type: uint
enum: rxfh-fields
-
name: sctp6
type: uint
enum: rxfh-fields
-
name: ah4
type: uint
enum: rxfh-fields
-
name: ah6
type: uint
enum: rxfh-fields
-
name: esp4
type: uint
enum: rxfh-fields
-
name: esp6
type: uint
enum: rxfh-fields
-
name: ah-esp4
type: uint
enum: rxfh-fields
-
name: ah-esp6
type: uint
enum: rxfh-fields
-
name: gtpu4
type: uint
enum: rxfh-fields
-
name: gtpu6
type: uint
enum: rxfh-fields
-
name: gtpc4
type: uint
enum: rxfh-fields
-
name: gtpc6
type: uint
enum: rxfh-fields
-
name: gtpc-teid4
type: uint
enum: rxfh-fields
-
name: gtpc-teid6
type: uint
enum: rxfh-fields
-
name: gtpu-eh4
type: uint
enum: rxfh-fields
-
name: gtpu-eh6
type: uint
enum: rxfh-fields
-
name: gtpu-ul4
type: uint
enum: rxfh-fields
-
name: gtpu-ul6
type: uint
enum: rxfh-fields
-
name: gtpu-dl4
type: uint
enum: rxfh-fields
-
name: gtpu-dl6
type: uint
enum: rxfh-fields
-
name: rss
attr-cnt-name: __ethtool-a-rss-cnt
@@ -1478,6 +1624,10 @@ attribute-sets:
-
name: start-context
type: u32
-
name: flow-hash
type: nest
nested-attributes: flow
-
name: plca
attr-cnt-name: __ethtool-a-plca-cnt
@@ -2307,6 +2457,7 @@ operations:
- indir
- hkey
- input-xfrm
- flow-hash
dump:
request:
attributes:

View File

@@ -1969,14 +1969,15 @@ used to ignore context 0s and only dump additional contexts).
Kernel response contents:
===================================== ====== ==========================
===================================== ====== ===============================
``ETHTOOL_A_RSS_HEADER`` nested reply header
``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_INPUT_XFRM`` u32 RSS input data transformation
===================================== ====== ==========================
``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash
===================================== ====== ===============================
ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function
being used. Current supported options are toeplitz, xor or crc32.
@@ -1985,6 +1986,8 @@ indicates queue number.
ETHTOOL_A_RSS_INPUT_XFRM attribute is a bitmap indicating the type of
transformation applied to the input protocol fields before given to the RSS
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.
PLCA_GET_CFG
============
@@ -2436,7 +2439,7 @@ are netlink only.
``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET``
``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET``
``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET``
``ETHTOOL_GRXFH`` n/a
``ETHTOOL_GRXFH`` ``ETHTOOL_MSG_RSS_GET``
``ETHTOOL_SRXFH`` n/a
``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET``
``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET``

View File

@@ -678,6 +678,39 @@ enum {
ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1)
};
enum {
ETHTOOL_A_FLOW_ETHER = 1,
ETHTOOL_A_FLOW_IP4,
ETHTOOL_A_FLOW_IP6,
ETHTOOL_A_FLOW_TCP4,
ETHTOOL_A_FLOW_TCP6,
ETHTOOL_A_FLOW_UDP4,
ETHTOOL_A_FLOW_UDP6,
ETHTOOL_A_FLOW_SCTP4,
ETHTOOL_A_FLOW_SCTP6,
ETHTOOL_A_FLOW_AH4,
ETHTOOL_A_FLOW_AH6,
ETHTOOL_A_FLOW_ESP4,
ETHTOOL_A_FLOW_ESP6,
ETHTOOL_A_FLOW_AH_ESP4,
ETHTOOL_A_FLOW_AH_ESP6,
ETHTOOL_A_FLOW_GTPU4,
ETHTOOL_A_FLOW_GTPU6,
ETHTOOL_A_FLOW_GTPC4,
ETHTOOL_A_FLOW_GTPC6,
ETHTOOL_A_FLOW_GTPC_TEID4,
ETHTOOL_A_FLOW_GTPC_TEID6,
ETHTOOL_A_FLOW_GTPU_EH4,
ETHTOOL_A_FLOW_GTPU_EH6,
ETHTOOL_A_FLOW_GTPU_UL4,
ETHTOOL_A_FLOW_GTPU_UL6,
ETHTOOL_A_FLOW_GTPU_DL4,
ETHTOOL_A_FLOW_GTPU_DL6,
__ETHTOOL_A_FLOW_CNT,
ETHTOOL_A_FLOW_MAX = (__ETHTOOL_A_FLOW_CNT - 1)
};
enum {
ETHTOOL_A_RSS_UNSPEC,
ETHTOOL_A_RSS_HEADER,
@@ -687,6 +720,7 @@ enum {
ETHTOOL_A_RSS_HKEY,
ETHTOOL_A_RSS_INPUT_XFRM,
ETHTOOL_A_RSS_START_CONTEXT,
ETHTOOL_A_RSS_FLOW_HASH,
__ETHTOOL_A_RSS_CNT,
ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1)

View File

@@ -1101,7 +1101,11 @@ ethtool_set_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
rc = ops->set_rxfh_fields(dev, &fields, NULL);
exit_unlock:
mutex_unlock(&dev->ethtool->rss_lock);
return rc;
if (rc)
return rc;
ethtool_rss_notify(dev, fields.rss_context);
return 0;
}
static noinline_for_stack int

View File

@@ -12,6 +12,7 @@ struct rss_req_info {
struct rss_reply_data {
struct ethnl_reply_data base;
bool has_flow_hash;
bool no_key_fields;
u32 indir_size;
u32 hkey_size;
@@ -19,6 +20,37 @@ struct rss_reply_data {
u32 input_xfrm;
u32 *indir_table;
u8 *hkey;
int flow_hash[__ETHTOOL_A_FLOW_CNT];
};
static const u8 ethtool_rxfh_ft_nl2ioctl[] = {
[ETHTOOL_A_FLOW_ETHER] = ETHER_FLOW,
[ETHTOOL_A_FLOW_IP4] = IPV4_FLOW,
[ETHTOOL_A_FLOW_IP6] = IPV6_FLOW,
[ETHTOOL_A_FLOW_TCP4] = TCP_V4_FLOW,
[ETHTOOL_A_FLOW_UDP4] = UDP_V4_FLOW,
[ETHTOOL_A_FLOW_SCTP4] = SCTP_V4_FLOW,
[ETHTOOL_A_FLOW_AH_ESP4] = AH_ESP_V4_FLOW,
[ETHTOOL_A_FLOW_TCP6] = TCP_V6_FLOW,
[ETHTOOL_A_FLOW_UDP6] = UDP_V6_FLOW,
[ETHTOOL_A_FLOW_SCTP6] = SCTP_V6_FLOW,
[ETHTOOL_A_FLOW_AH_ESP6] = AH_ESP_V6_FLOW,
[ETHTOOL_A_FLOW_AH4] = AH_V4_FLOW,
[ETHTOOL_A_FLOW_ESP4] = ESP_V4_FLOW,
[ETHTOOL_A_FLOW_AH6] = AH_V6_FLOW,
[ETHTOOL_A_FLOW_ESP6] = ESP_V6_FLOW,
[ETHTOOL_A_FLOW_GTPU4] = GTPU_V4_FLOW,
[ETHTOOL_A_FLOW_GTPU6] = GTPU_V6_FLOW,
[ETHTOOL_A_FLOW_GTPC4] = GTPC_V4_FLOW,
[ETHTOOL_A_FLOW_GTPC6] = GTPC_V6_FLOW,
[ETHTOOL_A_FLOW_GTPC_TEID4] = GTPC_TEID_V4_FLOW,
[ETHTOOL_A_FLOW_GTPC_TEID6] = GTPC_TEID_V6_FLOW,
[ETHTOOL_A_FLOW_GTPU_EH4] = GTPU_EH_V4_FLOW,
[ETHTOOL_A_FLOW_GTPU_EH6] = GTPU_EH_V6_FLOW,
[ETHTOOL_A_FLOW_GTPU_UL4] = GTPU_UL_V4_FLOW,
[ETHTOOL_A_FLOW_GTPU_UL6] = GTPU_UL_V6_FLOW,
[ETHTOOL_A_FLOW_GTPU_DL4] = GTPU_DL_V4_FLOW,
[ETHTOOL_A_FLOW_GTPU_DL6] = GTPU_DL_V6_FLOW,
};
#define RSS_REQINFO(__req_base) \
@@ -49,6 +81,37 @@ rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
return 0;
}
static void
rss_prepare_flow_hash(const struct rss_req_info *req, struct net_device *dev,
struct rss_reply_data *data, const struct genl_info *info)
{
int i;
data->has_flow_hash = false;
if (!dev->ethtool_ops->get_rxfh_fields)
return;
if (req->rss_context && !dev->ethtool_ops->rxfh_per_ctx_fields)
return;
mutex_lock(&dev->ethtool->rss_lock);
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
struct ethtool_rxfh_fields fields = {
.flow_type = ethtool_rxfh_ft_nl2ioctl[i],
.rss_context = req->rss_context,
};
if (dev->ethtool_ops->get_rxfh_fields(dev, &fields)) {
data->flow_hash[i] = -1; /* Unsupported */
continue;
}
data->flow_hash[i] = fields.data;
data->has_flow_hash = true;
}
mutex_unlock(&dev->ethtool->rss_lock);
}
static int
rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
struct rss_reply_data *data, const struct genl_info *info)
@@ -153,6 +216,8 @@ static int
rss_prepare(const struct rss_req_info *request, struct net_device *dev,
struct rss_reply_data *data, const struct genl_info *info)
{
rss_prepare_flow_hash(request, dev, data, info);
if (request->rss_context)
return rss_prepare_ctx(request, dev, data, info);
return rss_prepare_get(request, dev, data, info);
@@ -190,7 +255,10 @@ rss_reply_size(const struct ethnl_req_info *req_base,
nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */
nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */
nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */
nla_total_size(data->hkey_size); /* _RSS_HKEY */
nla_total_size(data->hkey_size) + /* _RSS_HKEY */
nla_total_size(0) + /* _RSS_FLOW_HASH */
nla_total_size(sizeof(u32)) * ETHTOOL_A_FLOW_MAX +
0;
return len;
}
@@ -211,17 +279,34 @@ rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
sizeof(u32) * data->indir_size, data->indir_table)))
return -EMSGSIZE;
if (data->no_key_fields)
return 0;
if ((data->hfunc &&
nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
(data->input_xfrm &&
nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) ||
(data->hkey_size &&
nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey)))
if (!data->no_key_fields &&
((data->hfunc &&
nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
(data->input_xfrm &&
nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) ||
(data->hkey_size &&
nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))))
return -EMSGSIZE;
if (data->has_flow_hash) {
struct nlattr *nest;
int i;
nest = nla_nest_start(skb, ETHTOOL_A_RSS_FLOW_HASH);
if (!nest)
return -EMSGSIZE;
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
if (data->flow_hash[i] >= 0 &&
nla_put_uint(skb, i, data->flow_hash[i])) {
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
}
nla_nest_end(skb, nest);
}
return 0;
}