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