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

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

net: bridge: add bitfield for options and convert vlan opts



Bridge options have usually been added as separate fields all over the
net_bridge struct taking up space and ending up in different cache lines.
Let's move them to a single bitfield to save up space and speedup lookups.
This patch adds a simple API for option modifying and retrieving using
bitops and converts the first user of the API - the bridge vlan options
(vlan_enabled and vlan_stats_enabled).

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Reviewed-by: default avatarStephen Hemminger <stephen@networkplumber.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1c1cb6d0
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -175,6 +175,22 @@ static struct notifier_block br_switchdev_notifier = {
	.notifier_call = br_switchdev_event,
};

void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
{
	bool cur = !!br_opt_get(br, opt);

	br_debug(br, "toggle option: %d state: %d -> %d\n",
		 opt, cur, on);

	if (cur == on)
		return;

	if (on)
		set_bit(opt, &br->options);
	else
		clear_bit(opt, &br->options);
}

static void __net_exit br_net_exit(struct net *net)
{
	struct net_device *dev;
+2 −1
Original line number Diff line number Diff line
@@ -1416,7 +1416,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_u8(skb, IFLA_BR_VLAN_STATS_ENABLED, br->vlan_stats_enabled))
	    nla_put_u8(skb, IFLA_BR_VLAN_STATS_ENABLED,
		       br_opt_get(br, BROPT_VLAN_STATS_ENABLED)))
		return -EMSGSIZE;
#endif
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+14 −2
Original line number Diff line number Diff line
@@ -306,16 +306,20 @@ static inline struct net_bridge_port *br_port_get_rtnl_rcu(const struct net_devi
		rcu_dereference_rtnl(dev->rx_handler_data) : NULL;
}

enum net_bridge_opts {
	BROPT_VLAN_ENABLED,
	BROPT_VLAN_STATS_ENABLED,
};

struct net_bridge {
	spinlock_t			lock;
	spinlock_t			hash_lock;
	struct list_head		port_list;
	struct net_device		*dev;
	struct pcpu_sw_netstats		__percpu *stats;
	unsigned long			options;
	/* These fields are accessed on each packet */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
	u8				vlan_enabled;
	u8				vlan_stats_enabled;
	__be16				vlan_proto;
	u16				default_pvid;
	struct net_bridge_vlan_group	__rcu *vlgrp;
@@ -489,6 +493,14 @@ static inline bool br_vlan_should_use(const struct net_bridge_vlan *v)
	return true;
}

static inline int br_opt_get(const struct net_bridge *br,
			     enum net_bridge_opts opt)
{
	return test_bit(opt, &br->options);
}

void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on);

/* br_device.c */
void br_dev_setup(struct net_device *dev);
void br_dev_delete(struct net_device *dev, struct list_head *list);
+2 −2
Original line number Diff line number Diff line
@@ -743,7 +743,7 @@ static ssize_t vlan_filtering_show(struct device *d,
				   char *buf)
{
	struct net_bridge *br = to_bridge(d);
	return sprintf(buf, "%d\n", br->vlan_enabled);
	return sprintf(buf, "%d\n", br_opt_get(br, BROPT_VLAN_ENABLED));
}

static ssize_t vlan_filtering_store(struct device *d,
@@ -791,7 +791,7 @@ static ssize_t vlan_stats_enabled_show(struct device *d,
				       char *buf)
{
	struct net_bridge *br = to_bridge(d);
	return sprintf(buf, "%u\n", br->vlan_stats_enabled);
	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_VLAN_STATS_ENABLED));
}

static ssize_t vlan_stats_enabled_store(struct device *d,
+15 −13
Original line number Diff line number Diff line
@@ -386,7 +386,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
			return NULL;
		}
	}
	if (br->vlan_stats_enabled) {
	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
		stats = this_cpu_ptr(v->stats);
		u64_stats_update_begin(&stats->syncp);
		stats->tx_bytes += skb->len;
@@ -475,14 +475,14 @@ static bool __allowed_ingress(const struct net_bridge *br,
			skb->vlan_tci |= pvid;

		/* if stats are disabled we can avoid the lookup */
		if (!br->vlan_stats_enabled)
		if (!br_opt_get(br, BROPT_VLAN_STATS_ENABLED))
			return true;
	}
	v = br_vlan_find(vg, *vid);
	if (!v || !br_vlan_should_use(v))
		goto drop;

	if (br->vlan_stats_enabled) {
	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
		stats = this_cpu_ptr(v->stats);
		u64_stats_update_begin(&stats->syncp);
		stats->rx_bytes += skb->len;
@@ -504,7 +504,7 @@ bool br_allowed_ingress(const struct net_bridge *br,
	/* If VLAN filtering is disabled on the bridge, all packets are
	 * permitted.
	 */
	if (!br->vlan_enabled) {
	if (!br_opt_get(br, BROPT_VLAN_ENABLED)) {
		BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
		return true;
	}
@@ -538,7 +538,7 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
	struct net_bridge *br = p->br;

	/* If filtering was disabled at input, let it pass. */
	if (!br->vlan_enabled)
	if (!br_opt_get(br, BROPT_VLAN_ENABLED))
		return true;

	vg = nbp_vlan_group_rcu(p);
@@ -699,7 +699,8 @@ static void recalculate_group_addr(struct net_bridge *br)
		return;

	spin_lock_bh(&br->lock);
	if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) {
	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
	    br->vlan_proto == htons(ETH_P_8021Q)) {
		/* Bridge Group Address */
		br->group_addr[5] = 0x00;
	} else { /* vlan_enabled && ETH_P_8021AD */
@@ -712,7 +713,8 @@ static void recalculate_group_addr(struct net_bridge *br)
/* Must be protected by RTNL. */
void br_recalculate_fwd_mask(struct net_bridge *br)
{
	if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q))
	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
	    br->vlan_proto == htons(ETH_P_8021Q))
		br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
	else /* vlan_enabled && ETH_P_8021AD */
		br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
@@ -729,14 +731,14 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
	};
	int err;

	if (br->vlan_enabled == val)
	if (br_opt_get(br, BROPT_VLAN_ENABLED) == !!val)
		return 0;

	err = switchdev_port_attr_set(br->dev, &attr);
	if (err && err != -EOPNOTSUPP)
		return err;

	br->vlan_enabled = val;
	br_opt_toggle(br, BROPT_VLAN_ENABLED, !!val);
	br_manage_promisc(br);
	recalculate_group_addr(br);
	br_recalculate_fwd_mask(br);
@@ -753,7 +755,7 @@ bool br_vlan_enabled(const struct net_device *dev)
{
	struct net_bridge *br = netdev_priv(dev);

	return !!br->vlan_enabled;
	return br_opt_get(br, BROPT_VLAN_ENABLED);
}
EXPORT_SYMBOL_GPL(br_vlan_enabled);

@@ -819,7 +821,7 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val)
	switch (val) {
	case 0:
	case 1:
		br->vlan_stats_enabled = val;
		br_opt_toggle(br, BROPT_VLAN_STATS_ENABLED, !!val);
		break;
	default:
		return -EINVAL;
@@ -964,7 +966,7 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
		goto out;

	/* Only allow default pvid change when filtering is disabled */
	if (br->vlan_enabled) {
	if (br_opt_get(br, BROPT_VLAN_ENABLED)) {
		pr_info_once("Please disable vlan filtering to change default_pvid\n");
		err = -EPERM;
		goto out;
@@ -1018,7 +1020,7 @@ int nbp_vlan_init(struct net_bridge_port *p)
		.orig_dev = p->br->dev,
		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
		.u.vlan_filtering = p->br->vlan_enabled,
		.u.vlan_filtering = br_opt_get(p->br, BROPT_VLAN_ENABLED),
	};
	struct net_bridge_vlan_group *vg;
	int ret = -ENOMEM;