blob: 27bf1979b9099b77d6038e96f613b9126a861c14 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Spanning tree protocol; timer-related code
4 * Linux ethernet bridge
5 *
6 * Authors:
7 * Lennert Buytenhek <buytenh@gnu.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9
10#include <linux/kernel.h>
11#include <linux/times.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012
13#include "br_private.h"
14#include "br_private_stp.h"
15
16/* called under bridge lock */
17static int br_is_designated_for_some_port(const struct net_bridge *br)
18{
19 struct net_bridge_port *p;
20
21 list_for_each_entry(p, &br->port_list, list) {
22 if (p->state != BR_STATE_DISABLED &&
YOSHIFUJI Hideaki9d6f2292007-02-09 23:24:35 +090023 !memcmp(&p->designated_bridge, &br->bridge_id, 8))
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 return 1;
25 }
26
27 return 0;
28}
29
Allen Pais1a3deb12017-11-03 11:51:11 +053030static void br_hello_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -070031{
Allen Pais1a3deb12017-11-03 11:51:11 +053032 struct net_bridge *br = from_timer(br, t, hello_timer);
YOSHIFUJI Hideaki9d6f2292007-02-09 23:24:35 +090033
stephen hemminger28a16c92010-05-10 09:31:09 +000034 br_debug(br, "hello timer expired\n");
Stephen Hemmingere3efe082006-03-20 22:56:25 -080035 spin_lock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 if (br->dev->flags & IFF_UP) {
37 br_config_bpdu_generation(br);
38
Xin Long6d18c732017-05-19 22:20:29 +080039 if (br->stp_enabled == BR_KERNEL_STP)
Nikolay Aleksandrov76b91c32015-07-23 11:01:05 -070040 mod_timer(&br->hello_timer,
41 round_jiffies(jiffies + br->hello_time));
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 }
Stephen Hemmingere3efe082006-03-20 22:56:25 -080043 spin_unlock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044}
45
Allen Pais1a3deb12017-11-03 11:51:11 +053046static void br_message_age_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
Allen Pais1a3deb12017-11-03 11:51:11 +053048 struct net_bridge_port *p = from_timer(p, t, message_age_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 struct net_bridge *br = p->br;
50 const bridge_id *id = &p->designated_bridge;
51 int was_root;
52
53 if (p->state == BR_STATE_DISABLED)
54 return;
55
stephen hemminger28a16c92010-05-10 09:31:09 +000056 br_info(br, "port %u(%s) neighbor %.2x%.2x.%pM lost\n",
Eric Dumazet95c96172012-04-15 05:58:06 +000057 (unsigned int) p->port_no, p->dev->name,
stephen hemminger28a16c92010-05-10 09:31:09 +000058 id->prio[0], id->prio[1], &id->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60 /*
61 * According to the spec, the message age timer cannot be
62 * running when we are the root bridge. So.. this was_root
63 * check is redundant. I'm leaving it in for now, though.
64 */
Stephen Hemmingere3efe082006-03-20 22:56:25 -080065 spin_lock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 if (p->state == BR_STATE_DISABLED)
67 goto unlock;
68 was_root = br_is_root_bridge(br);
69
70 br_become_designated_port(p);
71 br_configuration_update(br);
72 br_port_state_selection(br);
73 if (br_is_root_bridge(br) && !was_root)
74 br_become_root_bridge(br);
75 unlock:
Stephen Hemmingere3efe082006-03-20 22:56:25 -080076 spin_unlock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077}
78
Allen Pais1a3deb12017-11-03 11:51:11 +053079static void br_forward_delay_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080{
Allen Pais1a3deb12017-11-03 11:51:11 +053081 struct net_bridge_port *p = from_timer(p, t, forward_delay_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 struct net_bridge *br = p->br;
83
stephen hemminger28a16c92010-05-10 09:31:09 +000084 br_debug(br, "port %u(%s) forward delay timer\n",
Eric Dumazet95c96172012-04-15 05:58:06 +000085 (unsigned int) p->port_no, p->dev->name);
Stephen Hemmingere3efe082006-03-20 22:56:25 -080086 spin_lock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if (p->state == BR_STATE_LISTENING) {
Florian Fainelli775dd692014-09-30 16:13:19 -070088 br_set_state(p, BR_STATE_LEARNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 mod_timer(&p->forward_delay_timer,
90 jiffies + br->forward_delay);
91 } else if (p->state == BR_STATE_LEARNING) {
Florian Fainelli775dd692014-09-30 16:13:19 -070092 br_set_state(p, BR_STATE_FORWARDING);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 if (br_is_designated_for_some_port(br))
94 br_topology_change_detection(br);
stephen hemminger1faa4352011-03-07 08:34:06 +000095 netif_carrier_on(br->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 }
Eric Dumazet93a33a52015-05-21 13:28:29 -070097 rcu_read_lock();
Nikolay Aleksandrov92899062017-11-01 12:18:13 +020098 br_ifinfo_notify(RTM_NEWLINK, NULL, p);
Eric Dumazet93a33a52015-05-21 13:28:29 -070099 rcu_read_unlock();
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800100 spin_unlock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
Allen Pais1a3deb12017-11-03 11:51:11 +0530103static void br_tcn_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Allen Pais1a3deb12017-11-03 11:51:11 +0530105 struct net_bridge *br = from_timer(br, t, tcn_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
stephen hemminger28a16c92010-05-10 09:31:09 +0000107 br_debug(br, "tcn timer expired\n");
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800108 spin_lock(&br->lock);
stephen hemminger83401eb2013-05-02 14:23:28 +0000109 if (!br_is_root_bridge(br) && (br->dev->flags & IFF_UP)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 br_transmit_tcn(br);
YOSHIFUJI Hideaki9d6f2292007-02-09 23:24:35 +0900111
tanxiaojun31a5b832013-12-19 13:28:12 +0800112 mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 }
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800114 spin_unlock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115}
116
Allen Pais1a3deb12017-11-03 11:51:11 +0530117static void br_topology_change_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
Allen Pais1a3deb12017-11-03 11:51:11 +0530119 struct net_bridge *br = from_timer(br, t, topology_change_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
stephen hemminger28a16c92010-05-10 09:31:09 +0000121 br_debug(br, "topo change timer expired\n");
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800122 spin_lock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 br->topology_change_detected = 0;
Vivien Didelot8384b5f2016-12-10 13:44:28 -0500124 __br_set_topology_change(br, 0);
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800125 spin_unlock(&br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126}
127
Allen Pais1a3deb12017-11-03 11:51:11 +0530128static void br_hold_timer_expired(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Allen Pais1a3deb12017-11-03 11:51:11 +0530130 struct net_bridge_port *p = from_timer(p, t, hold_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
stephen hemminger28a16c92010-05-10 09:31:09 +0000132 br_debug(p->br, "port %u(%s) hold timer expired\n",
Eric Dumazet95c96172012-04-15 05:58:06 +0000133 (unsigned int) p->port_no, p->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800135 spin_lock(&p->br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 if (p->config_pending)
137 br_transmit_config(p);
Stephen Hemmingere3efe082006-03-20 22:56:25 -0800138 spin_unlock(&p->br->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139}
140
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141void br_stp_timer_init(struct net_bridge *br)
142{
Allen Pais1a3deb12017-11-03 11:51:11 +0530143 timer_setup(&br->hello_timer, br_hello_timer_expired, 0);
144 timer_setup(&br->tcn_timer, br_tcn_timer_expired, 0);
145 timer_setup(&br->topology_change_timer,
146 br_topology_change_timer_expired, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
149void br_stp_port_timer_init(struct net_bridge_port *p)
150{
Allen Pais1a3deb12017-11-03 11:51:11 +0530151 timer_setup(&p->message_age_timer, br_message_age_timer_expired, 0);
152 timer_setup(&p->forward_delay_timer, br_forward_delay_timer_expired, 0);
153 timer_setup(&p->hold_timer, br_hold_timer_expired, 0);
YOSHIFUJI Hideaki9d6f2292007-02-09 23:24:35 +0900154}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156/* Report ticks left (in USER_HZ) used for API */
157unsigned long br_timer_value(const struct timer_list *timer)
158{
159 return timer_pending(timer)
Eric Dumazeta399a802012-08-08 21:13:53 +0000160 ? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161}