Skip to content

Commit 9163a0f

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: add support for per-port vlan stats
This patch adds an option to have per-port vlan stats instead of the default global stats. The option can be set only when there are no port vlans in the bridge since we need to allocate the stats if it is set when vlans are being added to ports (and respectively free them when being deleted). Also bump RTNL_MAX_TYPE as the bridge is the largest user of options. The current stats design allows us to add these without any changes to the fast-path, it all comes down to the per-vlan stats pointer which, if this option is enabled, will be allocated for each port vlan instead of using the global bridge-wide one. CC: bridge@lists.linux-foundation.org CC: Roopa Prabhu <roopa@cumulusnetworks.com> Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 6660464 commit 9163a0f

File tree

6 files changed

+81
-4
lines changed

6 files changed

+81
-4
lines changed

include/uapi/linux/if_link.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ enum {
287287
IFLA_BR_MCAST_STATS_ENABLED,
288288
IFLA_BR_MCAST_IGMP_VERSION,
289289
IFLA_BR_MCAST_MLD_VERSION,
290+
IFLA_BR_VLAN_STATS_PER_PORT,
290291
__IFLA_BR_MAX,
291292
};
292293

net/bridge/br_netlink.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
10341034
[IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
10351035
[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
10361036
[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
1037+
[IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
10371038
};
10381039

10391040
static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1114,6 +1115,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
11141115
if (err)
11151116
return err;
11161117
}
1118+
1119+
if (data[IFLA_BR_VLAN_STATS_PER_PORT]) {
1120+
__u8 per_port = nla_get_u8(data[IFLA_BR_VLAN_STATS_PER_PORT]);
1121+
1122+
err = br_vlan_set_stats_per_port(br, per_port);
1123+
if (err)
1124+
return err;
1125+
}
11171126
#endif
11181127

11191128
if (data[IFLA_BR_GROUP_FWD_MASK]) {
@@ -1327,6 +1336,7 @@ static size_t br_get_size(const struct net_device *brdev)
13271336
nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */
13281337
nla_total_size(sizeof(u16)) + /* IFLA_BR_VLAN_DEFAULT_PVID */
13291338
nla_total_size(sizeof(u8)) + /* IFLA_BR_VLAN_STATS_ENABLED */
1339+
nla_total_size(sizeof(u8)) + /* IFLA_BR_VLAN_STATS_PER_PORT */
13301340
#endif
13311341
nla_total_size(sizeof(u16)) + /* IFLA_BR_GROUP_FWD_MASK */
13321342
nla_total_size(sizeof(struct ifla_bridge_id)) + /* IFLA_BR_ROOT_ID */
@@ -1417,7 +1427,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
14171427
if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto) ||
14181428
nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid) ||
14191429
nla_put_u8(skb, IFLA_BR_VLAN_STATS_ENABLED,
1420-
br_opt_get(br, BROPT_VLAN_STATS_ENABLED)))
1430+
br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) ||
1431+
nla_put_u8(skb, IFLA_BR_VLAN_STATS_PER_PORT,
1432+
br_opt_get(br, IFLA_BR_VLAN_STATS_PER_PORT)))
14211433
return -EMSGSIZE;
14221434
#endif
14231435
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING

net/bridge/br_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ enum net_bridge_opts {
320320
BROPT_HAS_IPV6_ADDR,
321321
BROPT_NEIGH_SUPPRESS_ENABLED,
322322
BROPT_MTU_SET_BY_USER,
323+
BROPT_VLAN_STATS_PER_PORT,
323324
};
324325

325326
struct net_bridge {
@@ -859,6 +860,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
859860
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto);
860861
int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
861862
int br_vlan_set_stats(struct net_bridge *br, unsigned long val);
863+
int br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val);
862864
int br_vlan_init(struct net_bridge *br);
863865
int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
864866
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);

net/bridge/br_sysfs_br.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,22 @@ static ssize_t vlan_stats_enabled_store(struct device *d,
803803
return store_bridge_parm(d, buf, len, br_vlan_set_stats);
804804
}
805805
static DEVICE_ATTR_RW(vlan_stats_enabled);
806+
807+
static ssize_t vlan_stats_per_port_show(struct device *d,
808+
struct device_attribute *attr,
809+
char *buf)
810+
{
811+
struct net_bridge *br = to_bridge(d);
812+
return sprintf(buf, "%u\n", br_opt_get(br, BROPT_VLAN_STATS_PER_PORT));
813+
}
814+
815+
static ssize_t vlan_stats_per_port_store(struct device *d,
816+
struct device_attribute *attr,
817+
const char *buf, size_t len)
818+
{
819+
return store_bridge_parm(d, buf, len, br_vlan_set_stats_per_port);
820+
}
821+
static DEVICE_ATTR_RW(vlan_stats_per_port);
806822
#endif
807823

808824
static struct attribute *bridge_attrs[] = {
@@ -856,6 +872,7 @@ static struct attribute *bridge_attrs[] = {
856872
&dev_attr_vlan_protocol.attr,
857873
&dev_attr_default_pvid.attr,
858874
&dev_attr_vlan_stats_enabled.attr,
875+
&dev_attr_vlan_stats_per_port.attr,
859876
#endif
860877
NULL
861878
};

net/bridge/br_vlan.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,19 @@ static void br_vlan_put_master(struct net_bridge_vlan *masterv)
190190
}
191191
}
192192

193+
static void nbp_vlan_rcu_free(struct rcu_head *rcu)
194+
{
195+
struct net_bridge_vlan *v;
196+
197+
v = container_of(rcu, struct net_bridge_vlan, rcu);
198+
WARN_ON(br_vlan_is_master(v));
199+
/* if we had per-port stats configured then free them here */
200+
if (v->brvlan->stats != v->stats)
201+
free_percpu(v->stats);
202+
v->stats = NULL;
203+
kfree(v);
204+
}
205+
193206
/* This is the shared VLAN add function which works for both ports and bridge
194207
* devices. There are four possible calls to this function in terms of the
195208
* vlan entry type:
@@ -245,7 +258,15 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
245258
if (!masterv)
246259
goto out_filt;
247260
v->brvlan = masterv;
248-
v->stats = masterv->stats;
261+
if (br_opt_get(br, BROPT_VLAN_STATS_PER_PORT)) {
262+
v->stats = netdev_alloc_pcpu_stats(struct br_vlan_stats);
263+
if (!v->stats) {
264+
err = -ENOMEM;
265+
goto out_filt;
266+
}
267+
} else {
268+
v->stats = masterv->stats;
269+
}
249270
} else {
250271
err = br_switchdev_port_vlan_add(dev, v->vid, flags);
251272
if (err && err != -EOPNOTSUPP)
@@ -329,7 +350,7 @@ static int __vlan_del(struct net_bridge_vlan *v)
329350
rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
330351
br_vlan_rht_params);
331352
__vlan_del_list(v);
332-
kfree_rcu(v, rcu);
353+
call_rcu(&v->rcu, nbp_vlan_rcu_free);
333354
}
334355

335356
br_vlan_put_master(masterv);
@@ -830,6 +851,30 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val)
830851
return 0;
831852
}
832853

854+
int br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val)
855+
{
856+
struct net_bridge_port *p;
857+
858+
/* allow to change the option if there are no port vlans configured */
859+
list_for_each_entry(p, &br->port_list, list) {
860+
struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
861+
862+
if (vg->num_vlans)
863+
return -EBUSY;
864+
}
865+
866+
switch (val) {
867+
case 0:
868+
case 1:
869+
br_opt_toggle(br, BROPT_VLAN_STATS_PER_PORT, !!val);
870+
break;
871+
default:
872+
return -EINVAL;
873+
}
874+
875+
return 0;
876+
}
877+
833878
static bool vlan_default_pvid(struct net_bridge_vlan_group *vg, u16 vid)
834879
{
835880
struct net_bridge_vlan *v;

net/core/rtnetlink.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
#include <net/rtnetlink.h>
6060
#include <net/net_namespace.h>
6161

62-
#define RTNL_MAX_TYPE 48
62+
#define RTNL_MAX_TYPE 49
6363
#define RTNL_SLAVE_MAX_TYPE 36
6464

6565
struct rtnl_link {

0 commit comments

Comments
 (0)