bridge: netlink: export per-vlan stats
[cascardo/linux.git] / net / bridge / br_netlink.c
index 7fba1f0..a5343c7 100644 (file)
@@ -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,