mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
net: ethtool: Introduce per-PHY DUMP operations
ethnl commands that target a phy_device need a DUMP implementation that will fill the reply for every PHY behind a netdev. We therefore need to iterate over the dev->topo to list them. When multiple PHYs are behind the same netdev, it's also useful to perform DUMP with a filter on a given netdev, to get the capability of every PHY. Implement dedicated genl ->start(), ->dumpit() and ->done() operations for PHY-targetting command, allowing filtered dumps and using a dump context that keep track of the PHY iteration for multi-message dump. PSE-PD and PLCA are converted to this new set of ops along the way. Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> Link: https://patch.msgid.link/20250502085242.248645-2-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
953d9480f7
commit
172265b44c
|
|
@ -357,6 +357,18 @@ struct ethnl_dump_ctx {
|
|||
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,
|
||||
|
|
@ -407,6 +419,12 @@ 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
|
||||
|
|
@ -662,6 +680,173 @@ 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)
|
||||
{
|
||||
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 (req_info->dev) {
|
||||
phy_ctx->ifindex = req_info->dev->ifindex;
|
||||
netdev_put(req_info->dev, &req_info->dev_tracker);
|
||||
req_info->dev = NULL;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto free_reply_data;
|
||||
|
||||
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 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;
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* default ->done() handler for GET requests */
|
||||
static int ethnl_default_done(struct netlink_callback *cb)
|
||||
{
|
||||
|
|
@ -1200,9 +1385,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
|
|||
{
|
||||
.cmd = ETHTOOL_MSG_PSE_GET,
|
||||
.doit = ethnl_default_doit,
|
||||
.start = ethnl_default_start,
|
||||
.dumpit = ethnl_default_dumpit,
|
||||
.done = ethnl_default_done,
|
||||
.start = ethnl_perphy_start,
|
||||
.dumpit = ethnl_perphy_dumpit,
|
||||
.done = ethnl_perphy_done,
|
||||
.policy = ethnl_pse_get_policy,
|
||||
.maxattr = ARRAY_SIZE(ethnl_pse_get_policy) - 1,
|
||||
},
|
||||
|
|
@ -1224,9 +1409,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
|
|||
{
|
||||
.cmd = ETHTOOL_MSG_PLCA_GET_CFG,
|
||||
.doit = ethnl_default_doit,
|
||||
.start = ethnl_default_start,
|
||||
.dumpit = ethnl_default_dumpit,
|
||||
.done = ethnl_default_done,
|
||||
.start = ethnl_perphy_start,
|
||||
.dumpit = ethnl_perphy_dumpit,
|
||||
.done = ethnl_perphy_done,
|
||||
.policy = ethnl_plca_get_cfg_policy,
|
||||
.maxattr = ARRAY_SIZE(ethnl_plca_get_cfg_policy) - 1,
|
||||
},
|
||||
|
|
@ -1240,9 +1425,9 @@ static const struct genl_ops ethtool_genl_ops[] = {
|
|||
{
|
||||
.cmd = ETHTOOL_MSG_PLCA_GET_STATUS,
|
||||
.doit = ethnl_default_doit,
|
||||
.start = ethnl_default_start,
|
||||
.dumpit = ethnl_default_dumpit,
|
||||
.done = ethnl_default_done,
|
||||
.start = ethnl_perphy_start,
|
||||
.dumpit = ethnl_perphy_dumpit,
|
||||
.done = ethnl_perphy_done,
|
||||
.policy = ethnl_plca_get_status_policy,
|
||||
.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user