[PATCH net-next v2 02/12] ethtool: Convert per-PHY commands to dump_one_dev

Björn Töpel posted 12 patches 1 week, 1 day ago
[PATCH net-next v2 02/12] ethtool: Convert per-PHY commands to dump_one_dev
Posted by Björn Töpel 1 week, 1 day ago
Convert PSE, PLCA, PHY, and MSE commands from the separate
ethnl_perphy_{start,dumpit,done} handlers to use the generic
dump_one_dev callback. This removes the per-PHY specific dump
infrastructure (ethnl_perphy_dump_ctx, ethnl_perphy_dump_context,
ethnl_perphy_start, ethnl_perphy_dumpit, ethnl_perphy_done, and the
internal helpers) in favor of a shared ethnl_perphy_dump_one_dev()
function.

Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
 net/ethtool/mse.c     |   1 +
 net/ethtool/netlink.c | 194 ++++++------------------------------------
 net/ethtool/netlink.h |   4 +
 net/ethtool/phy.c     |   1 +
 net/ethtool/plca.c    |   2 +
 net/ethtool/pse-pd.c  |   1 +
 6 files changed, 35 insertions(+), 168 deletions(-)

diff --git a/net/ethtool/mse.c b/net/ethtool/mse.c
index e91b74430f76..3f33182283ce 100644
--- a/net/ethtool/mse.c
+++ b/net/ethtool/mse.c
@@ -325,4 +325,5 @@ const struct ethnl_request_ops ethnl_mse_request_ops = {
 	.cleanup_data = mse_cleanup_data,
 	.reply_size = mse_reply_size,
 	.fill_reply = mse_fill_reply,
+	.dump_one_dev = ethnl_perphy_dump_one_dev,
 };
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 8d161f0882d0..edeeca67918a 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -399,12 +399,6 @@ static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
 	return (struct ethnl_dump_ctx *)cb->ctx;
 }
 
-static struct ethnl_perphy_dump_ctx *
-ethnl_perphy_dump_context(struct netlink_callback *cb)
-{
-	return (struct ethnl_perphy_dump_ctx *)cb->ctx;
-}
-
 /**
  * ethnl_default_parse() - Parse request message
  * @req_info:    pointer to structure to put data into
@@ -686,169 +680,33 @@ static int ethnl_default_start(struct netlink_callback *cb)
 	return ret;
 }
 
-/* per-PHY ->start() handler for GET requests */
-static int ethnl_perphy_start(struct netlink_callback *cb)
+/* Shared dump_one_dev for per-PHY commands (PSE, PLCA, PHY, MSE) */
+int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
+			      struct ethnl_dump_ctx *ctx,
+			      unsigned long *pos_sub,
+			      const struct genl_info *info)
 {
-	struct ethnl_perphy_dump_ctx *phy_ctx = ethnl_perphy_dump_context(cb);
-	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
-	struct ethnl_dump_ctx *ctx = &phy_ctx->ethnl_ctx;
-	struct ethnl_reply_data *reply_data;
-	const struct ethnl_request_ops *ops;
-	struct ethnl_req_info *req_info;
-	struct genlmsghdr *ghdr;
-	int ret;
-
-	BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
-
-	ghdr = nlmsg_data(cb->nlh);
-	ops = ethnl_default_requests[ghdr->cmd];
-	if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd))
-		return -EOPNOTSUPP;
-	req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
-	if (!req_info)
-		return -ENOMEM;
-	reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
-	if (!reply_data) {
-		ret = -ENOMEM;
-		goto free_req_info;
-	}
-
-	/* Unlike per-dev dump, don't ignore dev. The dump handler
-	 * will notice it and dump PHYs from given dev. We only keep track of
-	 * the dev's ifindex, .dumpit() will grab and release the netdev itself.
-	 */
-	ret = ethnl_default_parse(req_info, &info->info, ops, false);
-	if (ret < 0)
-		goto free_reply_data;
-	if (req_info->dev) {
-		phy_ctx->ifindex = req_info->dev->ifindex;
-		netdev_put(req_info->dev, &req_info->dev_tracker);
-		req_info->dev = NULL;
-	}
-
-	ctx->ops = ops;
-	ctx->req_info = req_info;
-	ctx->reply_data = reply_data;
-	ctx->pos_ifindex = 0;
-
-	return 0;
-
-free_reply_data:
-	kfree(reply_data);
-free_req_info:
-	kfree(req_info);
-
-	return ret;
-}
-
-static int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
-				     struct ethnl_perphy_dump_ctx *ctx,
-				     const struct genl_info *info)
-{
-	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
-	struct net_device *dev = ethnl_ctx->req_info->dev;
+	struct net_device *dev = ctx->req_info->dev;
 	struct phy_device_node *pdn;
 	int ret;
 
 	if (!dev->link_topo)
 		return 0;
 
-	xa_for_each_start(&dev->link_topo->phys, ctx->pos_phyindex, pdn,
-			  ctx->pos_phyindex) {
-		ethnl_ctx->req_info->phy_index = ctx->pos_phyindex;
+	xa_for_each_start(&dev->link_topo->phys, *pos_sub, pdn,
+			  *pos_sub) {
+		ctx->req_info->phy_index = *pos_sub;
 
 		/* We can re-use the original dump_one as ->prepare_data in
 		 * commands use ethnl_req_get_phydev(), which gets the PHY from
 		 * the req_info->phy_index
 		 */
-		ret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info);
+		ret = ethnl_default_dump_one(skb, dev, ctx, info);
 		if (ret)
 			return ret;
 	}
 
-	ctx->pos_phyindex = 0;
-
-	return 0;
-}
-
-static int ethnl_perphy_dump_all_dev(struct sk_buff *skb,
-				     struct ethnl_perphy_dump_ctx *ctx,
-				     const struct genl_info *info)
-{
-	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
-	struct net *net = sock_net(skb->sk);
-	netdevice_tracker dev_tracker;
-	struct net_device *dev;
-	int ret = 0;
-
-	rcu_read_lock();
-	for_each_netdev_dump(net, dev, ethnl_ctx->pos_ifindex) {
-		netdev_hold(dev, &dev_tracker, GFP_ATOMIC);
-		rcu_read_unlock();
-
-		/* per-PHY commands use ethnl_req_get_phydev(), which needs the
-		 * net_device in the req_info
-		 */
-		ethnl_ctx->req_info->dev = dev;
-		ret = ethnl_perphy_dump_one_dev(skb, ctx, info);
-
-		rcu_read_lock();
-		netdev_put(dev, &dev_tracker);
-		ethnl_ctx->req_info->dev = NULL;
-
-		if (ret < 0 && ret != -EOPNOTSUPP) {
-			if (likely(skb->len))
-				ret = skb->len;
-			break;
-		}
-		ret = 0;
-	}
-	rcu_read_unlock();
-
-	return ret;
-}
-
-/* per-PHY ->dumpit() handler for GET requests. */
-static int ethnl_perphy_dumpit(struct sk_buff *skb,
-			       struct netlink_callback *cb)
-{
-	struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb);
-	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
-	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
-	int ret = 0;
-
-	if (ctx->ifindex) {
-		netdevice_tracker dev_tracker;
-		struct net_device *dev;
-
-		dev = netdev_get_by_index(genl_info_net(&info->info),
-					  ctx->ifindex, &dev_tracker,
-					  GFP_KERNEL);
-		if (!dev)
-			return -ENODEV;
-
-		ethnl_ctx->req_info->dev = dev;
-		ret = ethnl_perphy_dump_one_dev(skb, ctx, genl_info_dump(cb));
-
-		if (ret < 0 && ret != -EOPNOTSUPP && likely(skb->len))
-			ret = skb->len;
-
-		netdev_put(dev, &dev_tracker);
-	} else {
-		ret = ethnl_perphy_dump_all_dev(skb, ctx, genl_info_dump(cb));
-	}
-
-	return ret;
-}
-
-/* per-PHY ->done() handler for GET requests */
-static int ethnl_perphy_done(struct netlink_callback *cb)
-{
-	struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb);
-	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
-
-	kfree(ethnl_ctx->reply_data);
-	kfree(ethnl_ctx->req_info);
+	*pos_sub = 0;
 
 	return 0;
 }
@@ -1410,9 +1268,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
 	{
 		.cmd	= ETHTOOL_MSG_PSE_GET,
 		.doit	= ethnl_default_doit,
-		.start	= ethnl_perphy_start,
-		.dumpit	= ethnl_perphy_dumpit,
-		.done	= ethnl_perphy_done,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
 		.policy = ethnl_pse_get_policy,
 		.maxattr = ARRAY_SIZE(ethnl_pse_get_policy) - 1,
 	},
@@ -1434,9 +1292,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
 	{
 		.cmd	= ETHTOOL_MSG_PLCA_GET_CFG,
 		.doit	= ethnl_default_doit,
-		.start	= ethnl_perphy_start,
-		.dumpit	= ethnl_perphy_dumpit,
-		.done	= ethnl_perphy_done,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
 		.policy = ethnl_plca_get_cfg_policy,
 		.maxattr = ARRAY_SIZE(ethnl_plca_get_cfg_policy) - 1,
 	},
@@ -1450,9 +1308,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
 	{
 		.cmd	= ETHTOOL_MSG_PLCA_GET_STATUS,
 		.doit	= ethnl_default_doit,
-		.start	= ethnl_perphy_start,
-		.dumpit	= ethnl_perphy_dumpit,
-		.done	= ethnl_perphy_done,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
 		.policy = ethnl_plca_get_status_policy,
 		.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
 	},
@@ -1482,9 +1340,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
 	{
 		.cmd	= ETHTOOL_MSG_PHY_GET,
 		.doit	= ethnl_default_doit,
-		.start	= ethnl_perphy_start,
-		.dumpit	= ethnl_perphy_dumpit,
-		.done	= ethnl_perphy_done,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
 		.policy = ethnl_phy_get_policy,
 		.maxattr = ARRAY_SIZE(ethnl_phy_get_policy) - 1,
 	},
@@ -1528,9 +1386,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
 	{
 		.cmd	= ETHTOOL_MSG_MSE_GET,
 		.doit	= ethnl_default_doit,
-		.start	= ethnl_perphy_start,
-		.dumpit	= ethnl_perphy_dumpit,
-		.done	= ethnl_perphy_done,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
 		.policy = ethnl_mse_get_policy,
 		.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
 	},
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index e01adc5db02f..dda2f5593ed9 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -546,6 +546,10 @@ 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);
+int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
+			      struct ethnl_dump_ctx *ctx,
+			      unsigned long *pos_sub,
+			      const 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];
diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c
index d4e6887055ab..4bb23a5d6ad5 100644
--- a/net/ethtool/phy.c
+++ b/net/ethtool/phy.c
@@ -162,4 +162,5 @@ const struct ethnl_request_ops ethnl_phy_request_ops = {
 	.reply_size		= phy_reply_size,
 	.fill_reply		= phy_fill_reply,
 	.cleanup_data		= phy_cleanup_data,
+	.dump_one_dev		= ethnl_perphy_dump_one_dev,
 };
diff --git a/net/ethtool/plca.c b/net/ethtool/plca.c
index 91f0c4233298..84e902532617 100644
--- a/net/ethtool/plca.c
+++ b/net/ethtool/plca.c
@@ -188,6 +188,7 @@ const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
 	.prepare_data		= plca_get_cfg_prepare_data,
 	.reply_size		= plca_get_cfg_reply_size,
 	.fill_reply		= plca_get_cfg_fill_reply,
+	.dump_one_dev		= ethnl_perphy_dump_one_dev,
 
 	.set			= ethnl_set_plca,
 	.set_ntf_cmd		= ETHTOOL_MSG_PLCA_NTF,
@@ -268,4 +269,5 @@ const struct ethnl_request_ops ethnl_plca_status_request_ops = {
 	.prepare_data		= plca_get_status_prepare_data,
 	.reply_size		= plca_get_status_reply_size,
 	.fill_reply		= plca_get_status_fill_reply,
+	.dump_one_dev		= ethnl_perphy_dump_one_dev,
 };
diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index 2eb9bdc2dcb9..83f0205053a3 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -338,6 +338,7 @@ const struct ethnl_request_ops ethnl_pse_request_ops = {
 	.reply_size		= pse_reply_size,
 	.fill_reply		= pse_fill_reply,
 	.cleanup_data		= pse_cleanup_data,
+	.dump_one_dev		= ethnl_perphy_dump_one_dev,
 
 	.set			= ethnl_set_pse,
 	/* PSE has no notification */
-- 
2.53.0

Re: [PATCH net-next v2 02/12] ethtool: Convert per-PHY commands to dump_one_dev
Posted by Maxime Chevallier 1 week, 1 day ago
Hi Björn

On 25/03/2026 15:50, Björn Töpel wrote:
> Convert PSE, PLCA, PHY, and MSE commands from the separate
> ethnl_perphy_{start,dumpit,done} handlers to use the generic
> dump_one_dev callback. This removes the per-PHY specific dump
> infrastructure (ethnl_perphy_dump_ctx, ethnl_perphy_dump_context,
> ethnl_perphy_start, ethnl_perphy_dumpit, ethnl_perphy_done, and the
> internal helpers) in favor of a shared ethnl_perphy_dump_one_dev()
> function.
> 
> Signed-off-by: Björn Töpel <bjorn@kernel.org>

Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>

Maxime

> ---
>  net/ethtool/mse.c     |   1 +
>  net/ethtool/netlink.c | 194 ++++++------------------------------------
>  net/ethtool/netlink.h |   4 +
>  net/ethtool/phy.c     |   1 +
>  net/ethtool/plca.c    |   2 +
>  net/ethtool/pse-pd.c  |   1 +
>  6 files changed, 35 insertions(+), 168 deletions(-)
> 
> diff --git a/net/ethtool/mse.c b/net/ethtool/mse.c
> index e91b74430f76..3f33182283ce 100644
> --- a/net/ethtool/mse.c
> +++ b/net/ethtool/mse.c
> @@ -325,4 +325,5 @@ const struct ethnl_request_ops ethnl_mse_request_ops = {
>  	.cleanup_data = mse_cleanup_data,
>  	.reply_size = mse_reply_size,
>  	.fill_reply = mse_fill_reply,
> +	.dump_one_dev = ethnl_perphy_dump_one_dev,
>  };
> diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
> index 8d161f0882d0..edeeca67918a 100644
> --- a/net/ethtool/netlink.c
> +++ b/net/ethtool/netlink.c
> @@ -399,12 +399,6 @@ static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
>  	return (struct ethnl_dump_ctx *)cb->ctx;
>  }
>  
> -static struct ethnl_perphy_dump_ctx *
> -ethnl_perphy_dump_context(struct netlink_callback *cb)
> -{
> -	return (struct ethnl_perphy_dump_ctx *)cb->ctx;
> -}
> -
>  /**
>   * ethnl_default_parse() - Parse request message
>   * @req_info:    pointer to structure to put data into
> @@ -686,169 +680,33 @@ static int ethnl_default_start(struct netlink_callback *cb)
>  	return ret;
>  }
>  
> -/* per-PHY ->start() handler for GET requests */
> -static int ethnl_perphy_start(struct netlink_callback *cb)
> +/* Shared dump_one_dev for per-PHY commands (PSE, PLCA, PHY, MSE) */
> +int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
> +			      struct ethnl_dump_ctx *ctx,
> +			      unsigned long *pos_sub,
> +			      const struct genl_info *info)
>  {
> -	struct ethnl_perphy_dump_ctx *phy_ctx = ethnl_perphy_dump_context(cb);
> -	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
> -	struct ethnl_dump_ctx *ctx = &phy_ctx->ethnl_ctx;
> -	struct ethnl_reply_data *reply_data;
> -	const struct ethnl_request_ops *ops;
> -	struct ethnl_req_info *req_info;
> -	struct genlmsghdr *ghdr;
> -	int ret;
> -
> -	BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
> -
> -	ghdr = nlmsg_data(cb->nlh);
> -	ops = ethnl_default_requests[ghdr->cmd];
> -	if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd))
> -		return -EOPNOTSUPP;
> -	req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
> -	if (!req_info)
> -		return -ENOMEM;
> -	reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
> -	if (!reply_data) {
> -		ret = -ENOMEM;
> -		goto free_req_info;
> -	}
> -
> -	/* Unlike per-dev dump, don't ignore dev. The dump handler
> -	 * will notice it and dump PHYs from given dev. We only keep track of
> -	 * the dev's ifindex, .dumpit() will grab and release the netdev itself.
> -	 */
> -	ret = ethnl_default_parse(req_info, &info->info, ops, false);
> -	if (ret < 0)
> -		goto free_reply_data;
> -	if (req_info->dev) {
> -		phy_ctx->ifindex = req_info->dev->ifindex;
> -		netdev_put(req_info->dev, &req_info->dev_tracker);
> -		req_info->dev = NULL;
> -	}
> -
> -	ctx->ops = ops;
> -	ctx->req_info = req_info;
> -	ctx->reply_data = reply_data;
> -	ctx->pos_ifindex = 0;
> -
> -	return 0;
> -
> -free_reply_data:
> -	kfree(reply_data);
> -free_req_info:
> -	kfree(req_info);
> -
> -	return ret;
> -}
> -
> -static int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
> -				     struct ethnl_perphy_dump_ctx *ctx,
> -				     const struct genl_info *info)
> -{
> -	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
> -	struct net_device *dev = ethnl_ctx->req_info->dev;
> +	struct net_device *dev = ctx->req_info->dev;
>  	struct phy_device_node *pdn;
>  	int ret;
>  
>  	if (!dev->link_topo)
>  		return 0;
>  
> -	xa_for_each_start(&dev->link_topo->phys, ctx->pos_phyindex, pdn,
> -			  ctx->pos_phyindex) {
> -		ethnl_ctx->req_info->phy_index = ctx->pos_phyindex;
> +	xa_for_each_start(&dev->link_topo->phys, *pos_sub, pdn,
> +			  *pos_sub) {
> +		ctx->req_info->phy_index = *pos_sub;
>  
>  		/* We can re-use the original dump_one as ->prepare_data in
>  		 * commands use ethnl_req_get_phydev(), which gets the PHY from
>  		 * the req_info->phy_index
>  		 */
> -		ret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info);
> +		ret = ethnl_default_dump_one(skb, dev, ctx, info);
>  		if (ret)
>  			return ret;
>  	}
>  
> -	ctx->pos_phyindex = 0;
> -
> -	return 0;
> -}
> -
> -static int ethnl_perphy_dump_all_dev(struct sk_buff *skb,
> -				     struct ethnl_perphy_dump_ctx *ctx,
> -				     const struct genl_info *info)
> -{
> -	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
> -	struct net *net = sock_net(skb->sk);
> -	netdevice_tracker dev_tracker;
> -	struct net_device *dev;
> -	int ret = 0;
> -
> -	rcu_read_lock();
> -	for_each_netdev_dump(net, dev, ethnl_ctx->pos_ifindex) {
> -		netdev_hold(dev, &dev_tracker, GFP_ATOMIC);
> -		rcu_read_unlock();
> -
> -		/* per-PHY commands use ethnl_req_get_phydev(), which needs the
> -		 * net_device in the req_info
> -		 */
> -		ethnl_ctx->req_info->dev = dev;
> -		ret = ethnl_perphy_dump_one_dev(skb, ctx, info);
> -
> -		rcu_read_lock();
> -		netdev_put(dev, &dev_tracker);
> -		ethnl_ctx->req_info->dev = NULL;
> -
> -		if (ret < 0 && ret != -EOPNOTSUPP) {
> -			if (likely(skb->len))
> -				ret = skb->len;
> -			break;
> -		}
> -		ret = 0;
> -	}
> -	rcu_read_unlock();
> -
> -	return ret;
> -}
> -
> -/* per-PHY ->dumpit() handler for GET requests. */
> -static int ethnl_perphy_dumpit(struct sk_buff *skb,
> -			       struct netlink_callback *cb)
> -{
> -	struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb);
> -	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
> -	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
> -	int ret = 0;
> -
> -	if (ctx->ifindex) {
> -		netdevice_tracker dev_tracker;
> -		struct net_device *dev;
> -
> -		dev = netdev_get_by_index(genl_info_net(&info->info),
> -					  ctx->ifindex, &dev_tracker,
> -					  GFP_KERNEL);
> -		if (!dev)
> -			return -ENODEV;
> -
> -		ethnl_ctx->req_info->dev = dev;
> -		ret = ethnl_perphy_dump_one_dev(skb, ctx, genl_info_dump(cb));
> -
> -		if (ret < 0 && ret != -EOPNOTSUPP && likely(skb->len))
> -			ret = skb->len;
> -
> -		netdev_put(dev, &dev_tracker);
> -	} else {
> -		ret = ethnl_perphy_dump_all_dev(skb, ctx, genl_info_dump(cb));
> -	}
> -
> -	return ret;
> -}
> -
> -/* per-PHY ->done() handler for GET requests */
> -static int ethnl_perphy_done(struct netlink_callback *cb)
> -{
> -	struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb);
> -	struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx;
> -
> -	kfree(ethnl_ctx->reply_data);
> -	kfree(ethnl_ctx->req_info);
> +	*pos_sub = 0;
>  
>  	return 0;
>  }
> @@ -1410,9 +1268,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
>  	{
>  		.cmd	= ETHTOOL_MSG_PSE_GET,
>  		.doit	= ethnl_default_doit,
> -		.start	= ethnl_perphy_start,
> -		.dumpit	= ethnl_perphy_dumpit,
> -		.done	= ethnl_perphy_done,
> +		.start	= ethnl_default_start,
> +		.dumpit	= ethnl_default_dumpit,
> +		.done	= ethnl_default_done,
>  		.policy = ethnl_pse_get_policy,
>  		.maxattr = ARRAY_SIZE(ethnl_pse_get_policy) - 1,
>  	},
> @@ -1434,9 +1292,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
>  	{
>  		.cmd	= ETHTOOL_MSG_PLCA_GET_CFG,
>  		.doit	= ethnl_default_doit,
> -		.start	= ethnl_perphy_start,
> -		.dumpit	= ethnl_perphy_dumpit,
> -		.done	= ethnl_perphy_done,
> +		.start	= ethnl_default_start,
> +		.dumpit	= ethnl_default_dumpit,
> +		.done	= ethnl_default_done,
>  		.policy = ethnl_plca_get_cfg_policy,
>  		.maxattr = ARRAY_SIZE(ethnl_plca_get_cfg_policy) - 1,
>  	},
> @@ -1450,9 +1308,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
>  	{
>  		.cmd	= ETHTOOL_MSG_PLCA_GET_STATUS,
>  		.doit	= ethnl_default_doit,
> -		.start	= ethnl_perphy_start,
> -		.dumpit	= ethnl_perphy_dumpit,
> -		.done	= ethnl_perphy_done,
> +		.start	= ethnl_default_start,
> +		.dumpit	= ethnl_default_dumpit,
> +		.done	= ethnl_default_done,
>  		.policy = ethnl_plca_get_status_policy,
>  		.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
>  	},
> @@ -1482,9 +1340,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
>  	{
>  		.cmd	= ETHTOOL_MSG_PHY_GET,
>  		.doit	= ethnl_default_doit,
> -		.start	= ethnl_perphy_start,
> -		.dumpit	= ethnl_perphy_dumpit,
> -		.done	= ethnl_perphy_done,
> +		.start	= ethnl_default_start,
> +		.dumpit	= ethnl_default_dumpit,
> +		.done	= ethnl_default_done,
>  		.policy = ethnl_phy_get_policy,
>  		.maxattr = ARRAY_SIZE(ethnl_phy_get_policy) - 1,
>  	},
> @@ -1528,9 +1386,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
>  	{
>  		.cmd	= ETHTOOL_MSG_MSE_GET,
>  		.doit	= ethnl_default_doit,
> -		.start	= ethnl_perphy_start,
> -		.dumpit	= ethnl_perphy_dumpit,
> -		.done	= ethnl_perphy_done,
> +		.start	= ethnl_default_start,
> +		.dumpit	= ethnl_default_dumpit,
> +		.done	= ethnl_default_done,
>  		.policy = ethnl_mse_get_policy,
>  		.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
>  	},
> diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
> index e01adc5db02f..dda2f5593ed9 100644
> --- a/net/ethtool/netlink.h
> +++ b/net/ethtool/netlink.h
> @@ -546,6 +546,10 @@ 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);
> +int ethnl_perphy_dump_one_dev(struct sk_buff *skb,
> +			      struct ethnl_dump_ctx *ctx,
> +			      unsigned long *pos_sub,
> +			      const 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];
> diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c
> index d4e6887055ab..4bb23a5d6ad5 100644
> --- a/net/ethtool/phy.c
> +++ b/net/ethtool/phy.c
> @@ -162,4 +162,5 @@ const struct ethnl_request_ops ethnl_phy_request_ops = {
>  	.reply_size		= phy_reply_size,
>  	.fill_reply		= phy_fill_reply,
>  	.cleanup_data		= phy_cleanup_data,
> +	.dump_one_dev		= ethnl_perphy_dump_one_dev,
>  };
> diff --git a/net/ethtool/plca.c b/net/ethtool/plca.c
> index 91f0c4233298..84e902532617 100644
> --- a/net/ethtool/plca.c
> +++ b/net/ethtool/plca.c
> @@ -188,6 +188,7 @@ const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
>  	.prepare_data		= plca_get_cfg_prepare_data,
>  	.reply_size		= plca_get_cfg_reply_size,
>  	.fill_reply		= plca_get_cfg_fill_reply,
> +	.dump_one_dev		= ethnl_perphy_dump_one_dev,
>  
>  	.set			= ethnl_set_plca,
>  	.set_ntf_cmd		= ETHTOOL_MSG_PLCA_NTF,
> @@ -268,4 +269,5 @@ const struct ethnl_request_ops ethnl_plca_status_request_ops = {
>  	.prepare_data		= plca_get_status_prepare_data,
>  	.reply_size		= plca_get_status_reply_size,
>  	.fill_reply		= plca_get_status_fill_reply,
> +	.dump_one_dev		= ethnl_perphy_dump_one_dev,
>  };
> diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
> index 2eb9bdc2dcb9..83f0205053a3 100644
> --- a/net/ethtool/pse-pd.c
> +++ b/net/ethtool/pse-pd.c
> @@ -338,6 +338,7 @@ const struct ethnl_request_ops ethnl_pse_request_ops = {
>  	.reply_size		= pse_reply_size,
>  	.fill_reply		= pse_fill_reply,
>  	.cleanup_data		= pse_cleanup_data,
> +	.dump_one_dev		= ethnl_perphy_dump_one_dev,
>  
>  	.set			= ethnl_set_pse,
>  	/* PSE has no notification */