/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include "byte-order.h"
+#include "connectivity.h"
#include "ofpbuf.h"
#include "packets.h"
+#include "seq.h"
+#include "unixctl.h"
#include "util.h"
#include "vlog.h"
#define STP_TYPE_CONFIG 0x00
#define STP_TYPE_TCN 0x80
+OVS_PACKED(
struct stp_bpdu_header {
ovs_be16 protocol_id; /* STP_PROTOCOL_ID. */
uint8_t protocol_version; /* STP_PROTOCOL_VERSION. */
uint8_t bpdu_type; /* One of STP_TYPE_*. */
-} __attribute__((packed));
+});
BUILD_ASSERT_DECL(sizeof(struct stp_bpdu_header) == 4);
enum stp_config_bpdu_flags {
STP_CONFIG_TOPOLOGY_CHANGE = 0x01
};
+OVS_PACKED(
struct stp_config_bpdu {
struct stp_bpdu_header header; /* Type STP_TYPE_CONFIG. */
uint8_t flags; /* STP_CONFIG_* flags. */
ovs_be16 max_age; /* 8.5.1.6: Timeout for received data. */
ovs_be16 hello_time; /* 8.5.1.7: Time between BPDU generation. */
ovs_be16 forward_delay; /* 8.5.1.8: State progression delay. */
-} __attribute__((packed));
+});
BUILD_ASSERT_DECL(sizeof(struct stp_config_bpdu) == 35);
+OVS_PACKED(
struct stp_tcn_bpdu {
struct stp_bpdu_header header; /* Type STP_TYPE_TCN. */
-} __attribute__((packed));
+});
BUILD_ASSERT_DECL(sizeof(struct stp_tcn_bpdu) == 4);
struct stp_timer {
};
struct stp {
+ struct list node; /* Node in all_stps list. */
+
/* Static bridge data. */
char *name; /* Human-readable name for log messages. */
stp_identifier bridge_id; /* 8.5.3.7: This bridge. */
struct stp_port ports[STP_MAX_PORTS];
/* Interface to client. */
+ bool fdb_needs_flush; /* MAC learning tables needs flushing. */
struct stp_port *first_changed_port;
void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux);
void *aux;
+
+ struct ovs_refcount ref_cnt;
};
+static struct ovs_mutex mutex;
+static struct list all_stps__ = LIST_INITIALIZER(&all_stps__);
+static struct list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
+
#define FOR_EACH_ENABLED_PORT(PORT, STP) \
for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \
(PORT); \
(PORT) = stp_next_enabled_port((STP), (PORT) + 1))
static struct stp_port *
stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
+ OVS_REQUIRES(mutex)
{
for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
if (port->state != STP_DISABLED) {
- return (struct stp_port *) port;
+ return CONST_CAST(struct stp_port *, port);
}
}
return NULL;
#define MESSAGE_AGE_INCREMENT 1
-static void stp_transmit_config(struct stp_port *);
+static void stp_transmit_config(struct stp_port *) OVS_REQUIRES(mutex);
static bool stp_supersedes_port_info(const struct stp_port *,
- const struct stp_config_bpdu *);
+ const struct stp_config_bpdu *)
+ OVS_REQUIRES(mutex);
static void stp_record_config_information(struct stp_port *,
- const struct stp_config_bpdu *);
+ const struct stp_config_bpdu *)
+ OVS_REQUIRES(mutex);
static void stp_record_config_timeout_values(struct stp *,
- const struct stp_config_bpdu *);
-static bool stp_is_designated_port(const struct stp_port *);
-static void stp_config_bpdu_generation(struct stp *);
-static void stp_transmit_tcn(struct stp *);
-static void stp_configuration_update(struct stp *);
+ const struct stp_config_bpdu *)
+ OVS_REQUIRES(mutex);
+static bool stp_is_designated_port(const struct stp_port *)
+ OVS_REQUIRES(mutex);
+static void stp_config_bpdu_generation(struct stp *) OVS_REQUIRES(mutex);
+static void stp_transmit_tcn(struct stp *) OVS_REQUIRES(mutex);
+static void stp_configuration_update(struct stp *) OVS_REQUIRES(mutex);
static bool stp_supersedes_root(const struct stp_port *root,
- const struct stp_port *);
-static void stp_root_selection(struct stp *);
-static void stp_designated_port_selection(struct stp *);
-static void stp_become_designated_port(struct stp_port *);
-static void stp_port_state_selection(struct stp *);
-static void stp_make_forwarding(struct stp_port *);
-static void stp_make_blocking(struct stp_port *);
-static void stp_set_port_state(struct stp_port *, enum stp_state);
-static void stp_topology_change_detection(struct stp *);
-static void stp_topology_change_acknowledged(struct stp *);
-static void stp_acknowledge_topology_change(struct stp_port *);
+ const struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_root_selection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_designated_port_selection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_become_designated_port(struct stp_port *)
+ OVS_REQUIRES(mutex);
+static void stp_port_state_selection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_make_forwarding(struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_make_blocking(struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_set_port_state(struct stp_port *, enum stp_state)
+ OVS_REQUIRES(mutex);
+static void stp_topology_change_detection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_topology_change_acknowledged(struct stp *)
+ OVS_REQUIRES(mutex);
+static void stp_acknowledge_topology_change(struct stp_port *)
+ OVS_REQUIRES(mutex);
static void stp_received_config_bpdu(struct stp *, struct stp_port *,
- const struct stp_config_bpdu *);
-static void stp_received_tcn_bpdu(struct stp *, struct stp_port *);
-static void stp_hello_timer_expiry(struct stp *);
-static void stp_message_age_timer_expiry(struct stp_port *);
-static bool stp_is_designated_for_some_port(const struct stp *);
-static void stp_forward_delay_timer_expiry(struct stp_port *);
-static void stp_tcn_timer_expiry(struct stp *);
-static void stp_topology_change_timer_expiry(struct stp *);
-static void stp_hold_timer_expiry(struct stp_port *);
-static void stp_initialize_port(struct stp_port *, enum stp_state);
-static void stp_become_root_bridge(struct stp *);
-static void stp_update_bridge_timers(struct stp *);
+ const struct stp_config_bpdu *)
+ OVS_REQUIRES(mutex);
+static void stp_received_tcn_bpdu(struct stp *, struct stp_port *)
+ OVS_REQUIRES(mutex);
+static void stp_hello_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
+static void stp_message_age_timer_expiry(struct stp_port *)
+ OVS_REQUIRES(mutex);
+static bool stp_is_designated_for_some_port(const struct stp *)
+ OVS_REQUIRES(mutex);
+static void stp_forward_delay_timer_expiry(struct stp_port *)
+ OVS_REQUIRES(mutex);
+static void stp_tcn_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
+static void stp_topology_change_timer_expiry(struct stp *)
+ OVS_REQUIRES(mutex);
+static void stp_hold_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_initialize_port(struct stp_port *, enum stp_state)
+ OVS_REQUIRES(mutex);
+static void stp_become_root_bridge(struct stp *) OVS_REQUIRES(mutex);
+static void stp_update_bridge_timers(struct stp *) OVS_REQUIRES(mutex);
static int clamp(int x, int min, int max);
static int ms_to_timer(int ms);
-static int ms_to_timer_remainder(int ms);
static int timer_to_ms(int timer);
static void stp_start_timer(struct stp_timer *, int value);
static void stp_stop_timer(struct stp_timer *);
static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout);
-static void stp_send_bpdu(struct stp_port *, const void *, size_t);
+static void stp_send_bpdu(struct stp_port *, const void *, size_t)
+ OVS_REQUIRES(mutex);
+static void stp_unixctl_tcn(struct unixctl_conn *, int argc,
+ const char *argv[], void *aux);
+
+void
+stp_init(void)
+{
+ unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn,
+ NULL);
+}
/* Creates and returns a new STP instance that initially has no ports enabled.
*
void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
void *aux)
{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
struct stp *stp;
struct stp_port *p;
+ if (ovsthread_once_start(&once)) {
+ /* We need a recursive mutex because stp_send_bpdu() could loop back
+ * into the stp module through a patch port. This happens
+ * intentionally as part of the unit tests. Ideally we'd ditch
+ * the call back function, but for now this is what we have. */
+ ovs_mutex_init_recursive(&mutex);
+ ovsthread_once_done(&once);
+ }
+
+ ovs_mutex_lock(&mutex);
stp = xzalloc(sizeof *stp);
stp->name = xstrdup(name);
stp->bridge_id = bridge_id;
p->path_cost = 19; /* Recommended default for 100 Mb/s link. */
stp_initialize_port(p, STP_DISABLED);
}
+ ovs_refcount_init(&stp->ref_cnt);
+
+ list_push_back(all_stps, &stp->node);
+ ovs_mutex_unlock(&mutex);
+ return stp;
+}
+
+struct stp *
+stp_ref(const struct stp *stp_)
+{
+ struct stp *stp = CONST_CAST(struct stp *, stp_);
+ if (stp) {
+ ovs_refcount_ref(&stp->ref_cnt);
+ }
return stp;
}
/* Destroys 'stp'. */
void
-stp_destroy(struct stp *stp)
+stp_unref(struct stp *stp)
{
- if (stp) {
+ if (stp && ovs_refcount_unref(&stp->ref_cnt) == 1) {
+ ovs_mutex_lock(&mutex);
+ list_remove(&stp->node);
+ ovs_mutex_unlock(&mutex);
free(stp->name);
+ ovs_refcount_destroy(&stp->ref_cnt);
free(stp);
}
}
struct stp_port *p;
int elapsed;
+ ovs_mutex_lock(&mutex);
/* Convert 'ms' to STP timer ticks. Preserve any leftover milliseconds
* from previous stp_tick() calls so that we don't lose STP ticks when we
* are called too frequently. */
ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder;
elapsed = ms_to_timer(ms);
- stp->elapsed_remainder = ms_to_timer_remainder(ms);
+ stp->elapsed_remainder = ms - timer_to_ms(elapsed);
if (!elapsed) {
- return;
+ goto out;
}
if (stp_timer_expired(&stp->hello_timer, elapsed, stp->hello_time)) {
stp_hold_timer_expiry(p);
}
}
+
+out:
+ ovs_mutex_unlock(&mutex);
}
static void
set_bridge_id(struct stp *stp, stp_identifier new_bridge_id)
+ OVS_REQUIRES(mutex)
{
if (new_bridge_id != stp->bridge_id) {
bool root;
{
const uint64_t mac_bits = (UINT64_C(1) << 48) - 1;
const uint64_t pri_bits = ~mac_bits;
+ ovs_mutex_lock(&mutex);
set_bridge_id(stp, (stp->bridge_id & pri_bits) | (bridge_id & mac_bits));
+ ovs_mutex_unlock(&mutex);
}
void
stp_set_bridge_priority(struct stp *stp, uint16_t new_priority)
{
const uint64_t mac_bits = (UINT64_C(1) << 48) - 1;
+ ovs_mutex_lock(&mutex);
set_bridge_id(stp, ((stp->bridge_id & mac_bits)
| ((uint64_t) new_priority << 48)));
+ ovs_mutex_unlock(&mutex);
}
/* Sets the desired hello time for 'stp' to 'ms', in milliseconds. The actual
void
stp_set_hello_time(struct stp *stp, int ms)
{
+ ovs_mutex_lock(&mutex);
stp->rq_hello_time = ms;
stp_update_bridge_timers(stp);
+ ovs_mutex_unlock(&mutex);
}
/* Sets the desired max age for 'stp' to 'ms', in milliseconds. The actual max
void
stp_set_max_age(struct stp *stp, int ms)
{
+ ovs_mutex_lock(&mutex);
stp->rq_max_age = ms;
stp_update_bridge_timers(stp);
+ ovs_mutex_unlock(&mutex);
}
/* Sets the desired forward delay for 'stp' to 'ms', in milliseconds. The
void
stp_set_forward_delay(struct stp *stp, int ms)
{
+ ovs_mutex_lock(&mutex);
stp->rq_forward_delay = ms;
stp_update_bridge_timers(stp);
+ ovs_mutex_unlock(&mutex);
}
/* Returns the name given to 'stp' in the call to stp_create(). */
const char *
stp_get_name(const struct stp *stp)
{
- return stp->name;
+ char *name;
+
+ ovs_mutex_lock(&mutex);
+ name = stp->name;
+ ovs_mutex_unlock(&mutex);
+ return name;
}
/* Returns the bridge ID for 'stp'. */
stp_identifier
stp_get_bridge_id(const struct stp *stp)
{
- return stp->bridge_id;
+ stp_identifier bridge_id;
+
+ ovs_mutex_lock(&mutex);
+ bridge_id = stp->bridge_id;
+ ovs_mutex_unlock(&mutex);
+ return bridge_id;
}
/* Returns the bridge ID of the bridge currently believed to be the root. */
stp_identifier
stp_get_designated_root(const struct stp *stp)
{
- return stp->designated_root;
+ stp_identifier designated_root;
+
+ ovs_mutex_lock(&mutex);
+ designated_root = stp->designated_root;
+ ovs_mutex_unlock(&mutex);
+ return designated_root;
}
/* Returns true if 'stp' believes itself to the be root of the spanning tree,
bool
stp_is_root_bridge(const struct stp *stp)
{
- return stp->bridge_id == stp->designated_root;
+ bool is_root;
+
+ ovs_mutex_lock(&mutex);
+ is_root = stp->bridge_id == stp->designated_root;
+ ovs_mutex_unlock(&mutex);
+ return is_root;
}
/* Returns the cost of the path from 'stp' to the root of the spanning tree. */
int
stp_get_root_path_cost(const struct stp *stp)
{
- return stp->root_path_cost;
+ int cost;
+
+ ovs_mutex_lock(&mutex);
+ cost = stp->root_path_cost;
+ ovs_mutex_unlock(&mutex);
+ return cost;
}
/* Returns the bridge hello time, in ms. The returned value is not necessarily
int
stp_get_hello_time(const struct stp *stp)
{
- return timer_to_ms(stp->bridge_hello_time);
+ int time;
+
+ ovs_mutex_lock(&mutex);
+ time = timer_to_ms(stp->bridge_hello_time);
+ ovs_mutex_unlock(&mutex);
+ return time;
}
/* Returns the bridge max age, in ms. The returned value is not necessarily
int
stp_get_max_age(const struct stp *stp)
{
- return timer_to_ms(stp->bridge_max_age);
+ int time;
+
+ ovs_mutex_lock(&mutex);
+ time = timer_to_ms(stp->bridge_max_age);
+ ovs_mutex_unlock(&mutex);
+ return time;
}
/* Returns the bridge forward delay, in ms. The returned value is not
int
stp_get_forward_delay(const struct stp *stp)
{
- return timer_to_ms(stp->bridge_forward_delay);
+ int time;
+
+ ovs_mutex_lock(&mutex);
+ time = timer_to_ms(stp->bridge_forward_delay);
+ ovs_mutex_unlock(&mutex);
+ return time;
+}
+
+/* Returns true if something has happened to 'stp' which necessitates flushing
+ * the client's MAC learning table. Calling this function resets 'stp' so that
+ * future calls will return false until flushing is required again. */
+bool
+stp_check_and_reset_fdb_flush(struct stp *stp)
+{
+ bool needs_flush;
+
+ ovs_mutex_lock(&mutex);
+ needs_flush = stp->fdb_needs_flush;
+ stp->fdb_needs_flush = false;
+ ovs_mutex_unlock(&mutex);
+ return needs_flush;
}
/* Returns the port in 'stp' with index 'port_no', which must be between 0 and
struct stp_port *
stp_get_port(struct stp *stp, int port_no)
{
- assert(port_no >= 0 && port_no < ARRAY_SIZE(stp->ports));
- return &stp->ports[port_no];
+ struct stp_port *port;
+
+ ovs_mutex_lock(&mutex);
+ ovs_assert(port_no >= 0 && port_no < ARRAY_SIZE(stp->ports));
+ port = &stp->ports[port_no];
+ ovs_mutex_unlock(&mutex);
+ return port;
}
/* Returns the port connecting 'stp' to the root bridge, or a null pointer if
struct stp_port *
stp_get_root_port(struct stp *stp)
{
- return stp->root_port;
+ struct stp_port *port;
+
+ ovs_mutex_lock(&mutex);
+ port = stp->root_port;
+ ovs_mutex_unlock(&mutex);
+ return port;
}
/* Finds a port whose state has changed. If successful, stores the port whose
bool
stp_get_changed_port(struct stp *stp, struct stp_port **portp)
{
- struct stp_port *end = &stp->ports[ARRAY_SIZE(stp->ports)];
- struct stp_port *p;
+ struct stp_port *end, *p;
+ bool changed = false;
+ ovs_mutex_lock(&mutex);
+ end = &stp->ports[ARRAY_SIZE(stp->ports)];
for (p = stp->first_changed_port; p < end; p++) {
if (p->state_changed) {
p->state_changed = false;
stp->first_changed_port = p + 1;
*portp = p;
- return true;
+ changed = true;
+ goto out;
}
}
stp->first_changed_port = end;
*portp = NULL;
- return false;
+
+out:
+ ovs_mutex_unlock(&mutex);
+ return changed;
}
/* Returns the name for the given 'state' (for use in debugging and log
case STP_BLOCKING:
return "blocking";
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
}
case STP_ROLE_DISABLED:
return "disabled";
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
}
struct stp *stp = p->stp;
const struct stp_bpdu_header *header;
+ ovs_mutex_lock(&mutex);
if (p->state == STP_DISABLED) {
- return;
+ goto out;
}
if (bpdu_size < sizeof(struct stp_bpdu_header)) {
- VLOG_WARN("%s: received runt %zu-byte BPDU", stp->name, bpdu_size);
+ VLOG_WARN("%s: received runt %"PRIuSIZE"-byte BPDU", stp->name, bpdu_size);
p->error_count++;
- return;
+ goto out;
}
header = bpdu;
VLOG_WARN("%s: received BPDU with unexpected protocol ID %"PRIu16,
stp->name, ntohs(header->protocol_id));
p->error_count++;
- return;
+ goto out;
}
if (header->protocol_version != STP_PROTOCOL_VERSION) {
VLOG_DBG("%s: received BPDU with unexpected protocol version %"PRIu8,
switch (header->bpdu_type) {
case STP_TYPE_CONFIG:
if (bpdu_size < sizeof(struct stp_config_bpdu)) {
- VLOG_WARN("%s: received config BPDU with invalid size %zu",
+ VLOG_WARN("%s: received config BPDU with invalid size %"PRIuSIZE,
stp->name, bpdu_size);
p->error_count++;
- return;
+ goto out;
}
stp_received_config_bpdu(stp, p, bpdu);
break;
case STP_TYPE_TCN:
if (bpdu_size != sizeof(struct stp_tcn_bpdu)) {
- VLOG_WARN("%s: received TCN BPDU with invalid size %zu",
+ VLOG_WARN("%s: received TCN BPDU with invalid size %"PRIuSIZE,
stp->name, bpdu_size);
p->error_count++;
- return;
+ goto out;
}
stp_received_tcn_bpdu(stp, p);
break;
VLOG_WARN("%s: received BPDU of unexpected type %"PRIu8,
stp->name, header->bpdu_type);
p->error_count++;
- return;
+ goto out;
}
p->rx_count++;
+
+out:
+ ovs_mutex_unlock(&mutex);
}
/* Returns the STP entity in which 'p' is nested. */
struct stp *
stp_port_get_stp(struct stp_port *p)
{
- return p->stp;
+ struct stp *stp;
+
+ ovs_mutex_lock(&mutex);
+ stp = p->stp;
+ ovs_mutex_unlock(&mutex);
+ return stp;
}
/* Sets the 'aux' member of 'p'.
void
stp_port_set_aux(struct stp_port *p, void *aux)
{
+ ovs_mutex_lock(&mutex);
p->aux = aux;
+ ovs_mutex_unlock(&mutex);
}
/* Returns the 'aux' member of 'p'. */
void *
stp_port_get_aux(struct stp_port *p)
{
- return p->aux;
+ void *aux;
+
+ ovs_mutex_lock(&mutex);
+ aux = p->aux;
+ ovs_mutex_unlock(&mutex);
+ return aux;
}
/* Returns the index of port 'p' within its bridge. */
int
stp_port_no(const struct stp_port *p)
{
- struct stp *stp = p->stp;
- assert(p >= stp->ports && p < &stp->ports[ARRAY_SIZE(stp->ports)]);
- return p - stp->ports;
+ struct stp *stp;
+ int index;
+
+ ovs_mutex_lock(&mutex);
+ stp = p->stp;
+ ovs_assert(p >= stp->ports && p < &stp->ports[ARRAY_SIZE(stp->ports)]);
+ index = p - p->stp->ports;
+ ovs_mutex_unlock(&mutex);
+ return index;
}
/* Returns the port ID for 'p'. */
int
stp_port_get_id(const struct stp_port *p)
{
- return p->port_id;
+ int port_id;
+
+ ovs_mutex_lock(&mutex);
+ port_id = p->port_id;
+ ovs_mutex_unlock(&mutex);
+ return port_id;
}
/* Returns the state of port 'p'. */
enum stp_state
stp_port_get_state(const struct stp_port *p)
{
- return p->state;
+ enum stp_state state;
+
+ ovs_mutex_lock(&mutex);
+ state = p->state;
+ ovs_mutex_unlock(&mutex);
+ return state;
}
/* Returns the role of port 'p'. */
enum stp_role
stp_port_get_role(const struct stp_port *p)
{
- struct stp_port *root_port = stp_get_root_port(p->stp);
+ struct stp_port *root_port;
+ enum stp_role role;
+ ovs_mutex_lock(&mutex);
+ root_port = p->stp->root_port;
if (root_port && root_port->port_id == p->port_id) {
- return STP_ROLE_ROOT;
+ role = STP_ROLE_ROOT;
} else if (stp_is_designated_port(p)) {
- return STP_ROLE_DESIGNATED;
+ role = STP_ROLE_DESIGNATED;
} else if (p->state == STP_DISABLED) {
- return STP_ROLE_DISABLED;
+ role = STP_ROLE_DISABLED;
} else {
- return STP_ROLE_ALTERNATE;
+ role = STP_ROLE_ALTERNATE;
}
+ ovs_mutex_unlock(&mutex);
+ return role;
}
/* Retrieves BPDU transmit and receive counts for 'p'. */
-void stp_port_get_counts(const struct stp_port *p,
- int *tx_count, int *rx_count, int *error_count)
+void
+stp_port_get_counts(const struct stp_port *p,
+ int *tx_count, int *rx_count, int *error_count)
{
+ ovs_mutex_lock(&mutex);
*tx_count = p->tx_count;
*rx_count = p->rx_count;
*error_count = p->error_count;
+ ovs_mutex_unlock(&mutex);
}
/* Disables STP on port 'p'. */
void
stp_port_disable(struct stp_port *p)
{
- struct stp *stp = p->stp;
+ struct stp *stp;
+
+ ovs_mutex_lock(&mutex);
+ stp = p->stp;
if (p->state != STP_DISABLED) {
bool root = stp_is_root_bridge(stp);
stp_become_designated_port(p);
}
p->aux = NULL;
}
+ ovs_mutex_unlock(&mutex);
}
/* Enables STP on port 'p'. The port will initially be in "blocking" state. */
void
stp_port_enable(struct stp_port *p)
{
+ ovs_mutex_lock(&mutex);
if (p->state == STP_DISABLED) {
stp_initialize_port(p, STP_BLOCKING);
stp_port_state_selection(p->stp);
}
+ ovs_mutex_unlock(&mutex);
}
/* Sets the priority of port 'p' to 'new_priority'. Lower numerical values
void
stp_port_set_priority(struct stp_port *p, uint8_t new_priority)
{
- uint16_t new_port_id = (p->port_id & 0xff) | (new_priority << 8);
+ uint16_t new_port_id;
+
+ ovs_mutex_lock(&mutex);
+ new_port_id = (p->port_id & 0xff) | (new_priority << 8);
if (p->port_id != new_port_id) {
struct stp *stp = p->stp;
if (stp_is_designated_port(p)) {
stp_port_state_selection(stp);
}
}
+ ovs_mutex_unlock(&mutex);
}
/* Convert 'speed' (measured in Mb/s) into the path cost. */
uint16_t
stp_convert_speed_to_cost(unsigned int speed)
{
- return speed >= 10000 ? 2 /* 10 Gb/s. */
- : speed >= 1000 ? 4 /* 1 Gb/s. */
- : speed >= 100 ? 19 /* 100 Mb/s. */
- : speed >= 16 ? 62 /* 16 Mb/s. */
- : speed >= 10 ? 100 /* 10 Mb/s. */
- : speed >= 4 ? 250 /* 4 Mb/s. */
- : 19; /* 100 Mb/s (guess). */
+ uint16_t ret;
+
+ ovs_mutex_lock(&mutex);
+ ret = speed >= 10000 ? 2 /* 10 Gb/s. */
+ : speed >= 1000 ? 4 /* 1 Gb/s. */
+ : speed >= 100 ? 19 /* 100 Mb/s. */
+ : speed >= 16 ? 62 /* 16 Mb/s. */
+ : speed >= 10 ? 100 /* 10 Mb/s. */
+ : speed >= 4 ? 250 /* 4 Mb/s. */
+ : 19; /* 100 Mb/s (guess). */
+ ovs_mutex_unlock(&mutex);
+ return ret;
}
/* Sets the path cost of port 'p' to 'path_cost'. Lower values are generally
void
stp_port_set_path_cost(struct stp_port *p, uint16_t path_cost)
{
+ ovs_mutex_lock(&mutex);
if (p->path_cost != path_cost) {
struct stp *stp = p->stp;
p->path_cost = path_cost;
stp_configuration_update(stp);
stp_port_state_selection(stp);
}
+ ovs_mutex_unlock(&mutex);
}
/* Sets the path cost of port 'p' based on 'speed' (measured in Mb/s). */
}
\f
static void
-stp_transmit_config(struct stp_port *p)
+stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
bool root = stp_is_root_bridge(stp);
static bool
stp_supersedes_port_info(const struct stp_port *p,
const struct stp_config_bpdu *config)
+ OVS_REQUIRES(mutex)
{
if (ntohll(config->root_id) != p->designated_root) {
return ntohll(config->root_id) < p->designated_root;
static void
stp_record_config_information(struct stp_port *p,
const struct stp_config_bpdu *config)
+ OVS_REQUIRES(mutex)
{
p->designated_root = ntohll(config->root_id);
p->designated_cost = ntohl(config->root_path_cost);
static void
stp_record_config_timeout_values(struct stp *stp,
const struct stp_config_bpdu *config)
+ OVS_REQUIRES(mutex)
{
stp->max_age = ntohs(config->max_age);
stp->hello_time = ntohs(config->hello_time);
}
static bool
-stp_is_designated_port(const struct stp_port *p)
+stp_is_designated_port(const struct stp_port *p) OVS_REQUIRES(mutex)
{
return (p->designated_bridge == p->stp->bridge_id
&& p->designated_port == p->port_id);
}
static void
-stp_config_bpdu_generation(struct stp *stp)
+stp_config_bpdu_generation(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_transmit_tcn(struct stp *stp)
+stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p = stp->root_port;
struct stp_tcn_bpdu tcn_bpdu;
}
static void
-stp_configuration_update(struct stp *stp)
+stp_configuration_update(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_root_selection(stp);
stp_designated_port_selection(stp);
+ seq_change(connectivity_seq_get());
}
static bool
stp_supersedes_root(const struct stp_port *root, const struct stp_port *p)
+ OVS_REQUIRES(mutex)
{
int p_cost = p->designated_cost + p->path_cost;
int root_cost = root->designated_cost + root->path_cost;
}
static void
-stp_root_selection(struct stp *stp)
+stp_root_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p, *root;
}
static void
-stp_designated_port_selection(struct stp *stp)
+stp_designated_port_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_become_designated_port(struct stp_port *p)
+stp_become_designated_port(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
p->designated_root = stp->designated_root;
}
static void
-stp_port_state_selection(struct stp *stp)
+stp_port_state_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_make_forwarding(struct stp_port *p)
+stp_make_forwarding(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->state == STP_BLOCKING) {
stp_set_port_state(p, STP_LISTENING);
}
static void
-stp_make_blocking(struct stp_port *p)
+stp_make_blocking(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (!(p->state & (STP_DISABLED | STP_BLOCKING))) {
if (p->state & (STP_FORWARDING | STP_LEARNING)) {
static void
stp_set_port_state(struct stp_port *p, enum stp_state state)
+ OVS_REQUIRES(mutex)
{
if (state != p->state && !p->state_changed) {
p->state_changed = true;
if (p < p->stp->first_changed_port) {
p->stp->first_changed_port = p;
}
+ seq_change(connectivity_seq_get());
}
p->state = state;
}
static void
-stp_topology_change_detection(struct stp *stp)
+stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex)
{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
if (stp_is_root_bridge(stp)) {
stp->topology_change = true;
stp_start_timer(&stp->topology_change_timer, 0);
stp_transmit_tcn(stp);
stp_start_timer(&stp->tcn_timer, 0);
}
+ stp->fdb_needs_flush = true;
stp->topology_change_detected = true;
+ seq_change(connectivity_seq_get());
+ VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name);
}
static void
-stp_topology_change_acknowledged(struct stp *stp)
+stp_topology_change_acknowledged(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->topology_change_detected = false;
stp_stop_timer(&stp->tcn_timer);
}
static void
-stp_acknowledge_topology_change(struct stp_port *p)
+stp_acknowledge_topology_change(struct stp_port *p) OVS_REQUIRES(mutex)
{
p->topology_change_ack = true;
stp_transmit_config(p);
static void
stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
const struct stp_config_bpdu *config)
+ OVS_REQUIRES(mutex)
{
if (ntohs(config->message_age) >= ntohs(config->max_age)) {
VLOG_WARN("%s: received config BPDU with message age (%u) greater "
if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) {
stp_topology_change_acknowledged(stp);
}
+ if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE) {
+ stp->fdb_needs_flush = true;
+ }
}
} else if (stp_is_designated_port(p)) {
stp_transmit_config(p);
static void
stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
+ OVS_REQUIRES(mutex)
{
if (p->state != STP_DISABLED) {
if (stp_is_designated_port(p)) {
}
static void
-stp_hello_timer_expiry(struct stp *stp)
+stp_hello_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_config_bpdu_generation(stp);
stp_start_timer(&stp->hello_timer, 0);
}
static void
-stp_message_age_timer_expiry(struct stp_port *p)
+stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
bool root = stp_is_root_bridge(stp);
}
static bool
-stp_is_designated_for_some_port(const struct stp *stp)
+stp_is_designated_for_some_port(const struct stp *stp) OVS_REQUIRES(mutex)
{
const struct stp_port *p;
}
static void
-stp_forward_delay_timer_expiry(struct stp_port *p)
+stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->state == STP_LISTENING) {
stp_set_port_state(p, STP_LEARNING);
}
static void
-stp_tcn_timer_expiry(struct stp *stp)
+stp_tcn_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_transmit_tcn(stp);
stp_start_timer(&stp->tcn_timer, 0);
}
static void
-stp_topology_change_timer_expiry(struct stp *stp)
+stp_topology_change_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->topology_change_detected = false;
stp->topology_change = false;
}
static void
-stp_hold_timer_expiry(struct stp_port *p)
+stp_hold_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->config_pending) {
stp_transmit_config(p);
static void
stp_initialize_port(struct stp_port *p, enum stp_state state)
+ OVS_REQUIRES(mutex)
{
- assert(state & (STP_DISABLED | STP_BLOCKING));
+ ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
stp_become_designated_port(p);
stp_set_port_state(p, state);
p->topology_change_ack = false;
}
static void
-stp_become_root_bridge(struct stp *stp)
+stp_become_root_bridge(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->max_age = stp->bridge_max_age;
stp->hello_time = stp->bridge_hello_time;
}
static void
-stp_start_timer(struct stp_timer *timer, int value)
+stp_start_timer(struct stp_timer *timer, int value) OVS_REQUIRES(mutex)
{
timer->value = value;
timer->active = true;
}
static void
-stp_stop_timer(struct stp_timer *timer)
+stp_stop_timer(struct stp_timer *timer) OVS_REQUIRES(mutex)
{
timer->active = false;
}
static bool
stp_timer_expired(struct stp_timer *timer, int elapsed, int timeout)
+ OVS_REQUIRES(mutex)
{
if (timer->active) {
timer->value += elapsed;
return ms * 0x100 / 1000;
}
-/* Returns the number of leftover milliseconds when 'ms' is converted to STP
- * timer ticks. */
-static int
-ms_to_timer_remainder(int ms)
-{
- return ms * 0x100 % 1000;
-}
-
/* Returns the number of whole milliseconds in 'timer' STP timer ticks. There
* are 256 STP timer ticks per second. */
static int
}
static void
-stp_update_bridge_timers(struct stp *stp)
+stp_update_bridge_timers(struct stp *stp) OVS_REQUIRES(mutex)
{
int ht, ma, fd;
static void
stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
+ OVS_REQUIRES(mutex)
{
struct eth_header *eth;
struct llc_header *llc;
p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux);
p->tx_count++;
}
+\f
+/* Unixctl. */
+
+static struct stp *
+stp_find(const char *name) OVS_REQUIRES(mutex)
+{
+ struct stp *stp;
+
+ LIST_FOR_EACH (stp, node, all_stps) {
+ if (!strcmp(stp->name, name)) {
+ return stp;
+ }
+ }
+ return NULL;
+}
+
+static void
+stp_unixctl_tcn(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ ovs_mutex_lock(&mutex);
+ if (argc > 1) {
+ struct stp *stp = stp_find(argv[1]);
+
+ if (!stp) {
+ unixctl_command_reply_error(conn, "no such stp object");
+ goto out;
+ }
+ stp_topology_change_detection(stp);
+ } else {
+ struct stp *stp;
+
+ LIST_FOR_EACH (stp, node, all_stps) {
+ stp_topology_change_detection(stp);
+ }
+ }
+
+ unixctl_command_reply(conn, "OK");
+
+out:
+ ovs_mutex_unlock(&mutex);
+}