Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a60c0903 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller
Browse files

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: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6dada9b1
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -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)
+65 −0
Original line number Diff line number Diff line
@@ -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,
+7 −0
Original line number Diff line number Diff line
@@ -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 {
+27 −0
Original line number Diff line number Diff line
@@ -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;
	}
}