Add a new LINK_XSTATS_BRIDGE_VLAN 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.

Signed-off-by: Nikolay Aleksandrov <niko...@cumulusnetworks.com>
---
 include/uapi/linux/if_bridge.h |  8 ++++++
 include/uapi/linux/if_link.h   |  1 +
 net/bridge/br_netlink.c        | 57 ++++++++++++++++++++++++++++++++++++++++++
 net/bridge/br_private.h        |  7 ++++++
 net/bridge/br_vlan.c           | 27 ++++++++++++++++++++
 5 files changed, 100 insertions(+)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 0536eefff9bf..3eb4e7145825 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -134,6 +134,14 @@ struct bridge_vlan_info {
        __u16 vid;
 };
 
+struct bridge_vlan_xstats {
+       __u16 vid;
+       __u64 rx_bytes;
+       __u64 rx_packets;
+       __u64 tx_bytes;
+       __u64 tx_packets;
+};
+
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 1b874e26b15b..7a9420a19720 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -809,6 +809,7 @@ enum {
 /* These are embedded into IFLA_STATS_LINK_XSTATS */
 enum {
        LINK_XSTATS_UNSPEC,
+       LINK_XSTATS_BRIDGE_VLAN,
        __LINK_XSTATS_MAX
 };
 #define LINK_XSTATS_MAX (__LINK_XSTATS_MAX - 1)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index f33d95b0f5d3..34b4fa6fd693 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1212,6 +1212,61 @@ 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 || !vg->num_vlans)
+               return 0;
+
+       /* we need to count all, even placeholder entries */
+       list_for_each_entry(v, &vg->vlan_list, vlist)
+               numvls++;
+
+       return numvls * nla_total_size(sizeof(struct bridge_vlan_xstats));
+}
+
+static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device 
*dev,
+                             int *lidx)
+{
+       struct net_bridge *br = netdev_priv(dev);
+       struct net_bridge_vlan *v, *pvid;
+       struct net_bridge_vlan_group *vg;
+       struct bridge_vlan_xstats vxi;
+       int vl_idx = 0;
+
+       vg = br_vlan_group(br);
+       if (!vg || !vg->num_vlans)
+               goto out;
+       pvid = rtnl_dereference(vg->pvid);
+       list_for_each_entry(v, &vg->vlan_list, vlist) {
+               struct br_vlan_stats stats;
+
+               if (vl_idx++ < *lidx)
+                       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, LINK_XSTATS_BRIDGE_VLAN, sizeof(vxi), &vxi))
+                       goto nla_put_failure;
+       }
+       *lidx = 0;
+out:
+       return 0;
+
+nla_put_failure:
+       *lidx = vl_idx;
+       return -EMSGSIZE;
+}
 
 static struct rtnl_af_ops br_af_ops __read_mostly = {
        .family                 = AF_BRIDGE,
@@ -1230,6 +1285,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,
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index f6876ed718a5..a10f7ed26f3b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -709,6 +709,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)
@@ -876,6 +878,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 {
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index d7a70c2ea3ec..b39d9f5761d9 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1029,3 +1029,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;
+       }
+}
-- 
2.4.11

Reply via email to