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

Commit e8194d4f authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bridge-per-vlan-stats'



Nikolay Aleksandrov says:

====================
bridge: per-vlan stats

This set adds support for bridge per-vlan statistics.
In order to be able to dump statistics for many vlans we need a way to
continue dumping after reaching maximum size, thus patches 01 and 02 extend
the new stats API with a per-device extended link stats attribute and
callback which can save its local state and continue where it left off
afterwards. I considered using the already existing "fill_xstats" callback
but it gets confusing since we need to separate the linkinfo dump from the
new stats api dump and adding a flag/argument to do that just looks messy.
I don't think the rtnl_link_ops size is an issue, so adding these seemed
like the cleaner approach.

Patches 03 and 04 add the stats support and netlink dump support
respectively. The stats accounting is controlled via a bridge option which
is default off, thus the performance impact is kept minimal.
I've tested this set with both old and modified iproute2, kmemleak on and
some traffic stress tests while adding/removing vlans and ports.

v3:
 - drop the RCU pvid patch and remove one pointer fetch as requested
 - make stats accounting optional with default to off, the option is in the
   same cache line as vlan_proto and vlan_enabled, so it is already fetched
   before the fast path check thus the performance impact is minimal, this
   also allows us to avoid one vlan lookup and return early when using pvid
 - rebased and retested

v2:
 - Improve the error checking, rename lidx to prividx and save the current
   idx user instead of restricting it to one in patch 01
 - squash patch 02 into 01 and remove the restriction
 - add callback descriptions, improve the size calculation and change the
   xstats message structure to have an embedding level per rtnl link type
   so we can avoid one call to get the link type (and thus filter on it)
   and also each link type can now have any number of private attributes
   inside
 - fix a problem where the vlan stats are not dumped if the bridge has 0
   vlans on it but has vlans on the ports, add bridge link type private
   attributes and also add paddings for future extensions to avoid at least
   a few netlink attributes and improve struct alignment
 - drop the is_skb_forwardable argument constifying patch as it's not
   needed anymore, but it's a nice cleanup which I'll send separately
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d1ac3b16 a60c0903
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -47,6 +47,9 @@ static inline int rtnl_msg_family(const struct nlmsghdr *nlh)
 *	@get_num_rx_queues: Function to determine number of receive queues
 *			    to create when creating a new device.
 *	@get_link_net: Function to get the i/o netns of the device
 *	@get_linkxstats_size: Function to calculate the required room for
 *			      dumping device-specific extended link stats
 *	@fill_linkxstats: Function to dump device-specific extended link stats
 */
struct rtnl_link_ops {
	struct list_head	list;
@@ -95,6 +98,10 @@ struct rtnl_link_ops {
						   const struct net_device *dev,
						   const struct net_device *slave_dev);
	struct net		*(*get_link_net)(const struct net_device *dev);
	size_t			(*get_linkxstats_size)(const struct net_device *dev);
	int			(*fill_linkxstats)(struct sk_buff *skb,
						   const struct net_device *dev,
						   int *prividx);
};

int __rtnl_link_register(struct rtnl_link_ops *ops);
+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 */
+14 −0
Original line number Diff line number Diff line
@@ -272,6 +272,7 @@ enum {
	IFLA_BR_NF_CALL_ARPTABLES,
	IFLA_BR_VLAN_DEFAULT_PVID,
	IFLA_BR_PAD,
	IFLA_BR_VLAN_STATS_ENABLED,
	__IFLA_BR_MAX,
};

@@ -810,6 +811,7 @@ struct if_stats_msg {
enum {
	IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
	IFLA_STATS_LINK_64,
	IFLA_STATS_LINK_XSTATS,
	__IFLA_STATS_MAX,
};

@@ -817,4 +819,16 @@ enum {

#define IFLA_STATS_FILTER_BIT(ATTR)	(1 << (ATTR - 1))

/* These are embedded into IFLA_STATS_LINK_XSTATS:
 * [IFLA_STATS_LINK_XSTATS]
 * -> [LINK_XSTATS_TYPE_xxx]
 *    -> [rtnl link type specific attributes]
 */
enum {
	LINK_XSTATS_TYPE_UNSPEC,
	LINK_XSTATS_TYPE_BRIDGE,
	__LINK_XSTATS_TYPE_MAX
};
#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)

#endif /* _UAPI_LINUX_IF_LINK_H */
+77 −1
Original line number Diff line number Diff line
@@ -850,6 +850,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
	[IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 },
	[IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 },
	[IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 },
	[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
};

static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -921,6 +922,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
		if (err)
			return err;
	}

	if (data[IFLA_BR_VLAN_STATS_ENABLED]) {
		__u8 vlan_stats = nla_get_u8(data[IFLA_BR_VLAN_STATS_ENABLED]);

		err = br_vlan_set_stats(br, vlan_stats);
		if (err)
			return err;
	}
#endif

	if (data[IFLA_BR_GROUP_FWD_MASK]) {
@@ -1082,6 +1091,7 @@ static size_t br_get_size(const struct net_device *brdev)
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
	       nla_total_size(sizeof(__be16)) +	/* IFLA_BR_VLAN_PROTOCOL */
	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_VLAN_DEFAULT_PVID */
	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_VLAN_STATS_ENABLED */
#endif
	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_GROUP_FWD_MASK */
	       nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_ROOT_ID */
@@ -1167,7 +1177,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)

#ifdef CONFIG_BRIDGE_VLAN_FILTERING
	if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto) ||
	    nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid))
	    nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid) ||
	    nla_put_u8(skb, IFLA_BR_VLAN_STATS_ENABLED, br->vlan_stats_enabled))
		return -EMSGSIZE;
#endif
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
@@ -1223,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,
@@ -1241,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,
+18 −0
Original line number Diff line number Diff line
@@ -77,12 +77,21 @@ struct bridge_mcast_querier {
};
#endif

struct br_vlan_stats {
	u64 rx_bytes;
	u64 rx_packets;
	u64 tx_bytes;
	u64 tx_packets;
	struct u64_stats_sync syncp;
};

/**
 * struct net_bridge_vlan - per-vlan entry
 *
 * @vnode: rhashtable member
 * @vid: VLAN id
 * @flags: bridge vlan flags
 * @stats: per-cpu VLAN statistics
 * @br: if MASTER flag set, this points to a bridge struct
 * @port: if MASTER flag unset, this points to a port struct
 * @refcnt: if MASTER flag set, this is bumped for each port referencing it
@@ -100,6 +109,7 @@ struct net_bridge_vlan {
	struct rhash_head		vnode;
	u16				vid;
	u16				flags;
	struct br_vlan_stats __percpu	*stats;
	union {
		struct net_bridge	*br;
		struct net_bridge_port	*port;
@@ -342,6 +352,7 @@ struct net_bridge
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
	struct net_bridge_vlan_group	__rcu *vlgrp;
	u8				vlan_enabled;
	u8				vlan_stats_enabled;
	__be16				vlan_proto;
	u16				default_pvid;
#endif
@@ -691,6 +702,7 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto);
int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
int br_vlan_set_stats(struct net_bridge *br, unsigned long val);
int br_vlan_init(struct net_bridge *br);
int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);
@@ -699,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)
@@ -881,6 +895,10 @@ 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 {
Loading