]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ethtool: rss: support removing contexts via Netlink
authorJakub Kicinski <kuba@kernel.org>
Thu, 17 Jul 2025 23:43:42 +0000 (16:43 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 22 Jul 2025 01:21:19 +0000 (18:21 -0700)
Implement removing additional RSS contexts via Netlink.
Technically it'd be possible to shoehorn the delete operation
into ethnl_request_ops-compatible handler. The code ends
up longer than open coded version, and I think we'll need
a custom way of sending notifications at some stage (if we
allow tying the context lifetime to the netlink socket, in
the future).

Link: https://patch.msgid.link/20250717234343.2328602-8-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/common.c
net/ethtool/ioctl.c
net/ethtool/netlink.c
net/ethtool/netlink.h
net/ethtool/rss.c

index 25ffed5fddd5c5c00587c283bcdecbf8e4108780..1063d5d32fea2557a40dfcbad182b83efa0a62a2 100644 (file)
@@ -2706,6 +2706,24 @@ operations:
       doc: |
         Notification for creation of an additional RSS context.
       notify: rss-create-act
+    -
+      name: rss-delete-act
+      doc: Delete an RSS context.
+      attribute-set: rss
+      do:
+        request:
+          attributes:
+            - header
+            - context
+    -
+      name: rss-delete-ntf
+      doc: |
+        Notification for deletion of an additional RSS context.
+      attribute-set: rss
+      event:
+        attributes:
+          - header
+          - context
 
 mcast-groups:
   list:
index 2646fafb85129536ceb74318eb51d0131f412f68..ab20c644af2485ae9cfc049475c6fb36f94cc844 100644 (file)
@@ -241,6 +241,7 @@ Userspace to kernel:
   ``ETHTOOL_MSG_TSCONFIG_SET``          set hw timestamping configuration
   ``ETHTOOL_MSG_RSS_SET``               set RSS settings
   ``ETHTOOL_MSG_RSS_CREATE_ACT``        create an additional RSS context
+  ``ETHTOOL_MSG_RSS_DELETE_ACT``        delete an additional RSS context
   ===================================== =================================
 
 Kernel to userspace:
@@ -297,6 +298,7 @@ Kernel to userspace:
   ``ETHTOOL_MSG_RSS_NTF``                  RSS settings notification
   ``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY``     create an additional RSS context
   ``ETHTOOL_MSG_RSS_CREATE_NTF``           additional RSS context created
+  ``ETHTOOL_MSG_RSS_DELETE_NTF``           additional RSS context deleted
   ======================================== =================================
 
 ``GET`` requests are sent by userspace applications to retrieve device
@@ -2041,6 +2043,18 @@ Kernel response contents:
 Create an additional RSS context, if ``ETHTOOL_A_RSS_CONTEXT`` is not
 specified kernel will allocate one automatically.
 
+RSS_DELETE_ACT
+==============
+
+Request contents:
+
+=====================================  ======  ==============================
+  ``ETHTOOL_A_RSS_HEADER``             nested  request header
+  ``ETHTOOL_A_RSS_CONTEXT``            u32     context number
+=====================================  ======  ==============================
+
+Delete an additional RSS context.
+
 PLCA_GET_CFG
 ============
 
index dea77abd295fcfe9126e987ea663d92f3676e353..e3b8813465d737fc1c407db19f7610afb3de3bf4 100644 (file)
@@ -842,6 +842,7 @@ enum {
        ETHTOOL_MSG_TSCONFIG_SET,
        ETHTOOL_MSG_RSS_SET,
        ETHTOOL_MSG_RSS_CREATE_ACT,
+       ETHTOOL_MSG_RSS_DELETE_ACT,
 
        __ETHTOOL_MSG_USER_CNT,
        ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -901,6 +902,7 @@ enum {
        ETHTOOL_MSG_RSS_NTF,
        ETHTOOL_MSG_RSS_CREATE_ACT_REPLY,
        ETHTOOL_MSG_RSS_CREATE_NTF,
+       ETHTOOL_MSG_RSS_DELETE_NTF,
 
        __ETHTOOL_MSG_KERNEL_CNT,
        ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
index 2a1d40efb1fcc9d3398ddf1e5ed812b063651deb..4f58648a27ad6317360fec4fd68593a00910c249 100644 (file)
@@ -1136,5 +1136,6 @@ void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id)
        netdev_err(dev, "device error, RSS context %d lost\n", context_id);
        ctx = xa_erase(&dev->ethtool->rss_ctx, context_id);
        kfree(ctx);
+       ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_DELETE_NTF, context_id);
 }
 EXPORT_SYMBOL(ethtool_rxfh_context_lost);
index 4b586b0f18e8508fae5c6ea1b4319b77fa02e0f5..43a7854e784ef0c42022b172ff8297774b83fa16 100644 (file)
@@ -1647,6 +1647,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
                             !memchr_inv(ethtool_rxfh_context_key(ctx), 0,
                                         ctx->key_size));
        } else if (rxfh_dev.rss_delete) {
+               ntf = ETHTOOL_MSG_RSS_DELETE_NTF;
                ret = ops->remove_rxfh_context(dev, ctx, rxfh.rss_context,
                                               extack);
        } else {
index e9696113a96b43694cc4d7977ba48becf49776b7..2f813f25f07e1af3d927caf3947f48060f275f4c 100644 (file)
@@ -1527,6 +1527,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
                .policy = ethnl_rss_create_policy,
                .maxattr = ARRAY_SIZE(ethnl_rss_create_policy) - 1,
        },
+       {
+               .cmd    = ETHTOOL_MSG_RSS_DELETE_ACT,
+               .flags  = GENL_UNS_ADMIN_PERM,
+               .doit   = ethnl_rss_delete_doit,
+               .policy = ethnl_rss_delete_policy,
+               .maxattr = ARRAY_SIZE(ethnl_rss_delete_policy) - 1,
+       },
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
index b530bf9f85ee7c0aa5969fa1342b870796acc462..1d4f9ecb3d263bd200d472275b458847841b389c 100644 (file)
@@ -487,6 +487,7 @@ 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_FLOW_HASH + 1];
 extern const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1];
+extern const struct nla_policy ethnl_rss_delete_policy[ETHTOOL_A_RSS_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];
@@ -510,6 +511,7 @@ int ethnl_tsinfo_start(struct netlink_callback *cb);
 int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
 int ethnl_tsinfo_done(struct netlink_callback *cb);
 int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info);
+int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info);
 
 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
index be092dfa4407fae13270b302b16c92f9985136a8..992e98abe9ddb919878e6482ab95aaa315cdcfeb 100644 (file)
@@ -486,13 +486,49 @@ int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 
 /* RSS_NTF */
 
+static void ethnl_rss_delete_notify(struct net_device *dev, u32 rss_context)
+{
+       struct sk_buff *ntf;
+       size_t ntf_size;
+       void *hdr;
+
+       ntf_size = ethnl_reply_header_size() +
+               nla_total_size(sizeof(u32));    /* _RSS_CONTEXT */
+
+       ntf = genlmsg_new(ntf_size, GFP_KERNEL);
+       if (!ntf)
+               goto out_warn;
+
+       hdr = ethnl_bcastmsg_put(ntf, ETHTOOL_MSG_RSS_DELETE_NTF);
+       if (!hdr)
+               goto out_free_ntf;
+
+       if (ethnl_fill_reply_header(ntf, dev, ETHTOOL_A_RSS_HEADER) ||
+           nla_put_u32(ntf, ETHTOOL_A_RSS_CONTEXT, rss_context))
+               goto out_free_ntf;
+
+       genlmsg_end(ntf, hdr);
+       if (ethnl_multicast(ntf, dev))
+               goto out_warn;
+
+       return;
+
+out_free_ntf:
+       nlmsg_free(ntf);
+out_warn:
+       pr_warn_once("Failed to send a RSS delete notification");
+}
+
 void ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context)
 {
        struct rss_req_info req_info = {
                .rss_context = rss_context,
        };
 
-       ethnl_notify(dev, type, &req_info.base);
+       if (type == ETHTOOL_MSG_RSS_DELETE_NTF)
+               ethnl_rss_delete_notify(dev, rss_context);
+       else
+               ethnl_notify(dev, type, &req_info.base);
 }
 
 /* RSS_SET */
@@ -1096,3 +1132,74 @@ err_unlock_free_ctx:
        kfree(ctx);
        goto exit_unlock;
 }
+
+/* RSS_DELETE */
+
+const struct nla_policy ethnl_rss_delete_policy[ETHTOOL_A_RSS_CONTEXT + 1] = {
+       [ETHTOOL_A_RSS_HEADER]  = NLA_POLICY_NESTED(ethnl_header_policy),
+       [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
+};
+
+int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info)
+{
+       struct ethtool_rxfh_context *ctx;
+       struct nlattr **tb = info->attrs;
+       struct ethnl_req_info req = {};
+       const struct ethtool_ops *ops;
+       struct net_device *dev;
+       u32 rss_context;
+       int ret;
+
+       if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_RSS_CONTEXT))
+               return -EINVAL;
+       rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]);
+
+       ret = ethnl_parse_header_dev_get(&req, tb[ETHTOOL_A_RSS_HEADER],
+                                        genl_info_net(info), info->extack,
+                                        true);
+       if (ret < 0)
+               return ret;
+
+       dev = req.dev;
+       ops = dev->ethtool_ops;
+
+       if (!ops->create_rxfh_context)
+               goto exit_free_dev;
+
+       rtnl_lock();
+       netdev_lock_ops(dev);
+
+       ret = ethnl_ops_begin(dev);
+       if (ret < 0)
+               goto exit_dev_unlock;
+
+       mutex_lock(&dev->ethtool->rss_lock);
+       ret = ethtool_check_rss_ctx_busy(dev, rss_context);
+       if (ret)
+               goto exit_unlock;
+
+       ctx = xa_load(&dev->ethtool->rss_ctx, rss_context);
+       if (!ctx) {
+               ret = -ENOENT;
+               goto exit_unlock;
+       }
+
+       ret = ops->remove_rxfh_context(dev, ctx, rss_context, info->extack);
+       if (ret)
+               goto exit_unlock;
+
+       WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rss_context) != ctx);
+       kfree(ctx);
+
+       ethnl_rss_delete_notify(dev, rss_context);
+
+exit_unlock:
+       mutex_unlock(&dev->ethtool->rss_lock);
+       ethnl_ops_complete(dev);
+exit_dev_unlock:
+       netdev_unlock_ops(dev);
+       rtnl_unlock();
+exit_free_dev:
+       ethnl_parse_header_dev_put(&req);
+       return ret;
+}