bridge: netlink: export per-vlan stats
Add a new LINK_XSTATS_TYPE_BRIDGE attribute and implement the RTM_GETSTATS callbacks for IFLA_STATS_LINK_XSTATS (fill_linkxstats and get_linkxstats_size) in order to export the per-vlan stats. The paddings were added because soon these fields will be needed for per-port per-vlan stats (or something else if someone beats me to it) so avoiding at least a few more netlink attributes. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
6dada9b10a
Коммит
a60c090361
|
@ -134,6 +134,16 @@ struct bridge_vlan_info {
|
|||
__u16 vid;
|
||||
};
|
||||
|
||||
struct bridge_vlan_xstats {
|
||||
__u64 rx_bytes;
|
||||
__u64 rx_packets;
|
||||
__u64 tx_bytes;
|
||||
__u64 tx_packets;
|
||||
__u16 vid;
|
||||
__u16 pad1;
|
||||
__u32 pad2;
|
||||
};
|
||||
|
||||
/* Bridge multicast database attributes
|
||||
* [MDBA_MDB] = {
|
||||
* [MDBA_MDB_ENTRY] = {
|
||||
|
@ -233,4 +243,12 @@ enum {
|
|||
};
|
||||
#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
|
||||
|
||||
/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */
|
||||
enum {
|
||||
BRIDGE_XSTATS_UNSPEC,
|
||||
BRIDGE_XSTATS_VLAN,
|
||||
__BRIDGE_XSTATS_MAX
|
||||
};
|
||||
#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1)
|
||||
|
||||
#endif /* _UAPI_LINUX_IF_BRIDGE_H */
|
||||
|
|
|
@ -826,6 +826,7 @@ enum {
|
|||
*/
|
||||
enum {
|
||||
LINK_XSTATS_TYPE_UNSPEC,
|
||||
LINK_XSTATS_TYPE_BRIDGE,
|
||||
__LINK_XSTATS_TYPE_MAX
|
||||
};
|
||||
#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
|
||||
|
|
|
@ -1234,6 +1234,69 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static size_t br_get_linkxstats_size(const struct net_device *dev)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_vlan *v;
|
||||
int numvls = 0;
|
||||
|
||||
vg = br_vlan_group(br);
|
||||
if (!vg)
|
||||
return 0;
|
||||
|
||||
/* we need to count all, even placeholder entries */
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist)
|
||||
numvls++;
|
||||
|
||||
/* account for the vlans and the link xstats type nest attribute */
|
||||
return numvls * nla_total_size(sizeof(struct bridge_vlan_xstats)) +
|
||||
nla_total_size(0);
|
||||
}
|
||||
|
||||
static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
|
||||
int *prividx)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_vlan *v;
|
||||
struct nlattr *nest;
|
||||
int vl_idx = 0;
|
||||
|
||||
vg = br_vlan_group(br);
|
||||
if (!vg)
|
||||
goto out;
|
||||
nest = nla_nest_start(skb, LINK_XSTATS_TYPE_BRIDGE);
|
||||
if (!nest)
|
||||
return -EMSGSIZE;
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist) {
|
||||
struct bridge_vlan_xstats vxi;
|
||||
struct br_vlan_stats stats;
|
||||
|
||||
if (vl_idx++ < *prividx)
|
||||
continue;
|
||||
memset(&vxi, 0, sizeof(vxi));
|
||||
vxi.vid = v->vid;
|
||||
br_vlan_get_stats(v, &stats);
|
||||
vxi.rx_bytes = stats.rx_bytes;
|
||||
vxi.rx_packets = stats.rx_packets;
|
||||
vxi.tx_bytes = stats.tx_bytes;
|
||||
vxi.tx_packets = stats.tx_packets;
|
||||
|
||||
if (nla_put(skb, BRIDGE_XSTATS_VLAN, sizeof(vxi), &vxi))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
nla_nest_end(skb, nest);
|
||||
*prividx = 0;
|
||||
out:
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nla_nest_end(skb, nest);
|
||||
*prividx = vl_idx;
|
||||
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static struct rtnl_af_ops br_af_ops __read_mostly = {
|
||||
.family = AF_BRIDGE,
|
||||
|
@ -1252,6 +1315,8 @@ struct rtnl_link_ops br_link_ops __read_mostly = {
|
|||
.dellink = br_dev_delete,
|
||||
.get_size = br_get_size,
|
||||
.fill_info = br_fill_info,
|
||||
.fill_linkxstats = br_fill_linkxstats,
|
||||
.get_linkxstats_size = br_get_linkxstats_size,
|
||||
|
||||
.slave_maxtype = IFLA_BRPORT_MAX,
|
||||
.slave_policy = br_port_policy,
|
||||
|
|
|
@ -711,6 +711,8 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
|
|||
void nbp_vlan_flush(struct net_bridge_port *port);
|
||||
int nbp_vlan_init(struct net_bridge_port *port);
|
||||
int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
|
||||
void br_vlan_get_stats(const struct net_bridge_vlan *v,
|
||||
struct br_vlan_stats *stats);
|
||||
|
||||
static inline struct net_bridge_vlan_group *br_vlan_group(
|
||||
const struct net_bridge *br)
|
||||
|
@ -892,6 +894,11 @@ static inline struct net_bridge_vlan_group *nbp_vlan_group_rcu(
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void br_vlan_get_stats(const struct net_bridge_vlan *v,
|
||||
struct br_vlan_stats *stats)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
struct nf_br_ops {
|
||||
|
|
|
@ -1054,3 +1054,30 @@ void nbp_vlan_flush(struct net_bridge_port *port)
|
|||
synchronize_rcu();
|
||||
__vlan_group_free(vg);
|
||||
}
|
||||
|
||||
void br_vlan_get_stats(const struct net_bridge_vlan *v,
|
||||
struct br_vlan_stats *stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
for_each_possible_cpu(i) {
|
||||
u64 rxpackets, rxbytes, txpackets, txbytes;
|
||||
struct br_vlan_stats *cpu_stats;
|
||||
unsigned int start;
|
||||
|
||||
cpu_stats = per_cpu_ptr(v->stats, i);
|
||||
do {
|
||||
start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
|
||||
rxpackets = cpu_stats->rx_packets;
|
||||
rxbytes = cpu_stats->rx_bytes;
|
||||
txbytes = cpu_stats->tx_bytes;
|
||||
txpackets = cpu_stats->tx_packets;
|
||||
} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
|
||||
|
||||
stats->rx_packets += rxpackets;
|
||||
stats->rx_bytes += rxbytes;
|
||||
stats->tx_bytes += txbytes;
|
||||
stats->tx_packets += txpackets;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче