summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/netlink/specs/ethtool.yaml151
-rw-r--r--Documentation/networking/ethtool-netlink.rst9
-rw-r--r--include/uapi/linux/ethtool.h4
-rw-r--r--include/uapi/linux/ethtool_netlink_generated.h34
-rw-r--r--net/ethtool/ioctl.c7
-rw-r--r--net/ethtool/rss.c145
-rw-r--r--tools/net/ynl/pyynl/lib/ynl.py2
-rwxr-xr-xtools/testing/selftests/drivers/net/hw/rss_api.py47
8 files changed, 364 insertions, 35 deletions
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 49e782a33eb6..c38c03c624f0 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -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:
-
@@ -1448,6 +1477,123 @@ attribute-sets:
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
attributes:
@@ -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:
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 07e9808ebd2c..248bc3d93da9 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -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``
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 707c1844010c..9e9afdd1238a 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -2314,7 +2314,7 @@ enum {
IPV6_USER_FLOW = 0x0e, /* spec only (usr_ip6_spec; nfc only) */
IPV4_FLOW = 0x10, /* hash only */
IPV6_FLOW = 0x11, /* hash only */
- ETHER_FLOW = 0x12, /* spec only (ether_spec) */
+ ETHER_FLOW = 0x12, /* hash or spec (ether_spec) */
/* Used for GTP-U IPv4 and IPv6.
* The format of GTP packets only includes
@@ -2371,7 +2371,7 @@ enum {
/* Flag to enable RSS spreading of traffic matching rule (nfc only) */
#define FLOW_RSS 0x20000000
-/* L3-L4 network traffic flow hash options */
+/* L2-L4 network traffic flow hash options */
#define RXH_L2DA (1 << 1)
#define RXH_VLAN (1 << 2)
#define RXH_L3_PROTO (1 << 3)
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 8f30ffa1cd14..96027e26ffba 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -679,6 +679,39 @@ enum {
};
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,
ETHTOOL_A_RSS_CONTEXT,
@@ -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)
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 139f95620cdd..cccb4694f5e1 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -981,6 +981,7 @@ static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
static bool flow_type_hashable(u32 flow_type)
{
switch (flow_type) {
+ case ETHER_FLOW:
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case SCTP_V4_FLOW:
@@ -1100,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
diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 4e8ca2c38175..41ab9fc67652 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -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)
@@ -64,6 +127,7 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
+ mutex_lock(&dev->ethtool->rss_lock);
data->indir_size = 0;
data->hkey_size = 0;
@@ -77,7 +141,7 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
rss_config = kzalloc(total_size, GFP_KERNEL);
if (!rss_config) {
ret = -ENOMEM;
- goto out_ops;
+ goto out_unlock;
}
if (data->indir_size)
@@ -92,11 +156,12 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
ret = ops->get_rxfh(dev, &rxfh);
if (ret)
- goto out_ops;
+ goto out_unlock;
data->hfunc = rxfh.hfunc;
data->input_xfrm = rxfh.input_xfrm;
-out_ops:
+out_unlock:
+ mutex_unlock(&dev->ethtool->rss_lock);
ethnl_ops_complete(dev);
return ret;
}
@@ -108,12 +173,16 @@ rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev,
struct ethtool_rxfh_context *ctx;
u32 total_size, indir_bytes;
u8 *rss_config;
+ int ret;
data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key;
+ mutex_lock(&dev->ethtool->rss_lock);
ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
- if (!ctx)
- return -ENOENT;
+ if (!ctx) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
data->indir_size = ctx->indir_size;
data->hkey_size = ctx->key_size;
@@ -123,8 +192,10 @@ rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev,
indir_bytes = data->indir_size * sizeof(u32);
total_size = indir_bytes + data->hkey_size;
rss_config = kzalloc(total_size, GFP_KERNEL);
- if (!rss_config)
- return -ENOMEM;
+ if (!rss_config) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
data->indir_table = (u32 *)rss_config;
memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes);
@@ -135,13 +206,18 @@ rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev,
data->hkey_size);
}
- return 0;
+ ret = 0;
+out_unlock:
+ mutex_unlock(&dev->ethtool->rss_lock);
+ return ret;
}
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);
@@ -156,7 +232,6 @@ rss_prepare_data(const struct ethnl_req_info *req_base,
struct rss_req_info *request = RSS_REQINFO(req_base);
struct net_device *dev = reply_base->dev;
const struct ethtool_ops *ops;
- int ret;
ops = dev->ethtool_ops;
if (!ops->get_rxfh)
@@ -166,11 +241,7 @@ rss_prepare_data(const struct ethnl_req_info *req_base,
if (request->rss_context && !ops->create_rxfh_context)
return -EOPNOTSUPP;
- mutex_lock(&dev->ethtool->rss_lock);
- ret = rss_prepare(request, dev, data, info);
- mutex_unlock(&dev->ethtool->rss_lock);
-
- return ret;
+ return rss_prepare(request, dev, data, info);
}
static int
@@ -184,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;
}
@@ -205,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;
}
@@ -294,11 +385,7 @@ rss_dump_one_ctx(struct sk_buff *skb, struct netlink_callback *cb,
if (ret < 0)
goto err_cancel;
- /* Context 0 is not currently storred or cached in the XArray */
- if (!rss_context)
- ret = rss_prepare_get(&req, dev, &data, info);
- else
- ret = rss_prepare_ctx(&req, dev, &data, info);
+ ret = rss_prepare(&req, dev, &data, info);
if (ret)
goto err_cancel;
diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py
index 61deb5923067..7529bce174ff 100644
--- a/tools/net/ynl/pyynl/lib/ynl.py
+++ b/tools/net/ynl/pyynl/lib/ynl.py
@@ -762,6 +762,8 @@ class YnlFamily(SpecFamily):
decoded = True
elif attr_spec.is_auto_scalar:
decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order)
+ if 'enum' in attr_spec:
+ decoded = self._decode_enum(decoded, attr_spec)
elif attr_spec["type"] in NlAttr.type_formats:
decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order)
if 'enum' in attr_spec:
diff --git a/tools/testing/selftests/drivers/net/hw/rss_api.py b/tools/testing/selftests/drivers/net/hw/rss_api.py
index db0f723a674b..6ae908bed1a4 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_api.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_api.py
@@ -20,6 +20,38 @@ def _ethtool_create(cfg, act, opts):
return int(output.split()[-1])
+def _ethtool_get_cfg(cfg, fl_type, to_nl=False):
+ descr = ethtool(f"-n {cfg.ifname} rx-flow-hash {fl_type}").stdout
+
+ if to_nl:
+ converter = {
+ "IP SA": "ip-src",
+ "IP DA": "ip-dst",
+ "L4 bytes 0 & 1 [TCP/UDP src port]": "l4-b-0-1",
+ "L4 bytes 2 & 3 [TCP/UDP dst port]": "l4-b-2-3",
+ }
+
+ ret = set()
+ else:
+ converter = {
+ "IP SA": "s",
+ "IP DA": "d",
+ "L3 proto": "t",
+ "L4 bytes 0 & 1 [TCP/UDP src port]": "f",
+ "L4 bytes 2 & 3 [TCP/UDP dst port]": "n",
+ }
+
+ ret = ""
+
+ for line in descr.split("\n")[1:-2]:
+ # if this raises we probably need to add more keys to converter above
+ if to_nl:
+ ret.add(converter[line])
+ else:
+ ret += converter[line]
+ return ret
+
+
def test_rxfh_indir_ntf(cfg):
"""
Check that Netlink notifications are generated when RSS indirection
@@ -77,6 +109,21 @@ def test_rxfh_indir_ctx_ntf(cfg):
ksft_eq(set(ntf["msg"]["indir"]), {1})
+def test_rxfh_fields(cfg):
+ """
+ Test reading Rx Flow Hash over Netlink.
+ """
+
+ flow_types = ["tcp4", "tcp6", "udp4", "udp6"]
+ ethnl = EthtoolFamily()
+
+ cfg_nl = ethnl.rss_get({"header": {"dev-index": cfg.ifindex}})
+ for fl_type in flow_types:
+ one = _ethtool_get_cfg(cfg, fl_type, to_nl=True)
+ ksft_eq(one, cfg_nl["flow-hash"][fl_type],
+ comment="Config for " + fl_type)
+
+
def main() -> None:
""" Ksft boiler plate main """