Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+65 -23
+1 -1
net/bridge/br_device.c
··· 409 409 br->bridge_max_age = br->max_age = 20 * HZ; 410 410 br->bridge_hello_time = br->hello_time = 2 * HZ; 411 411 br->bridge_forward_delay = br->forward_delay = 15 * HZ; 412 - br->ageing_time = BR_DEFAULT_AGEING_TIME; 412 + br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME; 413 413 dev->max_mtu = ETH_MAX_MTU; 414 414 415 415 br_netfilter_rtable_init(br);
+3 -1
net/bridge/br_private.h
··· 300 300 unsigned long max_age; 301 301 unsigned long hello_time; 302 302 unsigned long forward_delay; 303 - unsigned long bridge_max_age; 304 303 unsigned long ageing_time; 304 + unsigned long bridge_max_age; 305 305 unsigned long bridge_hello_time; 306 306 unsigned long bridge_forward_delay; 307 + unsigned long bridge_ageing_time; 307 308 308 309 u8 group_addr[ETH_ALEN]; 309 310 bool group_addr_set; ··· 1000 999 int br_set_forward_delay(struct net_bridge *br, unsigned long x); 1001 1000 int br_set_hello_time(struct net_bridge *br, unsigned long x); 1002 1001 int br_set_max_age(struct net_bridge *br, unsigned long x); 1002 + int __set_ageing_time(struct net_device *dev, unsigned long t); 1003 1003 int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time); 1004 1004 1005 1005
+1
net/bridge/br_private_stp.h
··· 61 61 void br_transmit_config(struct net_bridge_port *p); 62 62 void br_transmit_tcn(struct net_bridge *br); 63 63 void br_topology_change_detection(struct net_bridge *br); 64 + void __br_set_topology_change(struct net_bridge *br, unsigned char val); 64 65 65 66 /* br_stp_bpdu.c */ 66 67 void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *);
+55 -10
net/bridge/br_stp.c
··· 234 234 br->max_age = bpdu->max_age; 235 235 br->hello_time = bpdu->hello_time; 236 236 br->forward_delay = bpdu->forward_delay; 237 - br->topology_change = bpdu->topology_change; 237 + __br_set_topology_change(br, bpdu->topology_change); 238 238 } 239 239 240 240 /* called under bridge lock */ ··· 344 344 isroot ? "propagating" : "sending tcn bpdu"); 345 345 346 346 if (isroot) { 347 - br->topology_change = 1; 347 + __br_set_topology_change(br, 1); 348 348 mod_timer(&br->topology_change_timer, jiffies 349 349 + br->bridge_forward_delay + br->bridge_max_age); 350 350 } else if (!br->topology_change_detected) { ··· 562 562 563 563 } 564 564 565 + /* called under bridge lock */ 566 + int __set_ageing_time(struct net_device *dev, unsigned long t) 567 + { 568 + struct switchdev_attr attr = { 569 + .orig_dev = dev, 570 + .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, 571 + .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, 572 + .u.ageing_time = jiffies_to_clock_t(t), 573 + }; 574 + int err; 575 + 576 + err = switchdev_port_attr_set(dev, &attr); 577 + if (err && err != -EOPNOTSUPP) 578 + return err; 579 + 580 + return 0; 581 + } 582 + 565 583 /* Set time interval that dynamic forwarding entries live 566 584 * For pure software bridge, allow values outside the 802.1 567 585 * standard specification for special cases: ··· 590 572 */ 591 573 int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) 592 574 { 593 - struct switchdev_attr attr = { 594 - .orig_dev = br->dev, 595 - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, 596 - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP, 597 - .u.ageing_time = ageing_time, 598 - }; 599 575 unsigned long t = clock_t_to_jiffies(ageing_time); 600 576 int err; 601 577 602 - err = switchdev_port_attr_set(br->dev, &attr); 603 - if (err && err != -EOPNOTSUPP) 578 + err = __set_ageing_time(br->dev, t); 579 + if (err) 604 580 return err; 605 581 582 + spin_lock_bh(&br->lock); 583 + br->bridge_ageing_time = t; 606 584 br->ageing_time = t; 585 + spin_unlock_bh(&br->lock); 586 + 607 587 mod_timer(&br->gc_timer, jiffies); 608 588 609 589 return 0; 590 + } 591 + 592 + /* called under bridge lock */ 593 + void __br_set_topology_change(struct net_bridge *br, unsigned char val) 594 + { 595 + unsigned long t; 596 + int err; 597 + 598 + if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { 599 + /* On topology change, set the bridge ageing time to twice the 600 + * forward delay. Otherwise, restore its default ageing time. 601 + */ 602 + 603 + if (val) { 604 + t = 2 * br->forward_delay; 605 + br_debug(br, "decreasing ageing time to %lu\n", t); 606 + } else { 607 + t = br->bridge_ageing_time; 608 + br_debug(br, "restoring ageing time to %lu\n", t); 609 + } 610 + 611 + err = __set_ageing_time(br->dev, t); 612 + if (err) 613 + br_warn(br, "error offloading ageing time\n"); 614 + else 615 + br->ageing_time = t; 616 + } 617 + 618 + br->topology_change = val; 610 619 } 611 620 612 621 void __br_set_forward_delay(struct net_bridge *br, unsigned long t)
+4 -10
net/bridge/br_stp_if.c
··· 36 36 /* called under bridge lock */ 37 37 void br_init_port(struct net_bridge_port *p) 38 38 { 39 - struct switchdev_attr attr = { 40 - .orig_dev = p->dev, 41 - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, 42 - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, 43 - .u.ageing_time = jiffies_to_clock_t(p->br->ageing_time), 44 - }; 45 39 int err; 46 40 47 41 p->port_id = br_make_port_id(p->priority, p->port_no); ··· 44 50 p->topology_change_ack = 0; 45 51 p->config_pending = 0; 46 52 47 - err = switchdev_port_attr_set(p->dev, &attr); 48 - if (err && err != -EOPNOTSUPP) 49 - netdev_err(p->dev, "failed to set HW ageing time\n"); 53 + err = __set_ageing_time(p->dev, p->br->ageing_time); 54 + if (err) 55 + netdev_err(p->dev, "failed to offload ageing time\n"); 50 56 } 51 57 52 58 /* NO locks held */ ··· 81 87 82 88 } 83 89 84 - br->topology_change = 0; 90 + __br_set_topology_change(br, 0); 85 91 br->topology_change_detected = 0; 86 92 spin_unlock_bh(&br->lock); 87 93
+1 -1
net/bridge/br_stp_timer.c
··· 125 125 br_debug(br, "topo change timer expired\n"); 126 126 spin_lock(&br->lock); 127 127 br->topology_change_detected = 0; 128 - br->topology_change = 0; 128 + __br_set_topology_change(br, 0); 129 129 spin_unlock(&br->lock); 130 130 } 131 131