diff options
author | David S. Miller <davem@davemloft.net> | 2016-12-11 05:28:39 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-12-11 05:28:39 +0300 |
commit | c280b482662018b2178f67ea017275191c30823e (patch) | |
tree | 1fc8f50b8590b7d90e898a3d95da56fced30aad5 | |
parent | bfd8d977af2390e646d4a5df8e88c1d7a1ae63d9 (diff) | |
parent | 34d8acd8aabb6e070b6fb718909db80458ef32ec (diff) | |
download | linux-c280b482662018b2178f67ea017275191c30823e.tar.xz |
Merge branch 'bridge-fast-ageing-on-topology-change'
Vivien Didelot says:
====================
net: bridge: fast ageing on topology change
802.1D [1] specifies that the bridges in a network must use a short
value to age out dynamic entries in the Filtering Database for a period,
once a topology change has been communicated by the root bridge.
This patchset fixes this for the in-kernel STP implementation.
Once the topology change flag is set in a net_bridge instance, the
ageing time value is shorten to twice the forward delay used by the
topology.
When the topology change flag is cleared, the ageing time configured for
the bridge is restored.
To accomplish that, a new bridge_ageing_time member is added to the
net_bridge structure, to store the user configured bridge ageing time.
Two helpers are added to offload the ageing time and set the topology
change flag in the net_bridge instance. Then the required logic is added
in the topology change helper if in-kernel STP is used.
This has been tested on the following topology:
+--------------+
| root bridge |
| 1 2 3 4 |
+--+--+--+--+--+
| | | | +--------+
| | | +------| laptop |
| | | +--------+
+--+--+--+-----+
| 1 2 3 |
| slave bridge |
+--------------+
When unplugging/replugging the laptop, the slave bridge (under test)
gets the topology change flag sent by the root bridge, and fast ageing
is triggered on the bridges. Once the topology change timer of the root
bridge expires, the topology change flag is cleared and the configured
ageing time is restored on the bridges.
A similar test has been done between two bridges under test.
When changing the forward delay of the root bridge with:
# echo 3000 > /sys/class/net/br0/bridge/forward_delay
the ageing time correctly changes on both bridges from 300s to 60s while
the TOPOLOGY_CHANGE flag is present.
[1] "8.3.5 Notifying topology changes",
http://profesores.elo.utfsm.cl/~agv/elo309/doc/802.1D-1998.pdf
No change since RFC: https://lkml.org/lkml/2016/10/19/828
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/bridge/br_device.c | 2 | ||||
-rw-r--r-- | net/bridge/br_private.h | 4 | ||||
-rw-r--r-- | net/bridge/br_private_stp.h | 1 | ||||
-rw-r--r-- | net/bridge/br_stp.c | 65 | ||||
-rw-r--r-- | net/bridge/br_stp_if.c | 14 | ||||
-rw-r--r-- | net/bridge/br_stp_timer.c | 2 |
6 files changed, 65 insertions, 23 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index c08e02b67818..bca5ead3e973 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 26aec2366bc3..8ce621e8345c 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 2fe910c4e170..3f7543a29b76 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 9258b8ef14ff..71fd1a4e63cc 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 d8ad73b38de2..6c1e21411125 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 da058b85aa22..7ddb38e0a06e 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); } |