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