net: ethtool: let the core choose RSS context IDs
authorEdward Cree <ecree.xilinx@gmail.com>
Thu, 27 Jun 2024 15:33:49 +0000 (16:33 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 29 Jun 2024 01:53:21 +0000 (18:53 -0700)
Add a new API to create/modify/remove RSS contexts, that passes in the
 newly-chosen context ID (not as a pointer) rather than leaving the
 driver to choose it on create.  Also pass in the ctx, allowing drivers
 to easily use its private data area to store their hardware-specific
 state.
Keep the existing .set_rxfh API for now as a fallback, but deprecate it
 for custom contexts (rss_context != 0).

Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Link: https://patch.msgid.link/45f1fe61df2163c091ec394c9f52000c8b16cc3b.1719502240.git.ecree.xilinx@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/ethtool.h
net/core/dev.c
net/ethtool/ioctl.c

index 43a2a14..4292a25 100644 (file)
@@ -723,6 +723,10 @@ struct ethtool_rxfh_param {
  *     RSS.
  * @rxfh_priv_size: size of the driver private data area the core should
  *     allocate for an RSS context (in &struct ethtool_rxfh_context).
+ * @rxfh_max_context_id: maximum (exclusive) supported RSS context ID.  If this
+ *     is zero then the core may choose any (nonzero) ID, otherwise the core
+ *     will only use IDs strictly less than this value, as the @rss_context
+ *     argument to @create_rxfh_context and friends.
  * @supported_coalesce_params: supported types of interrupt coalescing.
  * @supported_ring_params: supported ring params.
  * @get_drvinfo: Report driver/device information. Modern drivers no
@@ -819,6 +823,32 @@ struct ethtool_rxfh_param {
  *     will remain unchanged.
  *     Returns a negative error code or zero. An error code must be returned
  *     if at least one unsupported change was requested.
+ * @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;
+ *     note that the indir table, hkey and hfunc are not yet populated as
+ *     of this call.  The driver does not need to update these; the core
+ *     will do so if this op succeeds.
+ *     However, if @rxfh.indir is set to %NULL, the driver must update the
+ *     indir table in @ctx with the (default or inherited) table actually in
+ *     use; similarly, if @rxfh.key is %NULL, @rxfh.hfunc is
+ *     %ETH_RSS_HASH_NO_CHANGE, or @rxfh.input_xfrm is %RXH_XFRM_NO_CHANGE,
+ *     the driver should update the corresponding information in @ctx.
+ *     If the driver provides this method, it must also provide
+ *     @modify_rxfh_context and @remove_rxfh_context.
+ *     Returns a negative error code or zero.
+ * @modify_rxfh_context: Reconfigure the specified RSS context.  Allows setting
+ *     the contents of the RX flow hash indirection table, hash key, and/or
+ *     hash function associated with the given context.
+ *     Parameters which are set to %NULL or zero will remain unchanged.
+ *     The &struct ethtool_rxfh_context for this context is passed in @ctx;
+ *     note that it will still contain the *old* settings.  The driver does
+ *     not need to update these; the core will do so if this op succeeds.
+ *     Returns a negative error code or zero. An error code must be returned
+ *     if at least one unsupported change was requested.
+ * @remove_rxfh_context: Remove the specified RSS context.
+ *     The &struct ethtool_rxfh_context for this context is passed in @ctx.
+ *     Returns a negative error code or zero.
  * @get_channels: Get number of channels.
  * @set_channels: Set number of channels.  Returns a negative error code or
  *     zero.
@@ -909,6 +939,7 @@ struct ethtool_ops {
        u32     cap_rss_ctx_supported:1;
        u32     cap_rss_sym_xor_supported:1;
        u16     rxfh_priv_size;
+       u32     rxfh_max_context_id;
        u32     supported_coalesce_params;
        u32     supported_ring_params;
        void    (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
@@ -971,6 +1002,15 @@ 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     (*create_rxfh_context)(struct net_device *,
+                                      struct ethtool_rxfh_context *ctx,
+                                      const struct ethtool_rxfh_param *rxfh);
+       int     (*modify_rxfh_context)(struct net_device *,
+                                      struct ethtool_rxfh_context *ctx,
+                                      const struct ethtool_rxfh_param *rxfh);
+       int     (*remove_rxfh_context)(struct net_device *,
+                                      struct ethtool_rxfh_context *ctx,
+                                      u32 rss_context);
        void    (*get_channels)(struct net_device *, struct ethtool_channels *);
        int     (*set_channels)(struct net_device *, struct ethtool_channels *);
        int     (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
index bce810f..8481dc4 100644 (file)
@@ -11254,7 +11254,11 @@ static void netdev_rss_contexts_free(struct net_device *dev)
                rxfh.rss_delete = true;
 
                xa_erase(&dev->ethtool->rss_ctx, context);
-               dev->ethtool_ops->set_rxfh(dev, &rxfh, NULL);
+               if (dev->ethtool_ops->create_rxfh_context)
+                       dev->ethtool_ops->remove_rxfh_context(dev, ctx,
+                                                             context);
+               else
+                       dev->ethtool_ops->set_rxfh(dev, &rxfh, NULL);
                kfree(ctx);
        }
        xa_destroy(&dev->ethtool->rss_ctx);
index e901a20..46b50dd 100644 (file)
@@ -1395,9 +1395,24 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
                }
                ctx->indir_size = dev_indir_size;
                ctx->key_size = dev_key_size;
-               ctx->hfunc = rxfh.hfunc;
-               ctx->input_xfrm = rxfh.input_xfrm;
                ctx->priv_size = ops->rxfh_priv_size;
+               /* Initialise to an empty context */
+               ctx->hfunc = ETH_RSS_HASH_NO_CHANGE;
+               ctx->input_xfrm = RXH_XFRM_NO_CHANGE;
+               if (ops->create_rxfh_context) {
+                       u32 limit = ops->rxfh_max_context_id ?: U32_MAX;
+                       u32 ctx_id;
+
+                       /* driver uses new API, core allocates ID */
+                       ret = xa_alloc(&dev->ethtool->rss_ctx, &ctx_id, ctx,
+                                      XA_LIMIT(1, limit), GFP_KERNEL_ACCOUNT);
+                       if (ret < 0) {
+                               kfree(ctx);
+                               goto out;
+                       }
+                       WARN_ON(!ctx_id); /* can't happen */
+                       rxfh.rss_context = ctx_id;
+               }
        } else if (rxfh.rss_context) {
                ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
                if (!ctx) {
@@ -1409,11 +1424,24 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
        rxfh_dev.rss_context = rxfh.rss_context;
        rxfh_dev.input_xfrm = rxfh.input_xfrm;
 
-       ret = ops->set_rxfh(dev, &rxfh_dev, extack);
-       if (ret) {
+       if (rxfh.rss_context && ops->create_rxfh_context) {
                if (create)
+                       ret = ops->create_rxfh_context(dev, ctx, &rxfh_dev);
+               else if (rxfh_dev.rss_delete)
+                       ret = ops->remove_rxfh_context(dev, ctx,
+                                                      rxfh.rss_context);
+               else
+                       ret = ops->modify_rxfh_context(dev, ctx, &rxfh_dev);
+       } else {
+               ret = ops->set_rxfh(dev, &rxfh_dev, extack);
+       }
+       if (ret) {
+               if (create) {
                        /* failed to create, free our new tracking entry */
+                       if (ops->create_rxfh_context)
+                               xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context);
                        kfree(ctx);
+               }
                goto out;
        }
 
@@ -1429,12 +1457,8 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
                        dev->priv_flags |= IFF_RXFH_CONFIGURED;
        }
        /* Update rss_ctx tracking */
-       if (create) {
-               /* Ideally this should happen before calling the driver,
-                * so that we can fail more cleanly; but we don't have the
-                * context ID until the driver picks it, so we have to
-                * wait until after.
-                */
+       if (create && !ops->create_rxfh_context) {
+               /* driver uses old API, it chose context ID */
                if (WARN_ON(xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context))) {
                        /* context ID reused, our tracking is screwed */
                        kfree(ctx);
@@ -1446,8 +1470,6 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
                        kfree(ctx);
                        goto out;
                }
-               ctx->indir_configured = rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE;
-               ctx->key_configured = !!rxfh.key_size;
        }
        if (rxfh_dev.rss_delete) {
                WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context) != ctx);