]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: ethtool: add dedicated callbacks for getting and setting rxfh fields
authorJakub Kicinski <kuba@kernel.org>
Wed, 11 Jun 2025 14:59:44 +0000 (07:59 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 13 Jun 2025 00:16:20 +0000 (17:16 -0700)
We mux multiple calls to the drivers via the .get_nfc and .set_nfc
callbacks. This is slightly inconvenient to the drivers as they
have to de-mux them back. It will also be awkward for netlink code
to construct struct ethtool_rxnfc when it wants to get info about
RX Flow Hash, from the RSS module.

Add dedicated driver callbacks. Create struct ethtool_rxfh_fields
which contains only data relevant to RXFH. Maintain the names of
the fields to avoid having to heavily modify the drivers.

For now support both callbacks, once all drivers are converted
ethtool_*et_rxfh_fields() will stop using the rxnfc callbacks.

Link: https://patch.msgid.link/20250611145949.2674086-5-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/ethtool.h
net/ethtool/ioctl.c

index 1a6737721d7fe24b47199ab8c8e6c73ea813fe30..59877fd2a1d384cfe5207b2ea7b6a3bca5df80ed 100644 (file)
@@ -825,6 +825,19 @@ struct ethtool_rxfh_param {
        u8      input_xfrm;
 };
 
+/**
+ * struct ethtool_rxfh_fields - Rx Flow Hashing (RXFH) header field config
+ * @data: which header fields are used for hashing, bitmask of RXH_* defines
+ * @flow_type: L2-L4 network traffic flow type
+ * @rss_context: RSS context, will only be used if rxfh_per_ctx_fields is
+ *     set in struct ethtool_ops
+ */
+struct ethtool_rxfh_fields {
+       u32 data;
+       u32 flow_type;
+       u32 rss_context;
+};
+
 /**
  * struct kernel_ethtool_ts_info - kernel copy of struct ethtool_ts_info
  * @cmd: command number = %ETHTOOL_GET_TS_INFO
@@ -970,6 +983,8 @@ struct kernel_ethtool_ts_info {
  *     will remain unchanged.
  *     Returns a negative error code or zero. An error code must be returned
  *     if at least one unsupported change was requested.
+ * @get_rxfh_fields: Get header fields used for flow hashing.
+ * @set_rxfh_fields: Set header fields used for flow hashing.
  * @create_rxfh_context: Create a new RSS context with the specified RX flow
  *     hash indirection table, hash key, and hash function.
  *     The &struct ethtool_rxfh_context for this context is passed in @ctx;
@@ -1156,6 +1171,11 @@ struct ethtool_ops {
        int     (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *);
        int     (*set_rxfh)(struct net_device *, struct ethtool_rxfh_param *,
                            struct netlink_ext_ack *extack);
+       int     (*get_rxfh_fields)(struct net_device *,
+                                  struct ethtool_rxfh_fields *);
+       int     (*set_rxfh_fields)(struct net_device *,
+                                  const struct ethtool_rxfh_fields *,
+                                  struct netlink_ext_ack *extack);
        int     (*create_rxfh_context)(struct net_device *,
                                       struct ethtool_rxfh_context *ctx,
                                       const struct ethtool_rxfh_param *rxfh,
index 1a1705e900b3f503e28b2aa2a3552fb62ce113bb..a14cf901c32d6bdf5fedcb17f2e2f272078af821 100644 (file)
@@ -1048,9 +1048,20 @@ static int ethtool_check_flow_types(struct net_device *dev, u32 input_xfrm)
                        continue;
 
                info.flow_type = i;
-               err = ops->get_rxnfc(dev, &info, NULL);
-               if (err)
-                       continue;
+
+               if (ops->get_rxfh_fields) {
+                       struct ethtool_rxfh_fields fields = {
+                               .flow_type      = info.flow_type,
+                       };
+
+                       if (ops->get_rxfh_fields(dev, &fields))
+                               continue;
+
+                       info.data = fields.data;
+               } else {
+                       if (ops->get_rxnfc(dev, &info, NULL))
+                               continue;
+               }
 
                err = ethtool_check_xfrm_rxfh(input_xfrm, info.data);
                if (err)
@@ -1064,11 +1075,12 @@ static noinline_for_stack int
 ethtool_set_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
 {
        const struct ethtool_ops *ops = dev->ethtool_ops;
+       struct ethtool_rxfh_fields fields = {};
        struct ethtool_rxnfc info;
        size_t info_size = sizeof(info);
        int rc;
 
-       if (!ops->set_rxnfc)
+       if (!ops->set_rxnfc && !ops->set_rxfh_fields)
                return -EOPNOTSUPP;
 
        rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
@@ -1091,7 +1103,15 @@ ethtool_set_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
                        return rc;
        }
 
-       return ops->set_rxnfc(dev, &info);
+       if (!ops->set_rxfh_fields)
+               return ops->set_rxnfc(dev, &info);
+
+       fields.data = info.data;
+       fields.flow_type = info.flow_type & ~FLOW_RSS;
+       if (info.flow_type & FLOW_RSS)
+               fields.rss_context = info.rss_context;
+
+       return ops->set_rxfh_fields(dev, &fields, NULL);
 }
 
 static noinline_for_stack int
@@ -1102,7 +1122,7 @@ ethtool_get_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
        const struct ethtool_ops *ops = dev->ethtool_ops;
        int ret;
 
-       if (!ops->get_rxnfc)
+       if (!ops->get_rxnfc && !ops->get_rxfh_fields)
                return -EOPNOTSUPP;
 
        ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
@@ -1113,9 +1133,24 @@ ethtool_get_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
            !ops->rxfh_per_ctx_fields)
                return -EINVAL;
 
-       ret = ops->get_rxnfc(dev, &info, NULL);
-       if (ret < 0)
-               return ret;
+       if (ops->get_rxfh_fields) {
+               struct ethtool_rxfh_fields fields = {
+                       .flow_type      = info.flow_type & ~FLOW_RSS,
+               };
+
+               if (info.flow_type & FLOW_RSS)
+                       fields.rss_context = info.rss_context;
+
+               ret = ops->get_rxfh_fields(dev, &fields);
+               if (ret < 0)
+                       return ret;
+
+               info.data = fields.data;
+       } else {
+               ret = ops->get_rxnfc(dev, &info, NULL);
+               if (ret < 0)
+                       return ret;
+       }
 
        return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL);
 }
@@ -1493,7 +1528,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
        u8 *rss_config;
        int ret;
 
-       if (!ops->get_rxnfc || !ops->set_rxfh)
+       if ((!ops->get_rxnfc && !ops->get_rxfh_fields) || !ops->set_rxfh)
                return -EOPNOTSUPP;
 
        if (ops->get_rxfh_indir_size)