cregit-Linux how code gets into the kernel

Release 4.14 net/bridge/br_stp.c

Directory: net/bridge
/*
 *      Spanning tree protocol; generic parts
 *      Linux ethernet bridge
 *
 *      Authors:
 *      Lennert Buytenhek               <buytenh@gnu.org>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */
#include <linux/kernel.h>
#include <linux/rculist.h>
#include <net/switchdev.h>

#include "br_private.h"
#include "br_private_stp.h"

/* since time values in bpdu are in jiffies and then scaled (1/256)
 * before sending, make sure that is at least one STP tick.
 */

#define MESSAGE_AGE_INCR	((HZ / 256) + 1)


static const char *const br_port_state_names[] = {
	[BR_STATE_DISABLED] = "disabled",
	[BR_STATE_LISTENING] = "listening",
	[BR_STATE_LEARNING] = "learning",
	[BR_STATE_FORWARDING] = "forwarding",
	[BR_STATE_BLOCKING] = "blocking",
};


void br_set_state(struct net_bridge_port *p, unsigned int state) { struct switchdev_attr attr = { .orig_dev = p->dev, .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, .flags = SWITCHDEV_F_DEFER, .u.stp_state = state, }; int err; p->state = state; err = switchdev_port_attr_set(p->dev, &attr); if (err && err != -EOPNOTSUPP) br_warn(p->br, "error setting offload STP state on port %u(%s)\n", (unsigned int) p->port_no, p->dev->name); else br_info(p->br, "port %u(%s) entered %s state\n", (unsigned int) p->port_no, p->dev->name, br_port_state_names[p->state]); }

Contributors

PersonTokensPropCommitsCommitProp
Scott Feldman5946.09%333.33%
Vivien Didelot3124.22%111.11%
Florian Fainelli2015.62%111.11%
Ido Schimmel129.38%222.22%
Jiri Pirko64.69%222.22%
Total128100.00%9100.00%

/* called under bridge lock */
struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no) { struct net_bridge_port *p; list_for_each_entry_rcu(p, &br->port_list, list) { if (p->port_no == port_no) return p; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3579.55%125.00%
Stephen Hemminger920.45%375.00%
Total44100.00%4100.00%

/* called under bridge lock */
static int br_should_become_root_port(const struct net_bridge_port *p, u16 root_port) { struct net_bridge *br; struct net_bridge_port *rp; int t; br = p->br; if (p->state == BR_STATE_DISABLED || br_is_designated_port(p)) return 0; if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) return 0; if (!root_port) return 1; rp = br_get_port(br, root_port); t = memcmp(&p->designated_root, &rp->designated_root, 8); if (t < 0) return 1; else if (t > 0) return 0; if (p->designated_cost + p->path_cost < rp->designated_cost + rp->path_cost) return 1; else if (p->designated_cost + p->path_cost > rp->designated_cost + rp->path_cost) return 0; t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); if (t < 0) return 1; else if (t > 0) return 0; if (p->designated_port < rp->designated_port) return 1; else if (p->designated_port > rp->designated_port) return 0; if (p->port_id < rp->port_id) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)24599.19%133.33%
Stephen Hemminger20.81%266.67%
Total247100.00%3100.00%


static void br_root_port_block(const struct net_bridge *br, struct net_bridge_port *p) { br_notice(br, "port %u(%s) tried to become root port (blocked)", (unsigned int) p->port_no, p->dev->name); br_set_state(p, BR_STATE_LISTENING); br_ifinfo_notify(RTM_NEWLINK, p); if (br->forward_delay > 0) mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger7094.59%150.00%
Florian Fainelli45.41%150.00%
Total74100.00%2100.00%

/* called under bridge lock */
static void br_root_selection(struct net_bridge *br) { struct net_bridge_port *p; u16 root_port = 0; list_for_each_entry(p, &br->port_list, list) { if (!br_should_become_root_port(p, root_port)) continue; if (p->flags & BR_ROOT_BLOCK) br_root_port_block(br, p); else root_port = p->port_no; } br->root_port = root_port; if (!root_port) { br->designated_root = br->bridge_id; br->root_path_cost = 0; } else { p = br_get_port(br, root_port); br->designated_root = p->designated_root; br->root_path_cost = p->designated_cost + p->path_cost; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9476.42%125.00%
Stephen Hemminger2923.58%375.00%
Total123100.00%4100.00%

/* called under bridge lock */
void br_become_root_bridge(struct net_bridge *br) { br->max_age = br->bridge_max_age; br->hello_time = br->bridge_hello_time; br->forward_delay = br->bridge_forward_delay; br_topology_change_detection(br); del_timer(&br->tcn_timer); if (br->dev->flags & IFF_UP) { br_config_bpdu_generation(br); mod_timer(&br->hello_timer, jiffies + br->hello_time); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6076.92%133.33%
Stephen Hemminger1823.08%266.67%
Total78100.00%3100.00%

/* called under bridge lock */
void br_transmit_config(struct net_bridge_port *p) { struct br_config_bpdu bpdu; struct net_bridge *br; if (timer_pending(&p->hold_timer)) { p->config_pending = 1; return; } br = p->br; bpdu.topology_change = br->topology_change; bpdu.topology_change_ack = p->topology_change_ack; bpdu.root = br->designated_root; bpdu.root_path_cost = br->root_path_cost; bpdu.bridge_id = br->bridge_id; bpdu.port_id = p->port_id; if (br_is_root_bridge(br)) bpdu.message_age = 0; else { struct net_bridge_port *root = br_get_port(br, br->root_port); bpdu.message_age = (jiffies - root->designated_age) + MESSAGE_AGE_INCR; } bpdu.max_age = br->max_age; bpdu.hello_time = br->hello_time; bpdu.forward_delay = br->forward_delay; if (bpdu.message_age < br->max_age) { br_send_config_bpdu(p, &bpdu); p->topology_change_ack = 0; p->config_pending = 0; if (p->br->stp_enabled == BR_KERNEL_STP) mod_timer(&p->hold_timer, round_jiffies(jiffies + BR_HOLD_TIME)); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)16977.88%116.67%
Stephen Hemminger3817.51%466.67%
Nikolay Aleksandrov104.61%116.67%
Total217100.00%6100.00%

/* called under bridge lock */
static void br_record_config_information(struct net_bridge_port *p, const struct br_config_bpdu *bpdu) { p->designated_root = bpdu->root; p->designated_cost = bpdu->root_path_cost; p->designated_bridge = bpdu->bridge_id; p->designated_port = bpdu->port_id; p->designated_age = jiffies - bpdu->message_age; mod_timer(&p->message_age_timer, jiffies + (bpdu->max_age - bpdu->message_age)); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6177.22%120.00%
Stephen Hemminger1620.25%240.00%
Joakim Tjernlund11.27%120.00%
Chris Healy11.27%120.00%
Total79100.00%5100.00%

/* called under bridge lock */
static void br_record_config_timeout_values(struct net_bridge *br, const struct br_config_bpdu *bpdu) { br->max_age = bpdu->max_age; br->hello_time = bpdu->hello_time; br->forward_delay = bpdu->forward_delay; __br_set_topology_change(br, bpdu->topology_change); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4590.00%133.33%
Vivien Didelot48.00%133.33%
Stephen Hemminger12.00%133.33%
Total50100.00%3100.00%

/* called under bridge lock */
void br_transmit_tcn(struct net_bridge *br) { struct net_bridge_port *p; p = br_get_port(br, br->root_port); if (p) br_send_tcn_bpdu(p); else br_notice(br, "root port %u not found for topology notice\n", br->root_port); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger2757.45%150.00%
Linus Torvalds (pre-git)2042.55%150.00%
Total47100.00%2100.00%

/* called under bridge lock */
static int br_should_become_designated_port(const struct net_bridge_port *p) { struct net_bridge *br; int t; br = p->br; if (br_is_designated_port(p)) return 1; if (memcmp(&p->designated_root, &br->designated_root, 8)) return 1; if (br->root_path_cost < p->designated_cost) return 1; else if (br->root_path_cost > p->designated_cost) return 0; t = memcmp(&br->bridge_id, &p->designated_bridge, 8); if (t < 0) return 1; else if (t > 0) return 0; if (p->port_id < p->designated_port) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)13499.26%150.00%
Stephen Hemminger10.74%150.00%
Total135100.00%2100.00%

/* called under bridge lock */
static void br_designated_port_selection(struct net_bridge *br) { struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && br_should_become_designated_port(p)) br_become_designated_port(p); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3681.82%150.00%
Stephen Hemminger818.18%150.00%
Total44100.00%2100.00%

/* called under bridge lock */
static int br_supersedes_port_info(const struct net_bridge_port *p, const struct br_config_bpdu *bpdu) { int t; t = memcmp(&bpdu->root, &p->designated_root, 8); if (t < 0) return 1; else if (t > 0) return 0; if (bpdu->root_path_cost < p->designated_cost) return 1; else if (bpdu->root_path_cost > p->designated_cost) return 0; t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); if (t < 0) return 1; else if (t > 0) return 0; if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8)) return 1; if (bpdu->port_id <= p->designated_port) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)15698.73%150.00%
Stephen Hemminger21.27%150.00%
Total158100.00%2100.00%

/* called under bridge lock */
static void br_topology_change_acknowledged(struct net_bridge *br) { br->topology_change_detected = 0; del_timer(&br->tcn_timer); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)2496.00%150.00%
Stephen Hemminger14.00%150.00%
Total25100.00%2100.00%

/* called under bridge lock */
void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); if (br->stp_enabled != BR_KERNEL_STP) return; br_info(br, "topology change detected, %s\n", isroot ? "propagating" : "sending tcn bpdu"); if (isroot) { __br_set_topology_change(br, 1); mod_timer(&br->topology_change_timer, jiffies + br->bridge_forward_delay + br->bridge_max_age); } else if (!br->topology_change_detected) { br_transmit_tcn(br); mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time); } br->topology_change_detected = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6157.55%114.29%
Stephen Hemminger4037.74%457.14%
Vivien Didelot43.77%114.29%
Tommi Virtanen10.94%114.29%
Total106100.00%7100.00%

/* called under bridge lock */
void br_config_bpdu_generation(struct net_bridge *br) { struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && br_is_designated_port(p)) br_transmit_config(p); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3581.40%150.00%
Stephen Hemminger818.60%150.00%
Total43100.00%2100.00%

/* called under bridge lock */
static void br_reply(struct net_bridge_port *p) { br_transmit_config(p); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)16100.00%1100.00%
Total16100.00%1100.00%

/* called under bridge lock */
void br_configuration_update(struct net_bridge *br) { br_root_selection(br); br_designated_port_selection(br); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)20100.00%1100.00%
Total20100.00%1100.00%

/* called under bridge lock */
void br_become_designated_port(struct net_bridge_port *p) { struct net_bridge *br; br = p->br; p->designated_root = br->designated_root; p->designated_cost = br->root_path_cost; p->designated_bridge = br->bridge_id; p->designated_port = p->port_id; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)53100.00%1100.00%
Total53100.00%1100.00%

/* called under bridge lock */
static void br_make_blocking(struct net_bridge_port *p) { if (p->state != BR_STATE_DISABLED && p->state != BR_STATE_BLOCKING) { if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) br_topology_change_detection(p->br); br_set_state(p, BR_STATE_BLOCKING); br_ifinfo_notify(RTM_NEWLINK, p); del_timer(&p->forward_delay_timer); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5882.86%125.00%
Stephen Hemminger811.43%250.00%
Florian Fainelli45.71%125.00%
Total70100.00%4100.00%

/* called under bridge lock */
static void br_make_forwarding(struct net_bridge_port *p) { struct net_bridge *br = p->br; if (p->state != BR_STATE_BLOCKING) return; if (br->stp_enabled == BR_NO_STP || br->forward_delay == 0) { br_set_state(p, BR_STATE_FORWARDING); br_topology_change_detection(br); del_timer(&p->forward_delay_timer); } else if (br->stp_enabled == BR_KERNEL_STP) br_set_state(p, BR_STATE_LISTENING); else br_set_state(p, BR_STATE_LEARNING); br_ifinfo_notify(RTM_NEWLINK, p); if (br->forward_delay != 0) mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger6555.08%562.50%
Linus Torvalds (pre-git)3126.27%112.50%
Florian Fainelli1210.17%112.50%
Lennert Buytenhek108.47%112.50%
Total118100.00%8100.00%

/* called under bridge lock */
void br_port_state_selection(struct net_bridge *br) { struct net_bridge_port *p; unsigned int liveports = 0; list_for_each_entry(p, &br->port_list, list) { if (p->state == BR_STATE_DISABLED) continue; /* Don't change port states if userspace is handling STP */ if (br->stp_enabled != BR_USER_STP) { if (p->port_no == br->root_port) { p->config_pending = 0; p->topology_change_ack = 0; br_make_forwarding(p); } else if (br_is_designated_port(p)) { del_timer(&p->message_age_timer); br_make_forwarding(p); } else { p->config_pending = 0; p->topology_change_ack = 0; br_make_blocking(p); } } if (p->state != BR_STATE_BLOCKING) br_multicast_enable_port(p); /* Multicast is not disabled for the port when it goes in * blocking state because the timers will expire and stop by * themselves without sending more queries. */ if (p->state == BR_STATE_FORWARDING) ++liveports; } if (liveports == 0) netif_carrier_off(br->dev); else netif_carrier_on(br->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9556.21%114.29%
Stephen Hemminger4928.99%342.86%
Nikolay Aleksandrov148.28%228.57%
Vitalii Demianets116.51%114.29%
Total169100.00%7100.00%

/* called under bridge lock */
static void br_topology_change_acknowledge(struct net_bridge_port *p) { p->topology_change_ack = 1; br_transmit_config(p); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)22100.00%1100.00%
Total22100.00%1100.00%

/* called under bridge lock */
void br_received_config_bpdu(struct net_bridge_port *p, const struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; br = p->br; was_root = br_is_root_bridge(br); if (br_supersedes_port_info(p, bpdu)) { br_record_config_information(p, bpdu); br_configuration_update(br); br_port_state_selection(br); if (!br_is_root_bridge(br) && was_root) { del_timer(&br->hello_timer); if (br->topology_change_detected) { del_timer(&br->topology_change_timer); br_transmit_tcn(br); mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time); } } if (p->port_no == br->root_port) { br_record_config_timeout_values(br, bpdu); br_config_bpdu_generation(br); if (bpdu->topology_change_ack) br_topology_change_acknowledged(br); } } else if (br_is_designated_port(p)) { br_reply(p); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)16295.29%133.33%
Stephen Hemminger84.71%266.67%
Total170100.00%3100.00%

/* called under bridge lock */
void br_received_tcn_bpdu(struct net_bridge_port *p) { if (br_is_designated_port(p)) { br_info(p->br, "port %u(%s) received tcn bpdu\n", (unsigned int) p->port_no, p->dev->name); br_topology_change_detection(p->br); br_topology_change_acknowledge(p); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4787.04%133.33%
Stephen Hemminger611.11%133.33%
Eric Dumazet11.85%133.33%
Total54100.00%3100.00%

/* Change bridge STP parameter */
int br_set_hello_time(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME) return -ERANGE; spin_lock_bh(&br->lock); br->bridge_hello_time = t; if (br_is_root_bridge(br)) br->hello_time = br->bridge_hello_time; spin_unlock_bh(&br->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger77100.00%1100.00%
Total77100.00%1100.00%


int br_set_max_age(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE) return -ERANGE; spin_lock_bh(&br->lock); br->bridge_max_age = t; if (br_is_root_bridge(br)) br->max_age = br->bridge_max_age; spin_unlock_bh(&br->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger77100.00%1100.00%
Total77100.00%1100.00%

/* called under bridge lock */
int __set_ageing_time(struct net_device *dev, unsigned long t) { struct switchdev_attr attr = { .orig_dev = dev, .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, .u.ageing_time = jiffies_to_clock_t(t), }; int err; err = switchdev_port_attr_set(dev, &attr); if (err && err != -EOPNOTSUPP) return err; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vivien Didelot4154.67%133.33%
Scott Feldman2938.67%133.33%
Ido Schimmel56.67%133.33%
Total75100.00%3100.00%

/* Set time interval that dynamic forwarding entries live * For pure software bridge, allow values outside the 802.1 * standard specification for special cases: * 0 - entry never ages (all permanant) * 1 - entry disappears (no persistance) * * Offloaded switch entries maybe more restrictive */
int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) { unsigned long t = clock_t_to_jiffies(ageing_time); int err; err = __set_ageing_time(br->dev, t); if (err) return err; spin_lock_bh(&br->lock); br->bridge_ageing_time = t; br->ageing_time = t; spin_unlock_bh(&br->lock); mod_delayed_work(system_long_wq, &br->gc_work, 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Scott Feldman4552.33%125.00%
Vivien Didelot3641.86%250.00%
Nikolay Aleksandrov55.81%125.00%
Total86100.00%4100.00%

/* called under bridge lock */
void __br_set_topology_change(struct net_bridge *br, unsigned char val) { unsigned long t; int err; if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { /* On topology change, set the bridge ageing time to twice the * forward delay. Otherwise, restore its default ageing time. */ if (val) { t = 2 * br->forward_delay; br_debug(br, "decreasing ageing time to %lu\n", t); } else { t = br->bridge_ageing_time; br_debug(br, "restoring ageing time to %lu\n", t); } err = __set_ageing_time(br->dev, t); if (err) br_warn(br, "error offloading ageing time\n"); else br->ageing_time = t; } br->topology_change = val; }

Contributors

PersonTokensPropCommitsCommitProp
Vivien Didelot114100.00%2100.00%
Total114100.00%2100.00%


void __br_set_forward_delay(struct net_bridge *br, unsigned long t) { br->bridge_forward_delay = t; if (br_is_root_bridge(br)) br->forward_delay = br->bridge_forward_delay; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu35100.00%1100.00%
Total35100.00%1100.00%


int br_set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); int err = -ERANGE; spin_lock_bh(&br->lock); if (br->stp_enabled != BR_NO_STP && (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) goto unlock; __br_set_forward_delay(br, t); err = 0; unlock: spin_unlock_bh(&br->lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger3745.12%125.00%
Vlad Yasevich3239.02%125.00%
Ian Wilson1012.20%125.00%
Herbert Xu33.66%125.00%
Total82100.00%4100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)170858.41%11.92%
Stephen Hemminger64321.99%2140.38%
Vivien Didelot2337.97%47.69%
Scott Feldman1364.65%47.69%
Florian Fainelli401.37%11.92%
Herbert Xu381.30%11.92%
Vlad Yasevich321.09%11.92%
Nikolay Aleksandrov290.99%47.69%
Ido Schimmel170.58%23.85%
Vitalii Demianets110.38%11.92%
Ian Wilson100.34%11.92%
Lennert Buytenhek100.34%11.92%
Jiri Pirko60.21%23.85%
Joakim Tjernlund30.10%23.85%
Franck Bui-Huu30.10%11.92%
Adrian Bunk10.03%11.92%
Tommi Virtanen10.03%11.92%
Chris Healy10.03%11.92%
Jan Engelhardt10.03%11.92%
Eric Dumazet10.03%11.92%
Total2924100.00%52100.00%
Directory: net/bridge
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.