Commit 641f7e40 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge-igmp-stats'

Nikolay Aleksandrov says:

====================
net: bridge: add support for IGMP/MLD stats

This patchset adds support for the new IFLA_STATS_LINK_XSTATS_SLAVE
attribute which can be used with RTM_GETSTATS in order to export per-slave
statistics. It works by passing the attribute to the linkxstats callback
and if the callback user supports it - it should dump that slave's stats.
This is much more scalable and permits us to request only a single port's
statistics instead of dumping everything every time.
The second patch adds support for per-port IGMP/MLD statistics and uses
the new API to export them for the bridge and its ports. The stats are
made in a very lightweight manner, the normal fast-path is not affected
at all and the flood paths (br_flood/br_multicast_flood) are only affected
if the packet is IGMP and the IGMP stats have been enabled using cache-hot
data for the check.

v2: Patch 01 is new, patch 02 has been reworked to use the new API, also
in addition counters for IGMP/MLD parse errors have been added and members
are added for per-port multicast traffic stats. The multicast counting has
been slightly optimized (moved the br_multicast_count inside the IPv4/6
IGMP functions after the checks for IGMP traffic) to avoid one conditional
that was on all of the multicast traffic path (both IGMP and other).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 545c321b 1080ab95
...@@ -98,10 +98,11 @@ struct rtnl_link_ops { ...@@ -98,10 +98,11 @@ struct rtnl_link_ops {
const struct net_device *dev, const struct net_device *dev,
const struct net_device *slave_dev); const struct net_device *slave_dev);
struct net *(*get_link_net)(const struct net_device *dev); struct net *(*get_link_net)(const struct net_device *dev);
size_t (*get_linkxstats_size)(const struct net_device *dev); size_t (*get_linkxstats_size)(const struct net_device *dev,
int attr);
int (*fill_linkxstats)(struct sk_buff *skb, int (*fill_linkxstats)(struct sk_buff *skb,
const struct net_device *dev, const struct net_device *dev,
int *prividx); int *prividx, int attr);
}; };
int __rtnl_link_register(struct rtnl_link_ops *ops); int __rtnl_link_register(struct rtnl_link_ops *ops);
......
...@@ -247,8 +247,34 @@ enum { ...@@ -247,8 +247,34 @@ enum {
enum { enum {
BRIDGE_XSTATS_UNSPEC, BRIDGE_XSTATS_UNSPEC,
BRIDGE_XSTATS_VLAN, BRIDGE_XSTATS_VLAN,
BRIDGE_XSTATS_MCAST,
BRIDGE_XSTATS_PAD,
__BRIDGE_XSTATS_MAX __BRIDGE_XSTATS_MAX
}; };
#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) #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 */ #endif /* _UAPI_LINUX_IF_BRIDGE_H */
...@@ -273,6 +273,7 @@ enum { ...@@ -273,6 +273,7 @@ enum {
IFLA_BR_VLAN_DEFAULT_PVID, IFLA_BR_VLAN_DEFAULT_PVID,
IFLA_BR_PAD, IFLA_BR_PAD,
IFLA_BR_VLAN_STATS_ENABLED, IFLA_BR_VLAN_STATS_ENABLED,
IFLA_BR_MCAST_STATS_ENABLED,
__IFLA_BR_MAX, __IFLA_BR_MAX,
}; };
...@@ -822,6 +823,7 @@ enum { ...@@ -822,6 +823,7 @@ enum {
IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
IFLA_STATS_LINK_64, IFLA_STATS_LINK_64,
IFLA_STATS_LINK_XSTATS, IFLA_STATS_LINK_XSTATS,
IFLA_STATS_LINK_XSTATS_SLAVE,
__IFLA_STATS_MAX, __IFLA_STATS_MAX,
}; };
......
...@@ -104,8 +104,16 @@ static int br_dev_init(struct net_device *dev) ...@@ -104,8 +104,16 @@ static int br_dev_init(struct net_device *dev)
return -ENOMEM; return -ENOMEM;
err = br_vlan_init(br); err = br_vlan_init(br);
if (err) if (err) {
free_percpu(br->stats); 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); br_set_lockdep_class(dev);
return err; return err;
......
...@@ -198,8 +198,10 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, ...@@ -198,8 +198,10 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
struct sk_buff *skb), struct sk_buff *skb),
bool unicast) 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 *prev;
struct net_bridge_port *p;
prev = NULL; prev = NULL;
...@@ -218,6 +220,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, ...@@ -218,6 +220,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
prev = maybe_deliver(prev, p, skb, __packet_hook); prev = maybe_deliver(prev, p, skb, __packet_hook);
if (IS_ERR(prev)) if (IS_ERR(prev))
goto out; goto out;
if (prev == p)
br_multicast_count(p->br, p, proto, igmp_type,
BR_MCAST_DIR_TX);
} }
if (!prev) if (!prev)
...@@ -257,9 +262,12 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, ...@@ -257,9 +262,12 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
struct sk_buff *skb)) struct sk_buff *skb))
{ {
struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; 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 *br = netdev_priv(dev);
struct net_bridge_port *prev = NULL; struct net_bridge_port *prev = NULL;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
__be16 proto = skb->protocol;
struct hlist_node *rp; struct hlist_node *rp;
rp = rcu_dereference(hlist_first_rcu(&br->router_list)); rp = rcu_dereference(hlist_first_rcu(&br->router_list));
...@@ -277,6 +285,9 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, ...@@ -277,6 +285,9 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
prev = maybe_deliver(prev, port, skb, __packet_hook); prev = maybe_deliver(prev, port, skb, __packet_hook);
if (IS_ERR(prev)) if (IS_ERR(prev))
goto out; 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) if ((unsigned long)lport >= (unsigned long)port)
p = rcu_dereference(p->next); p = rcu_dereference(p->next);
......
...@@ -345,8 +345,8 @@ static int find_portno(struct net_bridge *br) ...@@ -345,8 +345,8 @@ static int find_portno(struct net_bridge *br)
static struct net_bridge_port *new_nbp(struct net_bridge *br, static struct net_bridge_port *new_nbp(struct net_bridge *br,
struct net_device *dev) struct net_device *dev)
{ {
int index;
struct net_bridge_port *p; struct net_bridge_port *p;
int index, err;
index = find_portno(br); index = find_portno(br);
if (index < 0) if (index < 0)
...@@ -366,7 +366,12 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, ...@@ -366,7 +366,12 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
br_init_port(p); br_init_port(p);
br_set_state(p, BR_STATE_DISABLED); br_set_state(p, BR_STATE_DISABLED);
br_stp_port_timer_init(p); 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; return p;
} }
......
...@@ -60,6 +60,9 @@ static int br_pass_frame_up(struct sk_buff *skb) ...@@ -60,6 +60,9 @@ static int br_pass_frame_up(struct sk_buff *skb)
skb = br_handle_vlan(br, vg, skb); skb = br_handle_vlan(br, vg, skb);
if (!skb) if (!skb)
return NET_RX_DROP; return NET_RX_DROP;
/* update the multicast stats if the packet is IGMP/MLD */
br_multicast_count(br, NULL, skb->protocol, br_multicast_igmp_type(skb),
BR_MCAST_DIR_TX);
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(indev), NULL, skb, indev, NULL, dev_net(indev), NULL, skb, indev, NULL,
......
This diff is collapsed.
...@@ -851,6 +851,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { ...@@ -851,6 +851,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
[IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 }, [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 },
[IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 }, [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 },
[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 }, [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
[IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
}; };
static int br_changelink(struct net_device *brdev, struct nlattr *tb[], static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
...@@ -1055,6 +1056,13 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], ...@@ -1055,6 +1056,13 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
br->multicast_startup_query_interval = clock_t_to_jiffies(val); br->multicast_startup_query_interval = clock_t_to_jiffies(val);
} }
if (data[IFLA_BR_MCAST_STATS_ENABLED]) {
__u8 mcast_stats;
mcast_stats = nla_get_u8(data[IFLA_BR_MCAST_STATS_ENABLED]);
br->multicast_stats_enabled = !!mcast_stats;
}
#endif #endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
if (data[IFLA_BR_NF_CALL_IPTABLES]) { if (data[IFLA_BR_NF_CALL_IPTABLES]) {
...@@ -1110,6 +1118,7 @@ static size_t br_get_size(const struct net_device *brdev) ...@@ -1110,6 +1118,7 @@ static size_t br_get_size(const struct net_device *brdev)
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERY_USE_IFADDR */
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_QUERIER */
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_STATS_ENABLED */
nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_ELASTICITY */
nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_HASH_MAX */
nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */ nla_total_size(sizeof(u32)) + /* IFLA_BR_MCAST_LAST_MEMBER_CNT */
...@@ -1187,6 +1196,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) ...@@ -1187,6 +1196,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR, nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
br->multicast_query_use_ifaddr) || br->multicast_query_use_ifaddr) ||
nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) || nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) ||
nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
br->multicast_stats_enabled) ||
nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY,
br->hash_elasticity) || br->hash_elasticity) ||
nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) || nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
...@@ -1234,7 +1245,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) ...@@ -1234,7 +1245,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
return 0; return 0;
} }
static size_t br_get_linkxstats_size(const struct net_device *dev) static size_t bridge_get_linkxstats_size(const struct net_device *dev)
{ {
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
...@@ -1242,53 +1253,88 @@ static size_t br_get_linkxstats_size(const struct net_device *dev) ...@@ -1242,53 +1253,88 @@ static size_t br_get_linkxstats_size(const struct net_device *dev)
int numvls = 0; int numvls = 0;
vg = br_vlan_group(br); vg = br_vlan_group(br);
if (!vg) if (vg) {
return 0; /* we need to count all, even placeholder entries */
list_for_each_entry(v, &vg->vlan_list, vlist)
/* we need to count all, even placeholder entries */ numvls++;
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)) + return numvls * nla_total_size(sizeof(struct bridge_vlan_xstats)) +
nla_total_size(sizeof(struct br_mcast_stats)) +
nla_total_size(0); nla_total_size(0);
} }
static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev, static size_t brport_get_linkxstats_size(const struct net_device *dev)
int *prividx) {
return nla_total_size(sizeof(struct br_mcast_stats)) +
nla_total_size(0);
}
static size_t br_get_linkxstats_size(const struct net_device *dev, int attr)
{
size_t retsize = 0;
switch (attr) {
case IFLA_STATS_LINK_XSTATS:
retsize = bridge_get_linkxstats_size(dev);
break;
case IFLA_STATS_LINK_XSTATS_SLAVE:
retsize = brport_get_linkxstats_size(dev);
break;
}
return retsize;
}
static int bridge_fill_linkxstats(struct sk_buff *skb,
const struct net_device *dev,
int *prividx)
{ {
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct nlattr *nla __maybe_unused;
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;
struct nlattr *nest; struct nlattr *nest;
int vl_idx = 0; int vl_idx = 0;
vg = br_vlan_group(br);
if (!vg)
goto out;
nest = nla_nest_start(skb, LINK_XSTATS_TYPE_BRIDGE); nest = nla_nest_start(skb, LINK_XSTATS_TYPE_BRIDGE);
if (!nest) if (!nest)
return -EMSGSIZE; 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) vg = br_vlan_group(br);
continue; if (vg) {
memset(&vxi, 0, sizeof(vxi)); list_for_each_entry(v, &vg->vlan_list, vlist) {
vxi.vid = v->vid; struct bridge_vlan_xstats vxi;
br_vlan_get_stats(v, &stats); struct br_vlan_stats stats;
vxi.rx_bytes = stats.rx_bytes;
vxi.rx_packets = stats.rx_packets; if (++vl_idx < *prividx)
vxi.tx_bytes = stats.tx_bytes; continue;
vxi.tx_packets = stats.tx_packets; memset(&vxi, 0, sizeof(vxi));
vxi.vid = v->vid;
if (nla_put(skb, BRIDGE_XSTATS_VLAN, sizeof(vxi), &vxi)) 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;
}
}
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
if (++vl_idx >= *prividx) {
nla = nla_reserve_64bit(skb, BRIDGE_XSTATS_MCAST,
sizeof(struct br_mcast_stats),
BRIDGE_XSTATS_PAD);
if (!nla)
goto nla_put_failure; goto nla_put_failure;
br_multicast_get_stats(br, NULL, nla_data(nla));
} }
#endif
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
*prividx = 0; *prividx = 0;
out:
return 0; return 0;
nla_put_failure: nla_put_failure:
...@@ -1298,6 +1344,52 @@ static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev, ...@@ -1298,6 +1344,52 @@ static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
return -EMSGSIZE; return -EMSGSIZE;
} }
static int brport_fill_linkxstats(struct sk_buff *skb,
const struct net_device *dev,
int *prividx)
{
struct net_bridge_port *p = br_port_get_rtnl(dev);
struct nlattr *nla __maybe_unused;
struct nlattr *nest;
if (!p)
return 0;
nest = nla_nest_start(skb, LINK_XSTATS_TYPE_BRIDGE);
if (!nest)
return -EMSGSIZE;
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
nla = nla_reserve_64bit(skb, BRIDGE_XSTATS_MCAST,
sizeof(struct br_mcast_stats),
BRIDGE_XSTATS_PAD);
if (!nla) {
nla_nest_end(skb, nest);
return -EMSGSIZE;
}
br_multicast_get_stats(p->br, p, nla_data(nla));
#endif
nla_nest_end(skb, nest);
return 0;
}
static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
int *prividx, int attr)
{
int ret = -EINVAL;
switch (attr) {
case IFLA_STATS_LINK_XSTATS:
ret = bridge_fill_linkxstats(skb, dev, prividx);
break;
case IFLA_STATS_LINK_XSTATS_SLAVE:
ret = brport_fill_linkxstats(skb, dev, prividx);
break;
}
return ret;
}
static struct rtnl_af_ops br_af_ops __read_mostly = { static struct rtnl_af_ops br_af_ops __read_mostly = {
.family = AF_BRIDGE, .family = AF_BRIDGE,
.get_link_af_size = br_get_link_af_size_filtered, .get_link_af_size = br_get_link_af_size_filtered,
......
...@@ -75,6 +75,12 @@ struct bridge_mcast_querier { ...@@ -75,6 +75,12 @@ struct bridge_mcast_querier {
struct br_ip addr; struct br_ip addr;
struct net_bridge_port __rcu *port; struct net_bridge_port __rcu *port;
}; };
/* IGMP/MLD statistics */
struct bridge_mcast_stats {
struct br_mcast_stats mstats;
struct u64_stats_sync syncp;
};
#endif #endif
struct br_vlan_stats { struct br_vlan_stats {
...@@ -229,6 +235,7 @@ struct net_bridge_port ...@@ -229,6 +235,7 @@ struct net_bridge_port
struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_own_query ip6_own_query;
#endif /* IS_ENABLED(CONFIG_IPV6) */ #endif /* IS_ENABLED(CONFIG_IPV6) */
unsigned char multicast_router; unsigned char multicast_router;
struct bridge_mcast_stats __percpu *mcast_stats;
struct timer_list multicast_router_timer; struct timer_list multicast_router_timer;
struct hlist_head mglist; struct hlist_head mglist;
struct hlist_node rlist; struct hlist_node rlist;
...@@ -315,6 +322,7 @@ struct net_bridge ...@@ -315,6 +322,7 @@ struct net_bridge
u8 multicast_querier:1; u8 multicast_querier:1;
u8 multicast_query_use_ifaddr:1; u8 multicast_query_use_ifaddr:1;
u8 has_ipv6_addr:1; u8 has_ipv6_addr:1;
u8 multicast_stats_enabled:1;
u32 hash_elasticity; u32 hash_elasticity;
u32 hash_max; u32 hash_max;
...@@ -337,6 +345,7 @@ struct net_bridge ...@@ -337,6 +345,7 @@ struct net_bridge
struct bridge_mcast_other_query ip4_other_query; struct bridge_mcast_other_query ip4_other_query;
struct bridge_mcast_own_query ip4_own_query; struct bridge_mcast_own_query ip4_own_query;
struct bridge_mcast_querier ip4_querier; struct bridge_mcast_querier ip4_querier;
struct bridge_mcast_stats __percpu *mcast_stats;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
struct bridge_mcast_other_query ip6_other_query; struct bridge_mcast_other_query ip6_other_query;
struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_own_query ip6_own_query;
...@@ -543,7 +552,7 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, ...@@ -543,7 +552,7 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
struct sk_buff *skb, u16 vid); struct sk_buff *skb, u16 vid);
struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
struct sk_buff *skb, u16 vid); struct sk_buff *skb, u16 vid);
void br_multicast_add_port(struct net_bridge_port *port); int br_multicast_add_port(struct net_bridge_port *port);
void br_multicast_del_port(struct net_bridge_port *port); void br_multicast_del_port(struct net_bridge_port *port);
void br_multicast_enable_port(struct net_bridge_port *port); void br_multicast_enable_port(struct net_bridge_port *port);
void br_multicast_disable_port(struct net_bridge_port *port); void br_multicast_disable_port(struct net_bridge_port *port);
...@@ -576,6 +585,12 @@ void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, ...@@ -576,6 +585,12 @@ void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 flags); struct br_ip *group, int type, u8 flags);
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
int type); int type);
void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
__be16 proto, u8 type, u8 dir);
int br_multicast_init_stats(struct net_bridge *br);
void br_multicast_get_stats(const struct net_bridge *br,
const struct net_bridge_port *p,
struct br_mcast_stats *dest);
#define mlock_dereference(X, br) \ #define mlock_dereference(X, br) \
rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
...@@ -623,6 +638,11 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br, ...@@ -623,6 +638,11 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
return false; return false;
} }
} }
static inline int br_multicast_igmp_type(const struct sk_buff *skb)
{
return BR_INPUT_SKB_CB(skb)->igmp;
}
#else #else
static inline int br_multicast_rcv(struct net_bridge *br, static inline int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
...@@ -638,8 +658,9 @@ static inline struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, ...@@ -638,8 +658,9 @@ static inline struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
return NULL; return NULL;
} }
static inline void br_multicast_add_port(struct net_bridge_port *port) static inline int br_multicast_add_port(struct net_bridge_port *port)
{ {
return 0;
} }
static inline void br_multicast_del_port(struct net_bridge_port *port) static inline void br_multicast_del_port(struct net_bridge_port *port)
...@@ -695,6 +716,22 @@ static inline void br_mdb_init(void) ...@@ -695,6 +716,22 @@ static inline void br_mdb_init(void)
static inline void br_mdb_uninit(void) static inline void br_mdb_uninit(void)
{ {
} }
static inline void br_multicast_count(struct net_bridge *br,
const struct net_bridge_port *p,
__be16 proto, u8 type, u8 dir)
{
}
static inline int br_multicast_init_stats(struct net_bridge *br)
{
return 0;
}
static inline int br_multicast_igmp_type(const struct sk_buff *skb)
{
return 0;
}
#endif #endif
/* br_vlan.c */ /* br_vlan.c */
......
...@@ -618,6 +618,30 @@ static ssize_t multicast_startup_query_interval_store( ...@@ -618,6 +618,30 @@ static ssize_t multicast_startup_query_interval_store(
return store_bridge_parm(d, buf, len, set_startup_query_interval); return store_bridge_parm(d, buf, len, set_startup_query_interval);
} }
static DEVICE_ATTR_RW(multicast_startup_query_interval); static DEVICE_ATTR_RW(multicast_startup_query_interval);
static ssize_t multicast_stats_enabled_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct net_bridge *br = to_bridge(d);
return sprintf(buf, "%u\n", br->multicast_stats_enabled);
}
static int set_stats_enabled(struct net_bridge *br, unsigned long val)
{
br->multicast_stats_enabled = !!val;
return 0;
}
static ssize_t multicast_stats_enabled_store(struct device *d,
struct device_attribute *attr,
const char *buf,
size_t len)
{
return store_bridge_parm(d, buf, len, set_stats_enabled);
}
static DEVICE_ATTR_RW(multicast_stats_enabled);
#endif #endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
static ssize_t nf_call_iptables_show( static ssize_t nf_call_iptables_show(
...@@ -784,6 +808,7 @@ static struct attribute *bridge_attrs[] = { ...@@ -784,6 +808,7 @@ static struct attribute *bridge_attrs[] = {
&dev_attr_multicast_query_interval.attr, &dev_attr_multicast_query_interval.attr,
&dev_attr_multicast_query_response_interval.attr, &dev_attr_multicast_query_response_interval.attr,
&dev_attr_multicast_startup_query_interval.attr, &dev_attr_multicast_startup_query_interval.attr,
&dev_attr_multicast_stats_enabled.attr,
#endif #endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
&dev_attr_nf_call_iptables.attr, &dev_attr_nf_call_iptables.attr,
......
...@@ -3519,7 +3519,32 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev, ...@@ -3519,7 +3519,32 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
if (!attr) if (!attr)
goto nla_put_failure; goto nla_put_failure;
err = ops->fill_linkxstats(skb, dev, prividx); err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
nla_nest_end(skb, attr);
if (err)
goto nla_put_failure;
*idxattr = 0;
}
}
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE,
*idxattr)) {
const struct rtnl_link_ops *ops = NULL;
const struct net_device *master;
master = netdev_master_upper_dev_get(dev);
if (master)
ops = master->rtnl_link_ops;
if (ops && ops->fill_linkxstats) {
int err;
*idxattr = IFLA_STATS_LINK_XSTATS_SLAVE;
attr = nla_nest_start(skb,
IFLA_STATS_LINK_XSTATS_SLAVE);
if (!attr)
goto nla_put_failure;
err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
nla_nest_end(skb, attr); nla_nest_end(skb, attr);
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
...@@ -3555,14 +3580,35 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, ...@@ -3555,14 +3580,35 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) { if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) {
const struct rtnl_link_ops *ops = dev->rtnl_link_ops; const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
int attr = IFLA_STATS_LINK_XSTATS;
if (ops && ops->get_linkxstats_size) { if (ops && ops->get_linkxstats_size) {
size += nla_total_size(ops->get_linkxstats_size(dev)); size += nla_total_size(ops->get_linkxstats_size(dev,
attr));
/* for IFLA_STATS_LINK_XSTATS */ /* for IFLA_STATS_LINK_XSTATS */
size += nla_total_size(0); size += nla_total_size(0);
} }
} }
if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE, 0)) {
struct net_device *_dev = (struct net_device *)dev;
const struct rtnl_link_ops *ops = NULL;
const struct net_device *master;
/* netdev_master_upper_dev_get can't take const */
master = netdev_master_upper_dev_get(_dev);
if (master)
ops = master->rtnl_link_ops;
if (ops && ops->get_linkxstats_size) {
int attr = IFLA_STATS_LINK_XSTATS_SLAVE;
size += nla_total_size(ops->get_linkxstats_size(dev,
attr));
/* for IFLA_STATS_LINK_XSTATS_SLAVE */
size += nla_total_size(0);
}
}
return size; return size;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment