Commit 80e73cc5 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller

net: rtnetlink: add support for the IFLA_STATS_LINK_XSTATS_SLAVE attribute

This patch adds support for the IFLA_STATS_LINK_XSTATS_SLAVE attribute
which allows to export per-slave statistics if the master device supports
the linkxstats callback. The attribute is passed down to the linkxstats
callback and it is up to the callback user to use it (an example has been
added to the only current user - the bridge). This allows us to query only
specific slaves of master devices like bridge ports and export only what
we're interested in instead of having to dump all ports and searching only
for a single one. This will be used to export per-port IGMP/MLD stats and
also per-port vlan stats in the future, possibly other statistics as well.
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 545c321b
...@@ -98,10 +98,11 @@ struct rtnl_link_ops { ...@@ -98,10 +98,11 @@ struct rtnl_link_ops {
const struct net_device *dev, const struct net_device *dev,
const struct net_device *slave_dev); const struct net_device *slave_dev);
struct net *(*get_link_net)(const struct net_device *dev); struct net *(*get_link_net)(const struct net_device *dev);
size_t (*get_linkxstats_size)(const struct net_device *dev); size_t (*get_linkxstats_size)(const struct net_device *dev,
int attr);
int (*fill_linkxstats)(struct sk_buff *skb, int (*fill_linkxstats)(struct sk_buff *skb,
const struct net_device *dev, const struct net_device *dev,
int *prividx); int *prividx, int attr);
}; };
int __rtnl_link_register(struct rtnl_link_ops *ops); int __rtnl_link_register(struct rtnl_link_ops *ops);
......
...@@ -822,6 +822,7 @@ enum { ...@@ -822,6 +822,7 @@ enum {
IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
IFLA_STATS_LINK_64, IFLA_STATS_LINK_64,
IFLA_STATS_LINK_XSTATS, IFLA_STATS_LINK_XSTATS,
IFLA_STATS_LINK_XSTATS_SLAVE,
__IFLA_STATS_MAX, __IFLA_STATS_MAX,
}; };
......
...@@ -1234,7 +1234,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) ...@@ -1234,7 +1234,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
return 0; return 0;
} }
static size_t br_get_linkxstats_size(const struct net_device *dev) static size_t bridge_get_linkxstats_size(const struct net_device *dev)
{ {
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
...@@ -1254,8 +1254,30 @@ static size_t br_get_linkxstats_size(const struct net_device *dev) ...@@ -1254,8 +1254,30 @@ static size_t br_get_linkxstats_size(const struct net_device *dev)
nla_total_size(0); nla_total_size(0);
} }
static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev, static size_t brport_get_linkxstats_size(const struct net_device *dev)
int *prividx) {
return nla_total_size(0);
}
static size_t br_get_linkxstats_size(const struct net_device *dev, int attr)
{
size_t retsize = 0;
switch (attr) {
case IFLA_STATS_LINK_XSTATS:
retsize = bridge_get_linkxstats_size(dev);
break;
case IFLA_STATS_LINK_XSTATS_SLAVE:
retsize = brport_get_linkxstats_size(dev);
break;
}
return retsize;
}
static int bridge_fill_linkxstats(struct sk_buff *skb,
const struct net_device *dev,
int *prividx)
{ {
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
...@@ -1298,6 +1320,37 @@ static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev, ...@@ -1298,6 +1320,37 @@ static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
return -EMSGSIZE; return -EMSGSIZE;
} }
static int brport_fill_linkxstats(struct sk_buff *skb,
const struct net_device *dev,
int *prividx)
{
struct nlattr *nest;
nest = nla_nest_start(skb, LINK_XSTATS_TYPE_BRIDGE);
if (!nest)
return -EMSGSIZE;
nla_nest_end(skb, nest);
return 0;
}
static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
int *prividx, int attr)
{
int ret = -EINVAL;
switch (attr) {
case IFLA_STATS_LINK_XSTATS:
ret = bridge_fill_linkxstats(skb, dev, prividx);
break;
case IFLA_STATS_LINK_XSTATS_SLAVE:
ret = brport_fill_linkxstats(skb, dev, prividx);
break;
}
return ret;
}
static struct rtnl_af_ops br_af_ops __read_mostly = { static struct rtnl_af_ops br_af_ops __read_mostly = {
.family = AF_BRIDGE, .family = AF_BRIDGE,
.get_link_af_size = br_get_link_af_size_filtered, .get_link_af_size = br_get_link_af_size_filtered,
......
...@@ -3519,7 +3519,32 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, ...@@ -3519,7 +3519,32 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
if (!attr) if (!attr)
goto nla_put_failure; goto nla_put_failure;
err = ops->fill_linkxstats(skb, dev, prividx); err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
nla_nest_end(skb, attr);
if (err)
goto nla_put_failure;
*idxattr = 0;
}
}
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE,
*idxattr)) {
const struct rtnl_link_ops *ops = NULL;
const struct net_device *master;
master = netdev_master_upper_dev_get(dev);
if (master)
ops = master->rtnl_link_ops;
if (ops && ops->fill_linkxstats) {
int err;
*idxattr = IFLA_STATS_LINK_XSTATS_SLAVE;
attr = nla_nest_start(skb,
IFLA_STATS_LINK_XSTATS_SLAVE);
if (!attr)
goto nla_put_failure;
err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
nla_nest_end(skb, attr); nla_nest_end(skb, attr);
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
...@@ -3555,14 +3580,35 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, ...@@ -3555,14 +3580,35 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) { if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) {
const struct rtnl_link_ops *ops = dev->rtnl_link_ops; const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
int attr = IFLA_STATS_LINK_XSTATS;
if (ops && ops->get_linkxstats_size) { if (ops && ops->get_linkxstats_size) {
size += nla_total_size(ops->get_linkxstats_size(dev)); size += nla_total_size(ops->get_linkxstats_size(dev,
attr));
/* for IFLA_STATS_LINK_XSTATS */ /* for IFLA_STATS_LINK_XSTATS */
size += nla_total_size(0); size += nla_total_size(0);
} }
} }
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE, 0)) {
struct net_device *_dev = (struct net_device *)dev;
const struct rtnl_link_ops *ops = NULL;
const struct net_device *master;
/* netdev_master_upper_dev_get can't take const */
master = netdev_master_upper_dev_get(_dev);
if (master)
ops = master->rtnl_link_ops;
if (ops && ops->get_linkxstats_size) {
int attr = IFLA_STATS_LINK_XSTATS_SLAVE;
size += nla_total_size(ops->get_linkxstats_size(dev,
attr));
/* for IFLA_STATS_LINK_XSTATS_SLAVE */
size += nla_total_size(0);
}
}
return size; return size;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment