diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index c08e02b67818fd5f4d27f442eef91f16fdeb978c..bca5ead3e97324a895d48aba64b321ede593caa2 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -409,7 +409,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_max_age = br->max_age = 20 * HZ; br->bridge_hello_time = br->hello_time = 2 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ; - br->ageing_time = BR_DEFAULT_AGEING_TIME; + br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME; dev->max_mtu = ETH_MAX_MTU; br_netfilter_rtable_init(br); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 26aec2366bc35ce616d2ed6fd34cd302a0c06831..8ce621e8345c478700ee64b76e41a98b2bd81d16 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -300,10 +300,11 @@ struct net_bridge unsigned long max_age; unsigned long hello_time; unsigned long forward_delay; - unsigned long bridge_max_age; unsigned long ageing_time; + unsigned long bridge_max_age; unsigned long bridge_hello_time; unsigned long bridge_forward_delay; + unsigned long bridge_ageing_time; u8 group_addr[ETH_ALEN]; bool group_addr_set; @@ -999,6 +1000,7 @@ void __br_set_forward_delay(struct net_bridge *br, unsigned long t); int br_set_forward_delay(struct net_bridge *br, unsigned long x); int br_set_hello_time(struct net_bridge *br, unsigned long x); int br_set_max_age(struct net_bridge *br, unsigned long x); +int __set_ageing_time(struct net_device *dev, unsigned long t); int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time); diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 2fe910c4e1700776c33275f0ae277d354e348f6c..3f7543a29b7629ae20d0fbfc1cf40202114d4a5b 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -61,6 +61,7 @@ void br_received_tcn_bpdu(struct net_bridge_port *p); void br_transmit_config(struct net_bridge_port *p); void br_transmit_tcn(struct net_bridge *br); void br_topology_change_detection(struct net_bridge *br); +void __br_set_topology_change(struct net_bridge *br, unsigned char val); /* br_stp_bpdu.c */ void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 9258b8ef14ff5f0b65af145b2fb9e517efa4eddd..71fd1a4e63cc84ec047f56adf5fe24bb971522fa 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -234,7 +234,7 @@ static void br_record_config_timeout_values(struct net_bridge *br, br->max_age = bpdu->max_age; br->hello_time = bpdu->hello_time; br->forward_delay = bpdu->forward_delay; - br->topology_change = bpdu->topology_change; + __br_set_topology_change(br, bpdu->topology_change); } /* called under bridge lock */ @@ -344,7 +344,7 @@ void br_topology_change_detection(struct net_bridge *br) isroot ? "propagating" : "sending tcn bpdu"); if (isroot) { - br->topology_change = 1; + __br_set_topology_change(br, 1); mod_timer(&br->topology_change_timer, jiffies + br->bridge_forward_delay + br->bridge_max_age); } else if (!br->topology_change_detected) { @@ -562,6 +562,24 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } +/* called under bridge lock */ +int __set_ageing_time(struct net_device *dev, unsigned long t) +{ + struct switchdev_attr attr = { + .orig_dev = dev, + .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, + .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, + .u.ageing_time = jiffies_to_clock_t(t), + }; + int err; + + err = switchdev_port_attr_set(dev, &attr); + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + /* Set time interval that dynamic forwarding entries live * For pure software bridge, allow values outside the 802.1 * standard specification for special cases: @@ -572,25 +590,52 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) */ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) { - struct switchdev_attr attr = { - .orig_dev = br->dev, - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP, - .u.ageing_time = ageing_time, - }; unsigned long t = clock_t_to_jiffies(ageing_time); int err; - err = switchdev_port_attr_set(br->dev, &attr); - if (err && err != -EOPNOTSUPP) + err = __set_ageing_time(br->dev, t); + if (err) return err; + spin_lock_bh(&br->lock); + br->bridge_ageing_time = t; br->ageing_time = t; + spin_unlock_bh(&br->lock); + mod_timer(&br->gc_timer, jiffies); return 0; } +/* called under bridge lock */ +void __br_set_topology_change(struct net_bridge *br, unsigned char val) +{ + unsigned long t; + int err; + + if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { + /* On topology change, set the bridge ageing time to twice the + * forward delay. Otherwise, restore its default ageing time. + */ + + if (val) { + t = 2 * br->forward_delay; + br_debug(br, "decreasing ageing time to %lu\n", t); + } else { + t = br->bridge_ageing_time; + br_debug(br, "restoring ageing time to %lu\n", t); + } + + err = __set_ageing_time(br->dev, t); + if (err) + br_warn(br, "error offloading ageing time\n"); + else + br->ageing_time = t; + } + + br->topology_change = val; +} + void __br_set_forward_delay(struct net_bridge *br, unsigned long t) { br->bridge_forward_delay = t; diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index d8ad73b38de27b49374862cca33954534606d486..6c1e214111250ea69199d40080f96af730d78934 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -36,12 +36,6 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no) /* called under bridge lock */ void br_init_port(struct net_bridge_port *p) { - struct switchdev_attr attr = { - .orig_dev = p->dev, - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, - .u.ageing_time = jiffies_to_clock_t(p->br->ageing_time), - }; int err; p->port_id = br_make_port_id(p->priority, p->port_no); @@ -50,9 +44,9 @@ void br_init_port(struct net_bridge_port *p) p->topology_change_ack = 0; p->config_pending = 0; - err = switchdev_port_attr_set(p->dev, &attr); - if (err && err != -EOPNOTSUPP) - netdev_err(p->dev, "failed to set HW ageing time\n"); + err = __set_ageing_time(p->dev, p->br->ageing_time); + if (err) + netdev_err(p->dev, "failed to offload ageing time\n"); } /* NO locks held */ @@ -87,7 +81,7 @@ void br_stp_disable_bridge(struct net_bridge *br) } - br->topology_change = 0; + __br_set_topology_change(br, 0); br->topology_change_detected = 0; spin_unlock_bh(&br->lock); diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index da058b85aa2260c6148034917e96944eefd85e28..7ddb38e0a06ea2d67f156a25eb50277a9cfc3898 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -125,7 +125,7 @@ static void br_topology_change_timer_expired(unsigned long arg) br_debug(br, "topo change timer expired\n"); spin_lock(&br->lock); br->topology_change_detected = 0; - br->topology_change = 0; + __br_set_topology_change(br, 0); spin_unlock(&br->lock); }