xref: /openbmc/linux/net/bridge/br_mst.c (revision ec7328b5)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *	Bridge Multiple Spanning Tree Support
4  *
5  *	Authors:
6  *	Tobias Waldekranz		<tobias@waldekranz.com>
7  */
8 
9 #include <linux/kernel.h>
10 
11 #include "br_private.h"
12 
13 DEFINE_STATIC_KEY_FALSE(br_mst_used);
14 
15 static void br_mst_vlan_set_state(struct net_bridge_port *p, struct net_bridge_vlan *v,
16 				  u8 state)
17 {
18 	struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
19 
20 	if (v->state == state)
21 		return;
22 
23 	br_vlan_set_state(v, state);
24 
25 	if (v->vid == vg->pvid)
26 		br_vlan_set_pvid_state(vg, state);
27 }
28 
29 int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
30 		     struct netlink_ext_ack *extack)
31 {
32 	struct net_bridge_vlan_group *vg;
33 	struct net_bridge_vlan *v;
34 
35 	vg = nbp_vlan_group(p);
36 	if (!vg)
37 		return 0;
38 
39 	list_for_each_entry(v, &vg->vlan_list, vlist) {
40 		if (v->brvlan->msti != msti)
41 			continue;
42 
43 		br_mst_vlan_set_state(p, v, state);
44 	}
45 
46 	return 0;
47 }
48 
49 void br_mst_vlan_init_state(struct net_bridge_vlan *v)
50 {
51 	/* VLANs always start out in MSTI 0 (CST) */
52 	v->msti = 0;
53 
54 	if (br_vlan_is_master(v))
55 		v->state = BR_STATE_FORWARDING;
56 	else
57 		v->state = v->port->state;
58 }
59 
60 int br_mst_set_enabled(struct net_bridge *br, bool on,
61 		       struct netlink_ext_ack *extack)
62 {
63 	struct net_bridge_vlan_group *vg;
64 	struct net_bridge_port *p;
65 
66 	list_for_each_entry(p, &br->port_list, list) {
67 		vg = nbp_vlan_group(p);
68 
69 		if (!vg->num_vlans)
70 			continue;
71 
72 		NL_SET_ERR_MSG(extack,
73 			       "MST mode can't be changed while VLANs exist");
74 		return -EBUSY;
75 	}
76 
77 	if (br_opt_get(br, BROPT_MST_ENABLED) == on)
78 		return 0;
79 
80 	if (on)
81 		static_branch_enable(&br_mst_used);
82 	else
83 		static_branch_disable(&br_mst_used);
84 
85 	br_opt_toggle(br, BROPT_MST_ENABLED, on);
86 	return 0;
87 }
88