Extend struct ethtool_ops with three loopback callbacks that drivers can implement for MAC-level loopback control:
- get_loopback(dev, name, id, entry): exact lookup by name and instance id, used by doit (single-entry GET) requests. - get_loopback_by_index(dev, index, entry): flat enumeration by index, used by dumpit (multi-entry GET) requests to iterate all loopback points on a device. - set_loopback(dev, entry, extack): apply a loopback configuration change. Returns 1 if hardware state changed, 0 if no-op. Wire the MAC component into loopback.c's dispatch functions. For dump enumeration, MAC entries are tried first via the driver's get_loopback_by_index() op, then MODULE/CMIS entries follow at the next index offset. Signed-off-by: Björn Töpel <[email protected]> --- include/linux/ethtool.h | 7 ++++++ net/ethtool/loopback.c | 56 ++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index c9beca11fc40..8893e732f930 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -1321,6 +1321,13 @@ struct ethtool_ops { int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg, struct netlink_ext_ack *extack); void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats); + int (*get_loopback)(struct net_device *dev, const char *name, + u32 id, struct ethtool_loopback_entry *entry); + int (*get_loopback_by_index)(struct net_device *dev, u32 index, + struct ethtool_loopback_entry *entry); + int (*set_loopback)(struct net_device *dev, + const struct ethtool_loopback_entry *entry, + struct netlink_ext_ack *extack); }; int ethtool_check_ops(const struct ethtool_ops *ops); diff --git a/net/ethtool/loopback.c b/net/ethtool/loopback.c index ed184f45322e..9c5834be743f 100644 --- a/net/ethtool/loopback.c +++ b/net/ethtool/loopback.c @@ -86,6 +86,10 @@ static int loopback_get(struct net_device *dev, struct ethtool_loopback_entry *entry) { switch (component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->get_loopback) + return -EOPNOTSUPP; + return dev->ethtool_ops->get_loopback(dev, name, id, entry); case ETHTOOL_LOOPBACK_COMPONENT_MODULE: return ethtool_cmis_get_loopback(dev, name, entry); default: @@ -93,10 +97,22 @@ static int loopback_get(struct net_device *dev, } } -static int loopback_get_by_index(struct net_device *dev, u32 index, +static int loopback_get_by_index(struct net_device *dev, + enum ethtool_loopback_component component, + u32 index, struct ethtool_loopback_entry *entry) { - return ethtool_cmis_get_loopback_by_index(dev, index, entry); + switch (component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->get_loopback_by_index) + return -EOPNOTSUPP; + return dev->ethtool_ops->get_loopback_by_index(dev, index, + entry); + case ETHTOOL_LOOPBACK_COMPONENT_MODULE: + return ethtool_cmis_get_loopback_by_index(dev, index, entry); + default: + return -EOPNOTSUPP; + } } static int loopback_prepare_data(const struct ethnl_req_info *req_base, @@ -116,7 +132,8 @@ static int loopback_prepare_data(const struct ethnl_req_info *req_base, ret = loopback_get(dev, req_info->component, req_info->id, req_info->name, &data->entry); else - ret = loopback_get_by_index(dev, req_info->index, &data->entry); + ret = loopback_get_by_index(dev, req_info->component, + req_info->index, &data->entry); ethnl_ops_complete(dev); @@ -225,6 +242,10 @@ static int __loopback_set(struct net_device *dev, struct netlink_ext_ack *extack) { switch (entry->component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->set_loopback) + return -EOPNOTSUPP; + return dev->ethtool_ops->set_loopback(dev, entry, extack); case ETHTOOL_LOOPBACK_COMPONENT_MODULE: return ethtool_cmis_set_loopback(dev, entry, extack); default: @@ -274,20 +295,31 @@ static int loopback_dump_one_dev(struct sk_buff *skb, { struct loopback_req_info *req_info = container_of(ctx->req_info, struct loopback_req_info, base); + /* pos_sub encodes: upper 16 bits = component phase, lower 16 = index + * within that component. dump_one_dev is called repeatedly with + * increasing pos_sub until all components are exhausted. + */ + enum ethtool_loopback_component phase = *pos_sub >> 16; + u32 idx = *pos_sub & 0xffff; int ret; - for (;; (*pos_sub)++) { - req_info->index = *pos_sub; - ret = ethnl_default_dump_one(skb, ctx->req_info->dev, ctx, - info); - if (ret == -EOPNOTSUPP) - break; - if (ret) - return ret; + for (; phase <= ETHTOOL_LOOPBACK_COMPONENT_MODULE; phase++) { + for (;; idx++) { + req_info->component = phase; + req_info->index = idx; + ret = ethnl_default_dump_one(skb, ctx->req_info->dev, + ctx, info); + if (ret == -EOPNOTSUPP) + break; + if (ret) { + *pos_sub = ((unsigned long)phase << 16) | idx; + return ret; + } + } + idx = 0; } *pos_sub = 0; - return 0; } -- 2.53.0

