xref: /openbmc/linux/net/bridge/br_stp_timer.c (revision 76b91c32)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Spanning tree protocol; timer-related code
31da177e4SLinus Torvalds  *	Linux ethernet bridge
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Lennert Buytenhek		<buytenh@gnu.org>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/times.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include "br_private.h"
181da177e4SLinus Torvalds #include "br_private_stp.h"
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds /* called under bridge lock */
211da177e4SLinus Torvalds static int br_is_designated_for_some_port(const struct net_bridge *br)
221da177e4SLinus Torvalds {
231da177e4SLinus Torvalds 	struct net_bridge_port *p;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
261da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED &&
271da177e4SLinus Torvalds 		    !memcmp(&p->designated_bridge, &br->bridge_id, 8))
281da177e4SLinus Torvalds 			return 1;
291da177e4SLinus Torvalds 	}
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 	return 0;
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static void br_hello_timer_expired(unsigned long arg)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds 	struct net_bridge *br = (struct net_bridge *)arg;
371da177e4SLinus Torvalds 
3828a16c97Sstephen hemminger 	br_debug(br, "hello timer expired\n");
39e3efe08eSStephen Hemminger 	spin_lock(&br->lock);
401da177e4SLinus Torvalds 	if (br->dev->flags & IFF_UP) {
411da177e4SLinus Torvalds 		br_config_bpdu_generation(br);
421da177e4SLinus Torvalds 
4376b91c32SNikolay Aleksandrov 		if (br->stp_enabled != BR_USER_STP)
4476b91c32SNikolay Aleksandrov 			mod_timer(&br->hello_timer,
4576b91c32SNikolay Aleksandrov 				  round_jiffies(jiffies + br->hello_time));
461da177e4SLinus Torvalds 	}
47e3efe08eSStephen Hemminger 	spin_unlock(&br->lock);
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds static void br_message_age_timer_expired(unsigned long arg)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds 	struct net_bridge_port *p = (struct net_bridge_port *) arg;
531da177e4SLinus Torvalds 	struct net_bridge *br = p->br;
541da177e4SLinus Torvalds 	const bridge_id *id = &p->designated_bridge;
551da177e4SLinus Torvalds 	int was_root;
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if (p->state == BR_STATE_DISABLED)
581da177e4SLinus Torvalds 		return;
591da177e4SLinus Torvalds 
6028a16c97Sstephen hemminger 	br_info(br, "port %u(%s) neighbor %.2x%.2x.%pM lost\n",
6195c96174SEric Dumazet 		(unsigned int) p->port_no, p->dev->name,
6228a16c97Sstephen hemminger 		id->prio[0], id->prio[1], &id->addr);
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	/*
651da177e4SLinus Torvalds 	 * According to the spec, the message age timer cannot be
661da177e4SLinus Torvalds 	 * running when we are the root bridge. So..  this was_root
671da177e4SLinus Torvalds 	 * check is redundant. I'm leaving it in for now, though.
681da177e4SLinus Torvalds 	 */
69e3efe08eSStephen Hemminger 	spin_lock(&br->lock);
701da177e4SLinus Torvalds 	if (p->state == BR_STATE_DISABLED)
711da177e4SLinus Torvalds 		goto unlock;
721da177e4SLinus Torvalds 	was_root = br_is_root_bridge(br);
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	br_become_designated_port(p);
751da177e4SLinus Torvalds 	br_configuration_update(br);
761da177e4SLinus Torvalds 	br_port_state_selection(br);
771da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !was_root)
781da177e4SLinus Torvalds 		br_become_root_bridge(br);
791da177e4SLinus Torvalds  unlock:
80e3efe08eSStephen Hemminger 	spin_unlock(&br->lock);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds static void br_forward_delay_timer_expired(unsigned long arg)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	struct net_bridge_port *p = (struct net_bridge_port *) arg;
861da177e4SLinus Torvalds 	struct net_bridge *br = p->br;
871da177e4SLinus Torvalds 
8828a16c97Sstephen hemminger 	br_debug(br, "port %u(%s) forward delay timer\n",
8995c96174SEric Dumazet 		 (unsigned int) p->port_no, p->dev->name);
90e3efe08eSStephen Hemminger 	spin_lock(&br->lock);
911da177e4SLinus Torvalds 	if (p->state == BR_STATE_LISTENING) {
92775dd692SFlorian Fainelli 		br_set_state(p, BR_STATE_LEARNING);
931da177e4SLinus Torvalds 		mod_timer(&p->forward_delay_timer,
941da177e4SLinus Torvalds 			  jiffies + br->forward_delay);
951da177e4SLinus Torvalds 	} else if (p->state == BR_STATE_LEARNING) {
96775dd692SFlorian Fainelli 		br_set_state(p, BR_STATE_FORWARDING);
971da177e4SLinus Torvalds 		if (br_is_designated_for_some_port(br))
981da177e4SLinus Torvalds 			br_topology_change_detection(br);
991faa4356Sstephen hemminger 		netif_carrier_on(br->dev);
1001da177e4SLinus Torvalds 	}
1011da177e4SLinus Torvalds 	br_log_state(p);
10293a33a58SEric Dumazet 	rcu_read_lock();
1034ecb961cSstephen hemminger 	br_ifinfo_notify(RTM_NEWLINK, p);
10493a33a58SEric Dumazet 	rcu_read_unlock();
105e3efe08eSStephen Hemminger 	spin_unlock(&br->lock);
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds static void br_tcn_timer_expired(unsigned long arg)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds 	struct net_bridge *br = (struct net_bridge *) arg;
1111da177e4SLinus Torvalds 
11228a16c97Sstephen hemminger 	br_debug(br, "tcn timer expired\n");
113e3efe08eSStephen Hemminger 	spin_lock(&br->lock);
11483401eb4Sstephen hemminger 	if (!br_is_root_bridge(br) && (br->dev->flags & IFF_UP)) {
1151da177e4SLinus Torvalds 		br_transmit_tcn(br);
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 		mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
1181da177e4SLinus Torvalds 	}
119e3efe08eSStephen Hemminger 	spin_unlock(&br->lock);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds static void br_topology_change_timer_expired(unsigned long arg)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	struct net_bridge *br = (struct net_bridge *) arg;
1251da177e4SLinus Torvalds 
12628a16c97Sstephen hemminger 	br_debug(br, "topo change timer expired\n");
127e3efe08eSStephen Hemminger 	spin_lock(&br->lock);
1281da177e4SLinus Torvalds 	br->topology_change_detected = 0;
1291da177e4SLinus Torvalds 	br->topology_change = 0;
130e3efe08eSStephen Hemminger 	spin_unlock(&br->lock);
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds static void br_hold_timer_expired(unsigned long arg)
1341da177e4SLinus Torvalds {
1351da177e4SLinus Torvalds 	struct net_bridge_port *p = (struct net_bridge_port *) arg;
1361da177e4SLinus Torvalds 
13728a16c97Sstephen hemminger 	br_debug(p->br, "port %u(%s) hold timer expired\n",
13895c96174SEric Dumazet 		 (unsigned int) p->port_no, p->dev->name);
1391da177e4SLinus Torvalds 
140e3efe08eSStephen Hemminger 	spin_lock(&p->br->lock);
1411da177e4SLinus Torvalds 	if (p->config_pending)
1421da177e4SLinus Torvalds 		br_transmit_config(p);
143e3efe08eSStephen Hemminger 	spin_unlock(&p->br->lock);
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds void br_stp_timer_init(struct net_bridge *br)
1471da177e4SLinus Torvalds {
148a95fcacdSStephen Hemminger 	setup_timer(&br->hello_timer, br_hello_timer_expired,
1491da177e4SLinus Torvalds 		      (unsigned long) br);
1501da177e4SLinus Torvalds 
151a95fcacdSStephen Hemminger 	setup_timer(&br->tcn_timer, br_tcn_timer_expired,
1521da177e4SLinus Torvalds 		      (unsigned long) br);
1531da177e4SLinus Torvalds 
154a95fcacdSStephen Hemminger 	setup_timer(&br->topology_change_timer,
1551da177e4SLinus Torvalds 		      br_topology_change_timer_expired,
1561da177e4SLinus Torvalds 		      (unsigned long) br);
1571da177e4SLinus Torvalds 
158a95fcacdSStephen Hemminger 	setup_timer(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds void br_stp_port_timer_init(struct net_bridge_port *p)
1621da177e4SLinus Torvalds {
163a95fcacdSStephen Hemminger 	setup_timer(&p->message_age_timer, br_message_age_timer_expired,
1641da177e4SLinus Torvalds 		      (unsigned long) p);
1651da177e4SLinus Torvalds 
166a95fcacdSStephen Hemminger 	setup_timer(&p->forward_delay_timer, br_forward_delay_timer_expired,
1671da177e4SLinus Torvalds 		      (unsigned long) p);
1681da177e4SLinus Torvalds 
169a95fcacdSStephen Hemminger 	setup_timer(&p->hold_timer, br_hold_timer_expired,
1701da177e4SLinus Torvalds 		      (unsigned long) p);
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds /* Report ticks left (in USER_HZ) used for API */
1741da177e4SLinus Torvalds unsigned long br_timer_value(const struct timer_list *timer)
1751da177e4SLinus Torvalds {
1761da177e4SLinus Torvalds 	return timer_pending(timer)
177a399a805SEric Dumazet 		? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0;
1781da177e4SLinus Torvalds }
179