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

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

net: bridge: add support for IGMP/MLD stats and export them via netlink



This patch adds stats support for the currently used IGMP/MLD types by the
bridge. The stats are per-port (plus one stat per-bridge) and per-direction
(RX/TX). The stats are exported via netlink via the new linkxstats API
(RTM_GETSTATS). In order to minimize the performance impact, a new option
is used to enable/disable the stats - multicast_stats_enabled, similar to
the recent vlan stats. Also in order to avoid multiple IGMP/MLD type
lookups and checks, we make use of the current "igmp" member of the bridge
private skb->cb region to record the type on Rx (both host-generated and
external packets pass by multicast_rcv()). We can do that since the igmp
member was used as a boolean and all the valid IGMP/MLD types are positive
values. The normal bridge fast-path is not affected at all, the only
affected paths are the flooding ones and since we make use of the IGMP/MLD
type, we can quickly determine if the packet should be counted using
cache-hot data (cb's igmp member). We add counters for:
* IGMP Queries
* IGMP Leaves
* IGMP v1/v2/v3 reports

* MLD Queries
* MLD Leaves
* MLD v1/v2 reports

These are invaluable when monitoring or debugging complex multicast setups
with bridges.

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 80e73cc5
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -247,8 +247,34 @@ enum {
enum {
	BRIDGE_XSTATS_UNSPEC,
	BRIDGE_XSTATS_VLAN,
	BRIDGE_XSTATS_MCAST,
	BRIDGE_XSTATS_PAD,
	__BRIDGE_XSTATS_MAX
};
#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1)

enum {
	BR_MCAST_DIR_RX,
	BR_MCAST_DIR_TX,
	BR_MCAST_DIR_SIZE
};

/* IGMP/MLD statistics */
struct br_mcast_stats {
	__u64 igmp_queries[BR_MCAST_DIR_SIZE];
	__u64 igmp_leaves[BR_MCAST_DIR_SIZE];
	__u64 igmp_v1reports[BR_MCAST_DIR_SIZE];
	__u64 igmp_v2reports[BR_MCAST_DIR_SIZE];
	__u64 igmp_v3reports[BR_MCAST_DIR_SIZE];
	__u64 igmp_parse_errors;

	__u64 mld_queries[BR_MCAST_DIR_SIZE];
	__u64 mld_leaves[BR_MCAST_DIR_SIZE];
	__u64 mld_v1reports[BR_MCAST_DIR_SIZE];
	__u64 mld_v2reports[BR_MCAST_DIR_SIZE];
	__u64 mld_parse_errors;

	__u64 mcast_bytes[BR_MCAST_DIR_SIZE];
	__u64 mcast_packets[BR_MCAST_DIR_SIZE];
};
#endif /* _UAPI_LINUX_IF_BRIDGE_H */
+1 −0
Original line number Diff line number Diff line
@@ -273,6 +273,7 @@ enum {
	IFLA_BR_VLAN_DEFAULT_PVID,
	IFLA_BR_PAD,
	IFLA_BR_VLAN_STATS_ENABLED,
	IFLA_BR_MCAST_STATS_ENABLED,
	__IFLA_BR_MAX,
};

+9 −1
Original line number Diff line number Diff line
@@ -104,8 +104,16 @@ static int br_dev_init(struct net_device *dev)
		return -ENOMEM;

	err = br_vlan_init(br);
	if (err)
	if (err) {
		free_percpu(br->stats);
		return err;
	}

	err = br_multicast_init_stats(br);
	if (err) {
		free_percpu(br->stats);
		br_vlan_flush(br);
	}
	br_set_lockdep_class(dev);

	return err;
+12 −1
Original line number Diff line number Diff line
@@ -198,8 +198,10 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
					   struct sk_buff *skb),
		     bool unicast)
{
	struct net_bridge_port *p;
	u8 igmp_type = br_multicast_igmp_type(skb);
	__be16 proto = skb->protocol;
	struct net_bridge_port *prev;
	struct net_bridge_port *p;

	prev = NULL;

@@ -218,6 +220,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
		prev = maybe_deliver(prev, p, skb, __packet_hook);
		if (IS_ERR(prev))
			goto out;
		if (prev == p)
			br_multicast_count(p->br, p, proto, igmp_type,
					   BR_MCAST_DIR_TX);
	}

	if (!prev)
@@ -257,9 +262,12 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
					struct sk_buff *skb))
{
	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
	u8 igmp_type = br_multicast_igmp_type(skb);
	struct net_bridge *br = netdev_priv(dev);
	struct net_bridge_port *prev = NULL;
	struct net_bridge_port_group *p;
	__be16 proto = skb->protocol;

	struct hlist_node *rp;

	rp = rcu_dereference(hlist_first_rcu(&br->router_list));
@@ -277,6 +285,9 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
		prev = maybe_deliver(prev, port, skb, __packet_hook);
		if (IS_ERR(prev))
			goto out;
		if (prev == port)
			br_multicast_count(port->br, port, proto, igmp_type,
					   BR_MCAST_DIR_TX);

		if ((unsigned long)lport >= (unsigned long)port)
			p = rcu_dereference(p->next);
+7 −2
Original line number Diff line number Diff line
@@ -345,8 +345,8 @@ static int find_portno(struct net_bridge *br)
static struct net_bridge_port *new_nbp(struct net_bridge *br,
				       struct net_device *dev)
{
	int index;
	struct net_bridge_port *p;
	int index, err;

	index = find_portno(br);
	if (index < 0)
@@ -366,7 +366,12 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
	br_init_port(p);
	br_set_state(p, BR_STATE_DISABLED);
	br_stp_port_timer_init(p);
	br_multicast_add_port(p);
	err = br_multicast_add_port(p);
	if (err) {
		dev_put(dev);
		kfree(p);
		p = ERR_PTR(err);
	}

	return p;
}
Loading