Blame view
net/bridge/br_stp.c
15.4 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * Spanning tree protocol; generic parts * Linux ethernet bridge * * Authors: * Lennert Buytenhek <buytenh@gnu.org> * |
1da177e4c Linux-2.6.12-rc2 |
8 9 10 11 12 13 |
* 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> |
82524746c rcu: split list.h... |
14 |
#include <linux/rculist.h> |
38dcf357a bridge: call netd... |
15 |
#include <net/switchdev.h> |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 20 |
#include "br_private.h" #include "br_private_stp.h" /* since time values in bpdu are in jiffies and then scaled (1/256) |
aaca735f4 bridge: Adjust mi... |
21 |
* before sending, make sure that is at least one STP tick. |
1da177e4c Linux-2.6.12-rc2 |
22 |
*/ |
aaca735f4 bridge: Adjust mi... |
23 |
#define MESSAGE_AGE_INCR ((HZ / 256) + 1) |
1da177e4c Linux-2.6.12-rc2 |
24 |
|
36cbd3dcc net: mark read-on... |
25 |
static const char *const br_port_state_names[] = { |
9d6f229fc [NET] BRIDGE: Fix... |
26 |
[BR_STATE_DISABLED] = "disabled", |
1da177e4c Linux-2.6.12-rc2 |
27 |
[BR_STATE_LISTENING] = "listening", |
9d6f229fc [NET] BRIDGE: Fix... |
28 29 |
[BR_STATE_LEARNING] = "learning", [BR_STATE_FORWARDING] = "forwarding", |
1da177e4c Linux-2.6.12-rc2 |
30 31 |
[BR_STATE_BLOCKING] = "blocking", }; |
775dd692b net: bridge: add ... |
32 33 |
void br_set_state(struct net_bridge_port *p, unsigned int state) { |
356360625 switchdev: conver... |
34 |
struct switchdev_attr attr = { |
6ff64f6f9 switchdev: Pass o... |
35 |
.orig_dev = p->dev, |
1f8683987 switchdev: rename... |
36 |
.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, |
0bc05d585 switchdev: allow ... |
37 |
.flags = SWITCHDEV_F_DEFER, |
42275bd8f switchdev: don't ... |
38 |
.u.stp_state = state, |
356360625 switchdev: conver... |
39 |
}; |
38dcf357a bridge: call netd... |
40 |
int err; |
775dd692b net: bridge: add ... |
41 |
p->state = state; |
356360625 switchdev: conver... |
42 |
err = switchdev_port_attr_set(p->dev, &attr); |
bbe14f542 switchdev: bridge... |
43 |
if (err && err != -EOPNOTSUPP) |
38dcf357a bridge: call netd... |
44 45 46 |
br_warn(p->br, "error setting offload STP state on port %u(%s) ", (unsigned int) p->port_no, p->dev->name); |
7c25b16db net: bridge: log ... |
47 48 49 50 51 |
else br_info(p->br, "port %u(%s) entered %s state ", (unsigned int) p->port_no, p->dev->name, br_port_state_names[p->state]); |
775dd692b net: bridge: add ... |
52 |
} |
1da177e4c Linux-2.6.12-rc2 |
53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
/* 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; } /* called under bridge lock */ |
9d6f229fc [NET] BRIDGE: Fix... |
67 |
static int br_should_become_root_port(const struct net_bridge_port *p, |
1da177e4c Linux-2.6.12-rc2 |
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
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; } |
1007dd1aa bridge: add root ... |
116 117 118 119 120 121 |
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); |
775dd692b net: bridge: add ... |
122 |
br_set_state(p, BR_STATE_LISTENING); |
1007dd1aa bridge: add root ... |
123 124 125 126 127 |
br_ifinfo_notify(RTM_NEWLINK, p); if (br->forward_delay > 0) mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); } |
1da177e4c Linux-2.6.12-rc2 |
128 129 130 131 132 133 134 |
/* 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) { |
1007dd1aa bridge: add root ... |
135 136 137 138 139 140 |
if (!br_should_become_root_port(p, root_port)) continue; if (p->flags & BR_ROOT_BLOCK) br_root_port_block(br, p); else |
1da177e4c Linux-2.6.12-rc2 |
141 |
root_port = p->port_no; |
1da177e4c Linux-2.6.12-rc2 |
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
} 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; } } /* 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); } } /* called under bridge lock */ void br_transmit_config(struct net_bridge_port *p) { struct br_config_bpdu bpdu; struct net_bridge *br; |
1da177e4c Linux-2.6.12-rc2 |
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
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); |
0c03150e7 bridge: send prop... |
194 |
bpdu.message_age = (jiffies - root->designated_age) |
1da177e4c Linux-2.6.12-rc2 |
195 196 197 198 199 200 201 202 203 204 |
+ 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; |
76b91c32d bridge: stp: when... |
205 206 207 |
if (p->br->stp_enabled == BR_KERNEL_STP) mod_timer(&p->hold_timer, round_jiffies(jiffies + BR_HOLD_TIME)); |
1da177e4c Linux-2.6.12-rc2 |
208 209 210 211 |
} } /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
212 213 |
static void br_record_config_information(struct net_bridge_port *p, const struct br_config_bpdu *bpdu) |
1da177e4c Linux-2.6.12-rc2 |
214 215 216 217 218 |
{ p->designated_root = bpdu->root; p->designated_cost = bpdu->root_path_cost; p->designated_bridge = bpdu->bridge_id; p->designated_port = bpdu->port_id; |
709e1b5cd bridge: message a... |
219 |
p->designated_age = jiffies - bpdu->message_age; |
1da177e4c Linux-2.6.12-rc2 |
220 |
|
9d6f229fc [NET] BRIDGE: Fix... |
221 |
mod_timer(&p->message_age_timer, jiffies |
9a0620133 resubmit bridge: ... |
222 |
+ (bpdu->max_age - bpdu->message_age)); |
1da177e4c Linux-2.6.12-rc2 |
223 224 225 |
} /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
226 |
static void br_record_config_timeout_values(struct net_bridge *br, |
1da177e4c Linux-2.6.12-rc2 |
227 228 229 230 231 |
const struct br_config_bpdu *bpdu) { br->max_age = bpdu->max_age; br->hello_time = bpdu->hello_time; br->forward_delay = bpdu->forward_delay; |
8384b5f5b net: bridge: add ... |
232 |
__br_set_topology_change(br, bpdu->topology_change); |
1da177e4c Linux-2.6.12-rc2 |
233 234 235 236 237 |
} /* called under bridge lock */ void br_transmit_tcn(struct net_bridge *br) { |
91bc033c4 bridge: avoid OOP... |
238 239 240 241 242 243 244 245 246 |
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 ", br->root_port); |
1da177e4c Linux-2.6.12-rc2 |
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
} /* 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; } /* 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); } } /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
293 294 |
static int br_supersedes_port_info(const struct net_bridge_port *p, const struct br_config_bpdu *bpdu) |
1da177e4c Linux-2.6.12-rc2 |
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
{ 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; } /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
325 |
static void br_topology_change_acknowledged(struct net_bridge *br) |
1da177e4c Linux-2.6.12-rc2 |
326 327 328 329 330 331 332 333 334 |
{ br->topology_change_detected = 0; del_timer(&br->tcn_timer); } /* called under bridge lock */ void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); |
4f0611af4 bridge: fix initi... |
335 336 |
if (br->stp_enabled != BR_KERNEL_STP) return; |
28a16c979 bridge: change co... |
337 338 |
br_info(br, "topology change detected, %s ", |
1da177e4c Linux-2.6.12-rc2 |
339 340 341 |
isroot ? "propagating" : "sending tcn bpdu"); if (isroot) { |
8384b5f5b net: bridge: add ... |
342 |
__br_set_topology_change(br, 1); |
1da177e4c Linux-2.6.12-rc2 |
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
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; } /* 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); } } /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
366 |
static void br_reply(struct net_bridge_port *p) |
1da177e4c Linux-2.6.12-rc2 |
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
{ br_transmit_config(p); } /* called under bridge lock */ void br_configuration_update(struct net_bridge *br) { br_root_selection(br); br_designated_port_selection(br); } /* 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; } /* 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); |
775dd692b net: bridge: add ... |
399 |
br_set_state(p, BR_STATE_BLOCKING); |
4ecb961c8 bridge: add notif... |
400 |
br_ifinfo_notify(RTM_NEWLINK, p); |
1da177e4c Linux-2.6.12-rc2 |
401 402 403 404 405 406 407 |
del_timer(&p->forward_delay_timer); } } /* called under bridge lock */ static void br_make_forwarding(struct net_bridge_port *p) { |
ef647f130 bridge: Eliminate... |
408 |
struct net_bridge *br = p->br; |
9cde07087 bridge: add suppo... |
409 |
|
ef647f130 bridge: Eliminate... |
410 411 |
if (p->state != BR_STATE_BLOCKING) return; |
a461c0297 bridge: skip forw... |
412 |
if (br->stp_enabled == BR_NO_STP || br->forward_delay == 0) { |
775dd692b net: bridge: add ... |
413 |
br_set_state(p, BR_STATE_FORWARDING); |
ef647f130 bridge: Eliminate... |
414 415 |
br_topology_change_detection(br); del_timer(&p->forward_delay_timer); |
160d73b84 bridge: minor cle... |
416 |
} else if (br->stp_enabled == BR_KERNEL_STP) |
775dd692b net: bridge: add ... |
417 |
br_set_state(p, BR_STATE_LISTENING); |
ef647f130 bridge: Eliminate... |
418 |
else |
775dd692b net: bridge: add ... |
419 |
br_set_state(p, BR_STATE_LEARNING); |
ef647f130 bridge: Eliminate... |
420 |
|
4ecb961c8 bridge: add notif... |
421 |
br_ifinfo_notify(RTM_NEWLINK, p); |
ef647f130 bridge: Eliminate... |
422 423 424 |
if (br->forward_delay != 0) mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); |
1da177e4c Linux-2.6.12-rc2 |
425 426 427 428 429 430 |
} /* called under bridge lock */ void br_port_state_selection(struct net_bridge *br) { struct net_bridge_port *p; |
1faa4356a bridge: control c... |
431 |
unsigned int liveports = 0; |
1da177e4c Linux-2.6.12-rc2 |
432 433 |
list_for_each_entry(p, &br->port_list, list) { |
1faa4356a bridge: control c... |
434 435 |
if (p->state == BR_STATE_DISABLED) continue; |
b03b6dd58 bridge: master de... |
436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
/* 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); } |
1da177e4c Linux-2.6.12-rc2 |
450 |
} |
7ce42de18 bridge: multicast... |
451 452 |
if (p->state != BR_STATE_BLOCKING) br_multicast_enable_port(p); |
9aa663821 bridge: multicast... |
453 454 455 456 |
/* 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. */ |
1faa4356a bridge: control c... |
457 458 |
if (p->state == BR_STATE_FORWARDING) ++liveports; |
1da177e4c Linux-2.6.12-rc2 |
459 |
} |
1faa4356a bridge: control c... |
460 461 462 463 464 |
if (liveports == 0) netif_carrier_off(br->dev); else netif_carrier_on(br->dev); |
1da177e4c Linux-2.6.12-rc2 |
465 466 467 |
} /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
468 |
static void br_topology_change_acknowledge(struct net_bridge_port *p) |
1da177e4c Linux-2.6.12-rc2 |
469 470 471 472 473 474 |
{ p->topology_change_ack = 1; br_transmit_config(p); } /* called under bridge lock */ |
160d73b84 bridge: minor cle... |
475 476 |
void br_received_config_bpdu(struct net_bridge_port *p, const struct br_config_bpdu *bpdu) |
1da177e4c Linux-2.6.12-rc2 |
477 478 479 |
{ struct net_bridge *br; int was_root; |
9d6f229fc [NET] BRIDGE: Fix... |
480 |
|
1da177e4c Linux-2.6.12-rc2 |
481 482 483 484 485 486 487 488 489 490 491 492 493 |
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); |
9d6f229fc [NET] BRIDGE: Fix... |
494 |
mod_timer(&br->tcn_timer, |
1da177e4c Linux-2.6.12-rc2 |
495 496 497 498 499 500 501 502 503 504 |
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); } |
9d6f229fc [NET] BRIDGE: Fix... |
505 506 |
} else if (br_is_designated_port(p)) { br_reply(p); |
1da177e4c Linux-2.6.12-rc2 |
507 508 509 510 511 512 513 |
} } /* called under bridge lock */ void br_received_tcn_bpdu(struct net_bridge_port *p) { if (br_is_designated_port(p)) { |
28a16c979 bridge: change co... |
514 515 |
br_info(p->br, "port %u(%s) received tcn bpdu ", |
95c961747 net: cleanup unsi... |
516 |
(unsigned int) p->port_no, p->dev->name); |
1da177e4c Linux-2.6.12-rc2 |
517 518 519 520 521 |
br_topology_change_detection(p->br); br_topology_change_acknowledge(p); } } |
14f98f258 bridge: range che... |
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
/* 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; } 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; } |
82dd4332a net: bridge: add ... |
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
/* 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; } |
4c656c13b bridge: allow zer... |
571 572 573 574 575 576 577 578 |
/* 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 */ |
9e0b27fe5 net: bridge: br_s... |
579 |
int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) |
c62987bbd bridge: push brid... |
580 |
{ |
c62987bbd bridge: push brid... |
581 582 |
unsigned long t = clock_t_to_jiffies(ageing_time); int err; |
82dd4332a net: bridge: add ... |
583 584 |
err = __set_ageing_time(br->dev, t); if (err) |
c62987bbd bridge: push brid... |
585 |
return err; |
34d8acd8a net: bridge: shor... |
586 587 |
spin_lock_bh(&br->lock); br->bridge_ageing_time = t; |
c62987bbd bridge: push brid... |
588 |
br->ageing_time = t; |
34d8acd8a net: bridge: shor... |
589 |
spin_unlock_bh(&br->lock); |
f7cdee8a7 bridge: move to w... |
590 |
mod_delayed_work(system_long_wq, &br->gc_work, 0); |
c62987bbd bridge: push brid... |
591 592 593 |
return 0; } |
8384b5f5b net: bridge: add ... |
594 595 596 |
/* called under bridge lock */ void __br_set_topology_change(struct net_bridge *br, unsigned char val) { |
34d8acd8a net: bridge: shor... |
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
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 ", t); } else { t = br->bridge_ageing_time; br_debug(br, "restoring ageing time to %lu ", t); } err = __set_ageing_time(br->dev, t); if (err) br_warn(br, "error offloading ageing time "); else br->ageing_time = t; } |
8384b5f5b net: bridge: add ... |
622 623 |
br->topology_change = val; } |
be4f154d5 bridge: Clamp for... |
624 625 626 627 628 629 |
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; } |
14f98f258 bridge: range che... |
630 631 632 |
int br_set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); |
8a921265e Revert "bridge: A... |
633 |
int err = -ERANGE; |
14f98f258 bridge: range che... |
634 |
|
34c2d9fb0 bridge: Allow for... |
635 |
spin_lock_bh(&br->lock); |
8a921265e Revert "bridge: A... |
636 637 638 |
if (br->stp_enabled != BR_NO_STP && (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) goto unlock; |
be4f154d5 bridge: Clamp for... |
639 |
__br_set_forward_delay(br, t); |
8a921265e Revert "bridge: A... |
640 641 642 |
err = 0; unlock: |
14f98f258 bridge: range che... |
643 |
spin_unlock_bh(&br->lock); |
8a921265e Revert "bridge: A... |
644 |
return err; |
14f98f258 bridge: range che... |
645 |
} |