18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Spanning tree protocol; timer-related code
48c2ecf20Sopenharmony_ci *	Linux ethernet bridge
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Lennert Buytenhek		<buytenh@gnu.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/times.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "br_private.h"
148c2ecf20Sopenharmony_ci#include "br_private_stp.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* called under bridge lock */
178c2ecf20Sopenharmony_cistatic int br_is_designated_for_some_port(const struct net_bridge *br)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
228c2ecf20Sopenharmony_ci		if (p->state != BR_STATE_DISABLED &&
238c2ecf20Sopenharmony_ci		    !memcmp(&p->designated_bridge, &br->bridge_id, 8))
248c2ecf20Sopenharmony_ci			return 1;
258c2ecf20Sopenharmony_ci	}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	return 0;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void br_hello_timer_expired(struct timer_list *t)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, hello_timer);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	br_debug(br, "hello timer expired\n");
358c2ecf20Sopenharmony_ci	spin_lock(&br->lock);
368c2ecf20Sopenharmony_ci	if (br->dev->flags & IFF_UP) {
378c2ecf20Sopenharmony_ci		br_config_bpdu_generation(br);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		if (br->stp_enabled == BR_KERNEL_STP)
408c2ecf20Sopenharmony_ci			mod_timer(&br->hello_timer,
418c2ecf20Sopenharmony_ci				  round_jiffies(jiffies + br->hello_time));
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	spin_unlock(&br->lock);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void br_message_age_timer_expired(struct timer_list *t)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct net_bridge_port *p = from_timer(p, t, message_age_timer);
498c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
508c2ecf20Sopenharmony_ci	const bridge_id *id = &p->designated_bridge;
518c2ecf20Sopenharmony_ci	int was_root;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (p->state == BR_STATE_DISABLED)
548c2ecf20Sopenharmony_ci		return;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	br_info(br, "port %u(%s) neighbor %.2x%.2x.%pM lost\n",
578c2ecf20Sopenharmony_ci		(unsigned int) p->port_no, p->dev->name,
588c2ecf20Sopenharmony_ci		id->prio[0], id->prio[1], &id->addr);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * According to the spec, the message age timer cannot be
628c2ecf20Sopenharmony_ci	 * running when we are the root bridge. So..  this was_root
638c2ecf20Sopenharmony_ci	 * check is redundant. I'm leaving it in for now, though.
648c2ecf20Sopenharmony_ci	 */
658c2ecf20Sopenharmony_ci	spin_lock(&br->lock);
668c2ecf20Sopenharmony_ci	if (p->state == BR_STATE_DISABLED)
678c2ecf20Sopenharmony_ci		goto unlock;
688c2ecf20Sopenharmony_ci	was_root = br_is_root_bridge(br);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	br_become_designated_port(p);
718c2ecf20Sopenharmony_ci	br_configuration_update(br);
728c2ecf20Sopenharmony_ci	br_port_state_selection(br);
738c2ecf20Sopenharmony_ci	if (br_is_root_bridge(br) && !was_root)
748c2ecf20Sopenharmony_ci		br_become_root_bridge(br);
758c2ecf20Sopenharmony_ci unlock:
768c2ecf20Sopenharmony_ci	spin_unlock(&br->lock);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void br_forward_delay_timer_expired(struct timer_list *t)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct net_bridge_port *p = from_timer(p, t, forward_delay_timer);
828c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	br_debug(br, "port %u(%s) forward delay timer\n",
858c2ecf20Sopenharmony_ci		 (unsigned int) p->port_no, p->dev->name);
868c2ecf20Sopenharmony_ci	spin_lock(&br->lock);
878c2ecf20Sopenharmony_ci	if (p->state == BR_STATE_LISTENING) {
888c2ecf20Sopenharmony_ci		br_set_state(p, BR_STATE_LEARNING);
898c2ecf20Sopenharmony_ci		mod_timer(&p->forward_delay_timer,
908c2ecf20Sopenharmony_ci			  jiffies + br->forward_delay);
918c2ecf20Sopenharmony_ci	} else if (p->state == BR_STATE_LEARNING) {
928c2ecf20Sopenharmony_ci		br_set_state(p, BR_STATE_FORWARDING);
938c2ecf20Sopenharmony_ci		if (br_is_designated_for_some_port(br))
948c2ecf20Sopenharmony_ci			br_topology_change_detection(br);
958c2ecf20Sopenharmony_ci		netif_carrier_on(br->dev);
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	rcu_read_lock();
988c2ecf20Sopenharmony_ci	br_ifinfo_notify(RTM_NEWLINK, NULL, p);
998c2ecf20Sopenharmony_ci	rcu_read_unlock();
1008c2ecf20Sopenharmony_ci	spin_unlock(&br->lock);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void br_tcn_timer_expired(struct timer_list *t)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, tcn_timer);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	br_debug(br, "tcn timer expired\n");
1088c2ecf20Sopenharmony_ci	spin_lock(&br->lock);
1098c2ecf20Sopenharmony_ci	if (!br_is_root_bridge(br) && (br->dev->flags & IFF_UP)) {
1108c2ecf20Sopenharmony_ci		br_transmit_tcn(br);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	spin_unlock(&br->lock);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void br_topology_change_timer_expired(struct timer_list *t)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, topology_change_timer);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	br_debug(br, "topo change timer expired\n");
1228c2ecf20Sopenharmony_ci	spin_lock(&br->lock);
1238c2ecf20Sopenharmony_ci	br->topology_change_detected = 0;
1248c2ecf20Sopenharmony_ci	__br_set_topology_change(br, 0);
1258c2ecf20Sopenharmony_ci	spin_unlock(&br->lock);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void br_hold_timer_expired(struct timer_list *t)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct net_bridge_port *p = from_timer(p, t, hold_timer);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	br_debug(p->br, "port %u(%s) hold timer expired\n",
1338c2ecf20Sopenharmony_ci		 (unsigned int) p->port_no, p->dev->name);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	spin_lock(&p->br->lock);
1368c2ecf20Sopenharmony_ci	if (p->config_pending)
1378c2ecf20Sopenharmony_ci		br_transmit_config(p);
1388c2ecf20Sopenharmony_ci	spin_unlock(&p->br->lock);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_civoid br_stp_timer_init(struct net_bridge *br)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	timer_setup(&br->hello_timer, br_hello_timer_expired, 0);
1448c2ecf20Sopenharmony_ci	timer_setup(&br->tcn_timer, br_tcn_timer_expired, 0);
1458c2ecf20Sopenharmony_ci	timer_setup(&br->topology_change_timer,
1468c2ecf20Sopenharmony_ci		    br_topology_change_timer_expired, 0);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid br_stp_port_timer_init(struct net_bridge_port *p)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	timer_setup(&p->message_age_timer, br_message_age_timer_expired, 0);
1528c2ecf20Sopenharmony_ci	timer_setup(&p->forward_delay_timer, br_forward_delay_timer_expired, 0);
1538c2ecf20Sopenharmony_ci	timer_setup(&p->hold_timer, br_hold_timer_expired, 0);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Report ticks left (in USER_HZ) used for API */
1578c2ecf20Sopenharmony_ciunsigned long br_timer_value(const struct timer_list *timer)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	return timer_pending(timer)
1608c2ecf20Sopenharmony_ci		? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0;
1618c2ecf20Sopenharmony_ci}
162