The per-PHY specific dump functions share a lot functionality of with the default dumpit infrastructure, but does not share the actual code. By introducing a new sub-iterator function, the two dumpit variants can be folded into one set of functions.
Add a new dump_one_dev callback in ethnl_request_ops. When ops->dump_one_dev is set, ethnl_default_start() saves the target device's ifindex for filtered dumps, and ethnl_default_dumpit() delegates per-device iteration to the callback instead of calling ethnl_default_dump_one() directly. No separate start/dumpit/done functions are needed. For the existing per-PHY commands (PSE, PLCA, PHY, MSE), the shared ethnl_perphy_dump_one_dev helper provides the xa_for_each_start loop over the device's PHY topology. This prepares the ethtool infrastructure for other commands that need similar per-device sub-iteration. Suggested-by: Maxime Chevallier <[email protected]> Signed-off-by: Björn Töpel <[email protected]> --- net/ethtool/mse.c | 1 + net/ethtool/netlink.c | 261 ++++++++++-------------------------------- net/ethtool/netlink.h | 31 +++++ net/ethtool/phy.c | 1 + net/ethtool/plca.c | 2 + net/ethtool/pse-pd.c | 1 + 6 files changed, 94 insertions(+), 203 deletions(-) diff --git a/net/ethtool/mse.c b/net/ethtool/mse.c index 8cb3fc5e7be4..0f2709b4f7de 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 6e5f0f4f815a..e740b11a0609 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -345,36 +345,6 @@ int ethnl_multicast(struct sk_buff *skb, struct net_device *dev) /* GET request helpers */ -/** - * struct ethnl_dump_ctx - context structure for generic dumpit() callback - * @ops: request ops of currently processed message type - * @req_info: parsed request header of processed request - * @reply_data: data needed to compose the reply - * @pos_ifindex: saved iteration position - ifindex - * - * These parameters are kept in struct netlink_callback as context preserved - * between iterations. They are initialized by ethnl_default_start() and used - * in ethnl_default_dumpit() and ethnl_default_done(). - */ -struct ethnl_dump_ctx { - const struct ethnl_request_ops *ops; - struct ethnl_req_info *req_info; - struct ethnl_reply_data *reply_data; - unsigned long pos_ifindex; -}; - -/** - * struct ethnl_perphy_dump_ctx - context for dumpit() PHY-aware callbacks - * @ethnl_ctx: generic ethnl context - * @ifindex: For Filtered DUMP requests, the ifindex of the targeted netdev - * @pos_phyindex: iterator position for multi-msg DUMP - */ -struct ethnl_perphy_dump_ctx { - struct ethnl_dump_ctx ethnl_ctx; - unsigned int ifindex; - unsigned long pos_phyindex; -}; - static const struct ethnl_request_ops * ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, @@ -428,12 +398,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 @@ -616,17 +580,41 @@ static int ethnl_default_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); + const struct genl_info *info = genl_info_dump(cb); struct net *net = sock_net(skb->sk); netdevice_tracker dev_tracker; struct net_device *dev; int ret = 0; + if (ctx->ops->dump_one_dev && ctx->ifindex) { + dev = netdev_get_by_index(net, ctx->ifindex, &dev_tracker, + GFP_KERNEL); + if (!dev) + return -ENODEV; + + ctx->req_info->dev = dev; + ret = ctx->ops->dump_one_dev(skb, ctx, &ctx->pos_sub, info); + + if (ret < 0 && ret != -EOPNOTSUPP && likely(skb->len)) + ret = skb->len; + + netdev_put(dev, &dev_tracker); + return ret; + } + rcu_read_lock(); for_each_netdev_dump(net, dev, ctx->pos_ifindex) { netdev_hold(dev, &dev_tracker, GFP_ATOMIC); rcu_read_unlock(); - ret = ethnl_default_dump_one(skb, dev, ctx, genl_info_dump(cb)); + if (ctx->ops->dump_one_dev) { + ctx->req_info->dev = dev; + ret = ctx->ops->dump_one_dev(skb, ctx, &ctx->pos_sub, + info); + ctx->req_info->dev = NULL; + } else { + ret = ethnl_default_dump_one(skb, dev, ctx, info); + } rcu_read_lock(); netdev_put(dev, &dev_tracker); @@ -673,10 +661,13 @@ static int ethnl_default_start(struct netlink_callback *cb) if (ret < 0) goto free_reply_data; if (req_info->dev) { - /* We ignore device specification in dump requests but as the - * same parser as for non-dump (doit) requests is used, it - * would take reference to the device if it finds one - */ + if (ops->dump_one_dev) { + /* Sub-iterator dumps keep track of the dev's ifindex + * so the dumpit handler can grab/release the netdev + * per iteration. + */ + ctx->ifindex = req_info->dev->ifindex; + } netdev_put(req_info->dev, &req_info->dev_tracker); req_info->dev = NULL; } @@ -696,169 +687,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; } @@ -1420,9 +1275,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, }, @@ -1444,9 +1299,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, }, @@ -1460,9 +1315,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, }, @@ -1492,9 +1347,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, }, @@ -1538,9 +1393,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 89010eaa67df..aa8d51903ecc 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -10,6 +10,28 @@ struct ethnl_req_info; +/** + * struct ethnl_dump_ctx - context structure for generic dumpit() callback + * @ops: request ops of currently processed message type + * @req_info: parsed request header of processed request + * @reply_data: data needed to compose the reply + * @pos_ifindex: saved iteration position - ifindex + * @ifindex: for filtered dump requests, the ifindex of the targeted netdev + * @pos_sub: iterator position for per-device iteration + * + * These parameters are kept in struct netlink_callback as context preserved + * between iterations. They are initialized by ethnl_default_start() and used + * in ethnl_default_dumpit() and ethnl_default_done(). + */ +struct ethnl_dump_ctx { + const struct ethnl_request_ops *ops; + struct ethnl_req_info *req_info; + struct ethnl_reply_data *reply_data; + unsigned long pos_ifindex; + unsigned int ifindex; + unsigned long pos_sub; +}; + u32 ethnl_bcast_seq_next(void); int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, const struct nlattr *nest, struct net *net, @@ -408,6 +430,11 @@ struct ethnl_request_ops { const struct ethnl_reply_data *reply_data); void (*cleanup_data)(struct ethnl_reply_data *reply_data); + int (*dump_one_dev)(struct sk_buff *skb, + struct ethnl_dump_ctx *ctx, + unsigned long *pos_sub, + const struct genl_info *info); + int (*set_validate)(struct ethnl_req_info *req_info, struct genl_info *info); int (*set)(struct ethnl_req_info *req_info, @@ -514,6 +541,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 68372bef4b2f..4158c2abb235 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 e1f7820a6158..cad0ba476c29 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 24def9c9dd54..1442a59e033f 100644 --- a/net/ethtool/pse-pd.c +++ b/net/ethtool/pse-pd.c @@ -337,6 +337,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

