|
@@ -123,6 +123,62 @@ void br_stp_disable_port(struct net_bridge_port *p)
|
|
|
br_become_root_bridge(br);
|
|
|
}
|
|
|
|
|
|
+static void br_stp_start(struct net_bridge *br)
|
|
|
+{
|
|
|
+ int r;
|
|
|
+ char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
|
|
|
+ char *envp[] = { NULL };
|
|
|
+
|
|
|
+ r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
|
|
|
+ if (r == 0) {
|
|
|
+ br->stp_enabled = BR_USER_STP;
|
|
|
+ printk(KERN_INFO "%s: userspace STP started\n", br->dev->name);
|
|
|
+ } else {
|
|
|
+ br->stp_enabled = BR_KERNEL_STP;
|
|
|
+ printk(KERN_INFO "%s: starting userspace STP failed, "
|
|
|
+ "staring kernel STP\n", br->dev->name);
|
|
|
+
|
|
|
+ /* To start timers on any ports left in blocking */
|
|
|
+ spin_lock_bh(&br->lock);
|
|
|
+ br_port_state_selection(br);
|
|
|
+ spin_unlock_bh(&br->lock);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void br_stp_stop(struct net_bridge *br)
|
|
|
+{
|
|
|
+ int r;
|
|
|
+ char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
|
|
|
+ char *envp[] = { NULL };
|
|
|
+
|
|
|
+ if (br->stp_enabled == BR_USER_STP) {
|
|
|
+ r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
|
|
|
+ printk(KERN_INFO "%s: userspace STP stopped, return code %d\n",
|
|
|
+ br->dev->name, r);
|
|
|
+
|
|
|
+
|
|
|
+ /* To start timers on any ports left in blocking */
|
|
|
+ spin_lock_bh(&br->lock);
|
|
|
+ br_port_state_selection(br);
|
|
|
+ spin_unlock_bh(&br->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ br->stp_enabled = BR_NO_STP;
|
|
|
+}
|
|
|
+
|
|
|
+void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
|
|
|
+{
|
|
|
+ ASSERT_RTNL();
|
|
|
+
|
|
|
+ if (val) {
|
|
|
+ if (br->stp_enabled == BR_NO_STP)
|
|
|
+ br_stp_start(br);
|
|
|
+ } else {
|
|
|
+ if (br->stp_enabled != BR_NO_STP)
|
|
|
+ br_stp_stop(br);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* called under bridge lock */
|
|
|
void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
|
|
|
{
|