Add callbacks to calculate the size and fill link extended statistics which can be split into multiple messages and are dumped via the new rtnl stats API (RTM_GETSTATS) with the IFLA_STATS_LINK_XSTATS attribute. Also add that attribute to the idx mask check since it is expected to be able to save state and resume dumping (e.g. future bridge per-vlan stats will be dumped via this attribute and callbacks).
Signed-off-by: Nikolay Aleksandrov <niko...@cumulusnetworks.com> --- include/net/rtnetlink.h | 6 +++++- include/uapi/linux/if_link.h | 8 ++++++++ net/core/rtnetlink.c | 26 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 3f3b0b1b8722..b449c1f3416f 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -95,6 +95,10 @@ struct rtnl_link_ops { const struct net_device *dev, const struct net_device *slave_dev); struct net *(*get_link_net)(const struct net_device *dev); + size_t (*get_linkxstats_size)(const struct net_device *dev); + int (*fill_linkxstats)(struct sk_buff *skb, + const struct net_device *dev, + int *lidx); }; int __rtnl_link_register(struct rtnl_link_ops *ops); @@ -154,6 +158,6 @@ int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len); * IFLA_STATS_IDX_ATTR_MASK has all the idx saving attributes set and is * used to check if more than one is being requested */ -#define IFLA_STATS_IDX_ATTR_MASK 0 +#define IFLA_STATS_IDX_ATTR_MASK IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS) #endif diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index ba69d4447249..1b874e26b15b 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -798,6 +798,7 @@ struct if_stats_msg { enum { IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, __IFLA_STATS_MAX, }; @@ -805,4 +806,11 @@ enum { #define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) +/* These are embedded into IFLA_STATS_LINK_XSTATS */ +enum { + LINK_XSTATS_UNSPEC, + __LINK_XSTATS_MAX +}; +#define LINK_XSTATS_MAX (__LINK_XSTATS_MAX - 1) + #endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ea03b6cd3d3c..9637618c408d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3477,6 +3477,23 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, dev_get_stats(dev, sp); } + if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS)) { + const struct rtnl_link_ops *ops = dev->rtnl_link_ops; + + if (ops && ops->fill_linkxstats) { + int err; + + attr = nla_nest_start(skb, IFLA_STATS_LINK_XSTATS); + if (!attr) + goto nla_put_failure; + + err = ops->fill_linkxstats(skb, dev, lidx); + nla_nest_end(skb, attr); + if (err) + goto nla_put_failure; + } + } + nlmsg_end(skb, nlh); return 0; @@ -3503,6 +3520,15 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_64)) size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64)); + if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS)) { + const struct rtnl_link_ops *ops = dev->rtnl_link_ops; + + if (ops && ops->get_linkxstats_size) + size += nla_total_size(ops->get_linkxstats_size(dev)); + /* anything dumped is embedded in IFLA_STATS_LINK_XSTATS */ + size += nla_total_size(0); + } + return size; } -- 2.4.11