]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ethtool: rss: report which fields are configured for hashing
authorJakub Kicinski <kuba@kernel.org>
Tue, 8 Jul 2025 22:06:39 +0000 (15:06 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 11 Jul 2025 00:57:49 +0000 (17:57 -0700)
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>
Documentation/netlink/specs/ethtool.yaml
Documentation/networking/ethtool-netlink.rst
include/uapi/linux/ethtool_netlink_generated.h
net/ethtool/ioctl.c
net/ethtool/rss.c

index 49e782a33eb6fd945843f9e9bd64a1b38c1b053f..c38c03c624f056a18bf77bd080e4d9c07d4e89f5 100644 (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:
index 07e9808ebd2c8ebd49d7ad6dfb5cd6e9a4ec97d2..248bc3d93da9505d27887317d964985ef816e6a8 100644 (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``
index 8f30ffa1cd143cc759e4555a9c475c06277ca8ab..96027e26ffbac0f046bee86c2538cfbe3c035d4d 100644 (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)
index 67f6d900a4ee482e6e5d170892ad7138716c0d19..cccb4694f5e18f2105db7112557c556b7594efa7 100644 (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
index 37a7b20fcd07e06f5c10db74846981f883f1d1c9..41ab9fc676527482938c70f4d955c1f4dd299cd1 100644 (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;
 }