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 * $Id: br_stp_timer.c,v 1.3 2000/05/05 02:17:17 davem Exp $ 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 111da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 121da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 131da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds #include <linux/kernel.h> 171da177e4SLinus Torvalds #include <linux/times.h> 181da177e4SLinus Torvalds #include <linux/smp_lock.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include "br_private.h" 211da177e4SLinus Torvalds #include "br_private_stp.h" 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds /* called under bridge lock */ 241da177e4SLinus Torvalds static int br_is_designated_for_some_port(const struct net_bridge *br) 251da177e4SLinus Torvalds { 261da177e4SLinus Torvalds struct net_bridge_port *p; 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds list_for_each_entry(p, &br->port_list, list) { 291da177e4SLinus Torvalds if (p->state != BR_STATE_DISABLED && 301da177e4SLinus Torvalds !memcmp(&p->designated_bridge, &br->bridge_id, 8)) 311da177e4SLinus Torvalds return 1; 321da177e4SLinus Torvalds } 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds return 0; 351da177e4SLinus Torvalds } 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static void br_hello_timer_expired(unsigned long arg) 381da177e4SLinus Torvalds { 391da177e4SLinus Torvalds struct net_bridge *br = (struct net_bridge *)arg; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds pr_debug("%s: hello timer expired\n", br->dev->name); 42e3efe08eSStephen Hemminger spin_lock(&br->lock); 431da177e4SLinus Torvalds if (br->dev->flags & IFF_UP) { 441da177e4SLinus Torvalds br_config_bpdu_generation(br); 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds mod_timer(&br->hello_timer, jiffies + br->hello_time); 471da177e4SLinus Torvalds } 48e3efe08eSStephen Hemminger spin_unlock(&br->lock); 491da177e4SLinus Torvalds } 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds static void br_message_age_timer_expired(unsigned long arg) 521da177e4SLinus Torvalds { 531da177e4SLinus Torvalds struct net_bridge_port *p = (struct net_bridge_port *) arg; 541da177e4SLinus Torvalds struct net_bridge *br = p->br; 551da177e4SLinus Torvalds const bridge_id *id = &p->designated_bridge; 561da177e4SLinus Torvalds int was_root; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds if (p->state == BR_STATE_DISABLED) 591da177e4SLinus Torvalds return; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n", 631da177e4SLinus Torvalds br->dev->name, 641da177e4SLinus Torvalds id->prio[0], id->prio[1], 651da177e4SLinus Torvalds id->addr[0], id->addr[1], id->addr[2], 661da177e4SLinus Torvalds id->addr[3], id->addr[4], id->addr[5], 671da177e4SLinus Torvalds p->port_no, p->dev->name); 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds /* 701da177e4SLinus Torvalds * According to the spec, the message age timer cannot be 711da177e4SLinus Torvalds * running when we are the root bridge. So.. this was_root 721da177e4SLinus Torvalds * check is redundant. I'm leaving it in for now, though. 731da177e4SLinus Torvalds */ 74e3efe08eSStephen Hemminger spin_lock(&br->lock); 751da177e4SLinus Torvalds if (p->state == BR_STATE_DISABLED) 761da177e4SLinus Torvalds goto unlock; 771da177e4SLinus Torvalds was_root = br_is_root_bridge(br); 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds br_become_designated_port(p); 801da177e4SLinus Torvalds br_configuration_update(br); 811da177e4SLinus Torvalds br_port_state_selection(br); 821da177e4SLinus Torvalds if (br_is_root_bridge(br) && !was_root) 831da177e4SLinus Torvalds br_become_root_bridge(br); 841da177e4SLinus Torvalds unlock: 85e3efe08eSStephen Hemminger spin_unlock(&br->lock); 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds static void br_forward_delay_timer_expired(unsigned long arg) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds struct net_bridge_port *p = (struct net_bridge_port *) arg; 911da177e4SLinus Torvalds struct net_bridge *br = p->br; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds pr_debug("%s: %d(%s) forward delay timer\n", 941da177e4SLinus Torvalds br->dev->name, p->port_no, p->dev->name); 95e3efe08eSStephen Hemminger spin_lock(&br->lock); 961da177e4SLinus Torvalds if (p->state == BR_STATE_LISTENING) { 971da177e4SLinus Torvalds p->state = BR_STATE_LEARNING; 981da177e4SLinus Torvalds mod_timer(&p->forward_delay_timer, 991da177e4SLinus Torvalds jiffies + br->forward_delay); 1001da177e4SLinus Torvalds } else if (p->state == BR_STATE_LEARNING) { 1011da177e4SLinus Torvalds p->state = BR_STATE_FORWARDING; 1021da177e4SLinus Torvalds if (br_is_designated_for_some_port(br)) 1031da177e4SLinus Torvalds br_topology_change_detection(br); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds br_log_state(p); 106e3efe08eSStephen Hemminger spin_unlock(&br->lock); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static void br_tcn_timer_expired(unsigned long arg) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds struct net_bridge *br = (struct net_bridge *) arg; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds pr_debug("%s: tcn timer expired\n", br->dev->name); 114e3efe08eSStephen Hemminger spin_lock(&br->lock); 1151da177e4SLinus Torvalds if (br->dev->flags & IFF_UP) { 1161da177e4SLinus Torvalds br_transmit_tcn(br); 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time); 1191da177e4SLinus Torvalds } 120e3efe08eSStephen Hemminger spin_unlock(&br->lock); 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds static void br_topology_change_timer_expired(unsigned long arg) 1241da177e4SLinus Torvalds { 1251da177e4SLinus Torvalds struct net_bridge *br = (struct net_bridge *) arg; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds pr_debug("%s: topo change timer expired\n", br->dev->name); 128e3efe08eSStephen Hemminger spin_lock(&br->lock); 1291da177e4SLinus Torvalds br->topology_change_detected = 0; 1301da177e4SLinus Torvalds br->topology_change = 0; 131e3efe08eSStephen Hemminger spin_unlock(&br->lock); 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds static void br_hold_timer_expired(unsigned long arg) 1351da177e4SLinus Torvalds { 1361da177e4SLinus Torvalds struct net_bridge_port *p = (struct net_bridge_port *) arg; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds pr_debug("%s: %d(%s) hold timer expired\n", 1391da177e4SLinus Torvalds p->br->dev->name, p->port_no, p->dev->name); 1401da177e4SLinus Torvalds 141e3efe08eSStephen Hemminger spin_lock(&p->br->lock); 1421da177e4SLinus Torvalds if (p->config_pending) 1431da177e4SLinus Torvalds br_transmit_config(p); 144e3efe08eSStephen Hemminger spin_unlock(&p->br->lock); 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static inline void br_timer_init(struct timer_list *timer, 1481da177e4SLinus Torvalds void (*_function)(unsigned long), 1491da177e4SLinus Torvalds unsigned long _data) 1501da177e4SLinus Torvalds { 1511da177e4SLinus Torvalds init_timer(timer); 1521da177e4SLinus Torvalds timer->function = _function; 1531da177e4SLinus Torvalds timer->data = _data; 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds void br_stp_timer_init(struct net_bridge *br) 1571da177e4SLinus Torvalds { 1581da177e4SLinus Torvalds br_timer_init(&br->hello_timer, br_hello_timer_expired, 1591da177e4SLinus Torvalds (unsigned long) br); 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds br_timer_init(&br->tcn_timer, br_tcn_timer_expired, 1621da177e4SLinus Torvalds (unsigned long) br); 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds br_timer_init(&br->topology_change_timer, 1651da177e4SLinus Torvalds br_topology_change_timer_expired, 1661da177e4SLinus Torvalds (unsigned long) br); 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds br_timer_init(&br->gc_timer, br_fdb_cleanup, (unsigned long) br); 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds void br_stp_port_timer_init(struct net_bridge_port *p) 1721da177e4SLinus Torvalds { 1731da177e4SLinus Torvalds br_timer_init(&p->message_age_timer, br_message_age_timer_expired, 1741da177e4SLinus Torvalds (unsigned long) p); 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds br_timer_init(&p->forward_delay_timer, br_forward_delay_timer_expired, 1771da177e4SLinus Torvalds (unsigned long) p); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds br_timer_init(&p->hold_timer, br_hold_timer_expired, 1801da177e4SLinus Torvalds (unsigned long) p); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds /* Report ticks left (in USER_HZ) used for API */ 1841da177e4SLinus Torvalds unsigned long br_timer_value(const struct timer_list *timer) 1851da177e4SLinus Torvalds { 1861da177e4SLinus Torvalds return timer_pending(timer) 1871da177e4SLinus Torvalds ? jiffies_to_clock_t(timer->expires - jiffies) : 0; 1881da177e4SLinus Torvalds } 189