diff mbox series

[v4,net-next,3/7] net: ethtool: record custom RSS contexts in the XArray

Message ID 97db46739ad095e0ed50f0dbd90e1b506c2991de.1695838185.git.ecree.xilinx@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series ethtool: track custom RSS contexts in the core | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3216 this patch: 3216
netdev/cc_maintainers warning 2 maintainers not CCed: d-tatianin@yandex-team.ru vladimir.oltean@nxp.com
netdev/build_clang success Errors and warnings before: 1558 this patch: 1558
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 3465 this patch: 3465
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

edward.cree@amd.com Sept. 27, 2023, 6:13 p.m. UTC
From: Edward Cree <ecree.xilinx@gmail.com>

Since drivers are still choosing the context IDs, we have to force the
 XArray to use the ID they've chosen rather than picking one ourselves,
 and handle the case where they give us an ID that's already in use.

Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
---
 include/linux/ethtool.h | 14 ++++++++
 net/ethtool/ioctl.c     | 73 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 85 insertions(+), 2 deletions(-)

Comments

Jacob Keller Sept. 29, 2023, 6:20 p.m. UTC | #1
On 9/27/2023 11:13 AM, edward.cree@amd.com wrote:
> From: Edward Cree <ecree.xilinx@gmail.com>
> 
> Since drivers are still choosing the context IDs, we have to force the
>  XArray to use the ID they've chosen rather than picking one ourselves,
>  and handle the case where they give us an ID that's already in use.
> 
> Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
> ---
>  include/linux/ethtool.h | 14 ++++++++
>  net/ethtool/ioctl.c     | 73 +++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 85 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
> index bb11cb2f477d..229a23571008 100644
> --- a/include/linux/ethtool.h
> +++ b/include/linux/ethtool.h
> @@ -194,6 +194,17 @@ static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
>  	return (u8 *)(ethtool_rxfh_context_indir(ctx) + ctx->indir_size);
>  }
>  
> +static inline size_t ethtool_rxfh_context_size(u32 indir_size, u32 key_size,
> +					       u16 priv_size)
> +{
> +	size_t indir_bytes = array_size(indir_size, sizeof(u32));
> +	size_t flex_len;
> +
> +	flex_len = size_add(size_add(indir_bytes, key_size),
> +			    ALIGN(priv_size, sizeof(u32)));
> +	return struct_size((struct ethtool_rxfh_context *)0, data, flex_len);
> +}
> +

Ah, we already have to use a flexible area to handle the indirection
table, and this way the core is the only one responsible for allocating
and freeing the memory.

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

>  /* declare a link mode bitmap */
>  #define __ETHTOOL_DECLARE_LINK_MODE_MASK(name)		\
>  	DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)
> @@ -731,6 +742,8 @@ struct ethtool_mm_stats {
>   *	will remain unchanged.
>   *	Returns a negative error code or zero. An error code must be returned
>   *	if at least one unsupported change was requested.
> + * @rxfh_priv_size: size of the driver private data area the core should
> + *	allocate for an RSS context.
>   * @get_rxfh_context: Get the contents of the RX flow hash indirection table,
>   *	hash key, and/or hash function assiciated to the given rss context.
>   *	Returns a negative error code or zero.
> @@ -824,6 +837,7 @@ struct ethtool_ops {
>  	u32     cap_link_lanes_supported:1;
>  	u32	supported_coalesce_params;
>  	u32	supported_ring_params;
> +	u16	rxfh_priv_size;
>  	void	(*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
>  	int	(*get_regs_len)(struct net_device *);
>  	void	(*get_regs)(struct net_device *, struct ethtool_regs *, void *);
> diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
> index de78b24fffc9..1d13bc8fbb75 100644
> --- a/net/ethtool/ioctl.c
> +++ b/net/ethtool/ioctl.c
> @@ -1249,6 +1249,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  {
>  	int ret;
>  	const struct ethtool_ops *ops = dev->ethtool_ops;
> +	struct ethtool_rxfh_context *ctx = NULL;
>  	struct ethtool_rxnfc rx_rings;
>  	struct ethtool_rxfh rxfh;
>  	u32 dev_indir_size = 0, dev_key_size = 0, i;
> @@ -1256,7 +1257,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  	u8 *hkey = NULL;
>  	u8 *rss_config;
>  	u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
> -	bool delete = false;
> +	bool create = false, delete = false;
>  
>  	if (!ops->get_rxnfc || !ops->set_rxfh)
>  		return -EOPNOTSUPP;
> @@ -1275,6 +1276,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  	/* Most drivers don't handle rss_context, check it's 0 as well */
>  	if (rxfh.rss_context && !ops->set_rxfh_context)
>  		return -EOPNOTSUPP;
> +	create = rxfh.rss_context == ETH_RXFH_CONTEXT_ALLOC;
>  
>  	/* If either indir, hash key or function is valid, proceed further.
>  	 * Must request at least one change: indir size, hash key or function.
> @@ -1332,13 +1334,42 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  		}
>  	}
>  
> +	if (create) {
> +		if (delete) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ctx = kzalloc(ethtool_rxfh_context_size(dev_indir_size,
> +							dev_key_size,
> +							ops->rxfh_priv_size),
> +			      GFP_KERNEL_ACCOUNT);
> +		if (!ctx) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +		ctx->indir_size = dev_indir_size;
> +		ctx->key_size = dev_key_size;
> +		ctx->hfunc = rxfh.hfunc;
> +		ctx->priv_size = ops->rxfh_priv_size;
> +	} else if (rxfh.rss_context) {
> +		ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
> +		if (!ctx) {
> +			ret = -ENOENT;
> +			goto out;
> +		}
> +	}
> +
>  	if (rxfh.rss_context)
>  		ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
>  					    &rxfh.rss_context, delete);
>  	else
>  		ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
> -	if (ret)
> +	if (ret) {
> +		if (create)
> +			/* failed to create, free our new tracking entry */
> +			kfree(ctx);
>  		goto out;
> +	}
>  
>  	if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
>  			 &rxfh.rss_context, sizeof(rxfh.rss_context)))
> @@ -1351,6 +1382,44 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  		else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
>  			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 (WARN_ON(xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context))) {
> +			/* context ID reused, our tracking is screwed */
> +			kfree(ctx);
> +			goto out;
> +		}
> +		/* Allocate the exact ID the driver gave us */
> +		if (xa_is_err(xa_store(&dev->ethtool->rss_ctx, rxfh.rss_context,
> +				       ctx, GFP_KERNEL))) {
> +			kfree(ctx);
> +			goto out;
> +		}
> +		ctx->indir_no_change = rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE;
> +		ctx->key_no_change = !rxfh.key_size;
> +	}
> +	if (delete) {
> +		WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context) != ctx);
> +		kfree(ctx);
> +	} else if (ctx) {
> +		if (indir) {
> +			for (i = 0; i < dev_indir_size; i++)
> +				ethtool_rxfh_context_indir(ctx)[i] = indir[i];
> +			ctx->indir_no_change = 0;
> +		}
> +		if (hkey) {
> +			memcpy(ethtool_rxfh_context_key(ctx), hkey,
> +			       dev_key_size);
> +			ctx->key_no_change = 0;
> +		}
> +		if (rxfh.hfunc != ETH_RSS_HASH_NO_CHANGE)
> +			ctx->hfunc = rxfh.hfunc;
> +	}
>  
>  out:
>  	kfree(rss_config);
>
Martin Habets Oct. 2, 2023, 10:41 a.m. UTC | #2
On Wed, Sep 27, 2023 at 07:13:34PM +0100, edward.cree@amd.com wrote:
> From: Edward Cree <ecree.xilinx@gmail.com>
> 
> Since drivers are still choosing the context IDs, we have to force the
>  XArray to use the ID they've chosen rather than picking one ourselves,
>  and handle the case where they give us an ID that's already in use.
> 
> Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
> ---
>  include/linux/ethtool.h | 14 ++++++++
>  net/ethtool/ioctl.c     | 73 +++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 85 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
> index bb11cb2f477d..229a23571008 100644
> --- a/include/linux/ethtool.h
> +++ b/include/linux/ethtool.h
> @@ -194,6 +194,17 @@ static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
>  	return (u8 *)(ethtool_rxfh_context_indir(ctx) + ctx->indir_size);
>  }
>  
> +static inline size_t ethtool_rxfh_context_size(u32 indir_size, u32 key_size,
> +					       u16 priv_size)
> +{
> +	size_t indir_bytes = array_size(indir_size, sizeof(u32));
> +	size_t flex_len;
> +
> +	flex_len = size_add(size_add(indir_bytes, key_size),
> +			    ALIGN(priv_size, sizeof(u32)));
> +	return struct_size((struct ethtool_rxfh_context *)0, data, flex_len);
> +}
> +
>  /* declare a link mode bitmap */
>  #define __ETHTOOL_DECLARE_LINK_MODE_MASK(name)		\
>  	DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)
> @@ -731,6 +742,8 @@ struct ethtool_mm_stats {
>   *	will remain unchanged.
>   *	Returns a negative error code or zero. An error code must be returned
>   *	if at least one unsupported change was requested.
> + * @rxfh_priv_size: size of the driver private data area the core should
> + *	allocate for an RSS context.

An odd place to push the documentation. Please keep the ordering the same
as the struct has below.

Martin

>   * @get_rxfh_context: Get the contents of the RX flow hash indirection table,
>   *	hash key, and/or hash function assiciated to the given rss context.
>   *	Returns a negative error code or zero.
> @@ -824,6 +837,7 @@ struct ethtool_ops {
>  	u32     cap_link_lanes_supported:1;
>  	u32	supported_coalesce_params;
>  	u32	supported_ring_params;
> +	u16	rxfh_priv_size;
>  	void	(*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
>  	int	(*get_regs_len)(struct net_device *);
>  	void	(*get_regs)(struct net_device *, struct ethtool_regs *, void *);
> diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
> index de78b24fffc9..1d13bc8fbb75 100644
> --- a/net/ethtool/ioctl.c
> +++ b/net/ethtool/ioctl.c
> @@ -1249,6 +1249,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  {
>  	int ret;
>  	const struct ethtool_ops *ops = dev->ethtool_ops;
> +	struct ethtool_rxfh_context *ctx = NULL;
>  	struct ethtool_rxnfc rx_rings;
>  	struct ethtool_rxfh rxfh;
>  	u32 dev_indir_size = 0, dev_key_size = 0, i;
> @@ -1256,7 +1257,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  	u8 *hkey = NULL;
>  	u8 *rss_config;
>  	u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
> -	bool delete = false;
> +	bool create = false, delete = false;
>  
>  	if (!ops->get_rxnfc || !ops->set_rxfh)
>  		return -EOPNOTSUPP;
> @@ -1275,6 +1276,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  	/* Most drivers don't handle rss_context, check it's 0 as well */
>  	if (rxfh.rss_context && !ops->set_rxfh_context)
>  		return -EOPNOTSUPP;
> +	create = rxfh.rss_context == ETH_RXFH_CONTEXT_ALLOC;
>  
>  	/* If either indir, hash key or function is valid, proceed further.
>  	 * Must request at least one change: indir size, hash key or function.
> @@ -1332,13 +1334,42 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  		}
>  	}
>  
> +	if (create) {
> +		if (delete) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ctx = kzalloc(ethtool_rxfh_context_size(dev_indir_size,
> +							dev_key_size,
> +							ops->rxfh_priv_size),
> +			      GFP_KERNEL_ACCOUNT);
> +		if (!ctx) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +		ctx->indir_size = dev_indir_size;
> +		ctx->key_size = dev_key_size;
> +		ctx->hfunc = rxfh.hfunc;
> +		ctx->priv_size = ops->rxfh_priv_size;
> +	} else if (rxfh.rss_context) {
> +		ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
> +		if (!ctx) {
> +			ret = -ENOENT;
> +			goto out;
> +		}
> +	}
> +
>  	if (rxfh.rss_context)
>  		ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
>  					    &rxfh.rss_context, delete);
>  	else
>  		ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
> -	if (ret)
> +	if (ret) {
> +		if (create)
> +			/* failed to create, free our new tracking entry */
> +			kfree(ctx);
>  		goto out;
> +	}
>  
>  	if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
>  			 &rxfh.rss_context, sizeof(rxfh.rss_context)))
> @@ -1351,6 +1382,44 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
>  		else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
>  			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 (WARN_ON(xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context))) {
> +			/* context ID reused, our tracking is screwed */
> +			kfree(ctx);
> +			goto out;
> +		}
> +		/* Allocate the exact ID the driver gave us */
> +		if (xa_is_err(xa_store(&dev->ethtool->rss_ctx, rxfh.rss_context,
> +				       ctx, GFP_KERNEL))) {
> +			kfree(ctx);
> +			goto out;
> +		}
> +		ctx->indir_no_change = rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE;
> +		ctx->key_no_change = !rxfh.key_size;
> +	}
> +	if (delete) {
> +		WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context) != ctx);
> +		kfree(ctx);
> +	} else if (ctx) {
> +		if (indir) {
> +			for (i = 0; i < dev_indir_size; i++)
> +				ethtool_rxfh_context_indir(ctx)[i] = indir[i];
> +			ctx->indir_no_change = 0;
> +		}
> +		if (hkey) {
> +			memcpy(ethtool_rxfh_context_key(ctx), hkey,
> +			       dev_key_size);
> +			ctx->key_no_change = 0;
> +		}
> +		if (rxfh.hfunc != ETH_RSS_HASH_NO_CHANGE)
> +			ctx->hfunc = rxfh.hfunc;
> +	}
>  
>  out:
>  	kfree(rss_config);
diff mbox series

Patch

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index bb11cb2f477d..229a23571008 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -194,6 +194,17 @@  static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
 	return (u8 *)(ethtool_rxfh_context_indir(ctx) + ctx->indir_size);
 }
 
+static inline size_t ethtool_rxfh_context_size(u32 indir_size, u32 key_size,
+					       u16 priv_size)
+{
+	size_t indir_bytes = array_size(indir_size, sizeof(u32));
+	size_t flex_len;
+
+	flex_len = size_add(size_add(indir_bytes, key_size),
+			    ALIGN(priv_size, sizeof(u32)));
+	return struct_size((struct ethtool_rxfh_context *)0, data, flex_len);
+}
+
 /* declare a link mode bitmap */
 #define __ETHTOOL_DECLARE_LINK_MODE_MASK(name)		\
 	DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)
@@ -731,6 +742,8 @@  struct ethtool_mm_stats {
  *	will remain unchanged.
  *	Returns a negative error code or zero. An error code must be returned
  *	if at least one unsupported change was requested.
+ * @rxfh_priv_size: size of the driver private data area the core should
+ *	allocate for an RSS context.
  * @get_rxfh_context: Get the contents of the RX flow hash indirection table,
  *	hash key, and/or hash function assiciated to the given rss context.
  *	Returns a negative error code or zero.
@@ -824,6 +837,7 @@  struct ethtool_ops {
 	u32     cap_link_lanes_supported:1;
 	u32	supported_coalesce_params;
 	u32	supported_ring_params;
+	u16	rxfh_priv_size;
 	void	(*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
 	int	(*get_regs_len)(struct net_device *);
 	void	(*get_regs)(struct net_device *, struct ethtool_regs *, void *);
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index de78b24fffc9..1d13bc8fbb75 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1249,6 +1249,7 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 {
 	int ret;
 	const struct ethtool_ops *ops = dev->ethtool_ops;
+	struct ethtool_rxfh_context *ctx = NULL;
 	struct ethtool_rxnfc rx_rings;
 	struct ethtool_rxfh rxfh;
 	u32 dev_indir_size = 0, dev_key_size = 0, i;
@@ -1256,7 +1257,7 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 	u8 *hkey = NULL;
 	u8 *rss_config;
 	u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
-	bool delete = false;
+	bool create = false, delete = false;
 
 	if (!ops->get_rxnfc || !ops->set_rxfh)
 		return -EOPNOTSUPP;
@@ -1275,6 +1276,7 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 	/* Most drivers don't handle rss_context, check it's 0 as well */
 	if (rxfh.rss_context && !ops->set_rxfh_context)
 		return -EOPNOTSUPP;
+	create = rxfh.rss_context == ETH_RXFH_CONTEXT_ALLOC;
 
 	/* If either indir, hash key or function is valid, proceed further.
 	 * Must request at least one change: indir size, hash key or function.
@@ -1332,13 +1334,42 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 		}
 	}
 
+	if (create) {
+		if (delete) {
+			ret = -EINVAL;
+			goto out;
+		}
+		ctx = kzalloc(ethtool_rxfh_context_size(dev_indir_size,
+							dev_key_size,
+							ops->rxfh_priv_size),
+			      GFP_KERNEL_ACCOUNT);
+		if (!ctx) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		ctx->indir_size = dev_indir_size;
+		ctx->key_size = dev_key_size;
+		ctx->hfunc = rxfh.hfunc;
+		ctx->priv_size = ops->rxfh_priv_size;
+	} else if (rxfh.rss_context) {
+		ctx = xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context);
+		if (!ctx) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
 	if (rxfh.rss_context)
 		ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
 					    &rxfh.rss_context, delete);
 	else
 		ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
-	if (ret)
+	if (ret) {
+		if (create)
+			/* failed to create, free our new tracking entry */
+			kfree(ctx);
 		goto out;
+	}
 
 	if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
 			 &rxfh.rss_context, sizeof(rxfh.rss_context)))
@@ -1351,6 +1382,44 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 		else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
 			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 (WARN_ON(xa_load(&dev->ethtool->rss_ctx, rxfh.rss_context))) {
+			/* context ID reused, our tracking is screwed */
+			kfree(ctx);
+			goto out;
+		}
+		/* Allocate the exact ID the driver gave us */
+		if (xa_is_err(xa_store(&dev->ethtool->rss_ctx, rxfh.rss_context,
+				       ctx, GFP_KERNEL))) {
+			kfree(ctx);
+			goto out;
+		}
+		ctx->indir_no_change = rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE;
+		ctx->key_no_change = !rxfh.key_size;
+	}
+	if (delete) {
+		WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context) != ctx);
+		kfree(ctx);
+	} else if (ctx) {
+		if (indir) {
+			for (i = 0; i < dev_indir_size; i++)
+				ethtool_rxfh_context_indir(ctx)[i] = indir[i];
+			ctx->indir_no_change = 0;
+		}
+		if (hkey) {
+			memcpy(ethtool_rxfh_context_key(ctx), hkey,
+			       dev_key_size);
+			ctx->key_no_change = 0;
+		}
+		if (rxfh.hfunc != ETH_RSS_HASH_NO_CHANGE)
+			ctx->hfunc = rxfh.hfunc;
+	}
 
 out:
 	kfree(rss_config);