ofpbuf: Use ptrdiff_t for pointer delta.
[cascardo/ovs.git] / lib / stp.c
index 2ff9df7..467b0ba 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 <inttypes.h>
 #include <stdlib.h>
 #include "byte-order.h"
+#include "connectivity.h"
 #include "ofpbuf.h"
+#include "ovs-atomic.h"
+#include "dp-packet.h"
 #include "packets.h"
+#include "seq.h"
 #include "unixctl.h"
 #include "util.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(stp);
 
+static struct vlog_rate_limit stp_rl = VLOG_RATE_LIMIT_INIT(60, 60);
+
 #define STP_PROTOCOL_ID 0x0000
 #define STP_PROTOCOL_VERSION 0x00
 #define STP_TYPE_CONFIG 0x00
@@ -80,6 +86,7 @@ struct stp_timer {
 
 struct stp_port {
     struct stp *stp;
+    char *port_name;                /* Human-readable name for log messages. */
     void *aux;                      /* Auxiliary data the user may retrieve. */
     int port_id;                    /* 8.5.5.1: Unique port identifier. */
     enum stp_state state;           /* 8.5.5.2: Current state. */
@@ -104,7 +111,7 @@ struct stp_port {
 };
 
 struct stp {
-    struct list node;               /* Node in all_stps list. */
+    struct ovs_list node;           /* Node in all_stps list. */
 
     /* Static bridge data. */
     char *name;                     /* Human-readable name for log messages. */
@@ -138,15 +145,15 @@ struct stp {
     /* 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 (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux);
     void *aux;
 
-    atomic_int ref_cnt;
+    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__;
+static struct ovs_list all_stps__ = OVS_LIST_INITIALIZER(&all_stps__);
+static struct ovs_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);   \
@@ -154,7 +161,7 @@ static struct list *const all_stps OVS_GUARDED_BY(&mutex) = &all_stps__;
          (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_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
         if (port->state != STP_DISABLED) {
@@ -166,57 +173,57 @@ stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
 
 #define MESSAGE_AGE_INCREMENT 1
 
-static void stp_transmit_config(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+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 *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_record_config_information(struct stp_port *,
                                           const struct stp_config_bpdu *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_record_config_timeout_values(struct stp *,
                                              const struct stp_config_bpdu  *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static bool stp_is_designated_port(const struct stp_port *)
-    OVS_REQ_WRLOCK(mutex);
-static void stp_config_bpdu_generation(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_transmit_tcn(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_configuration_update(struct stp *) OVS_REQ_WRLOCK(mutex);
+    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 *) OVS_REQ_WRLOCK(mutex);
-static void stp_root_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_designated_port_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
+                                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_REQ_WRLOCK(mutex);
-static void stp_port_state_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_make_forwarding(struct stp_port *) OVS_REQ_WRLOCK(mutex);
-static void stp_make_blocking(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+    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_REQ_WRLOCK(mutex);
-static void stp_topology_change_detection(struct stp *) OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
+static void stp_topology_change_detection(struct stp *) OVS_REQUIRES(mutex);
 static void stp_topology_change_acknowledged(struct stp *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_acknowledge_topology_change(struct stp_port *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_received_config_bpdu(struct stp *, struct stp_port *,
                                      const struct stp_config_bpdu *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_received_tcn_bpdu(struct stp *, struct stp_port *)
-    OVS_REQ_WRLOCK(mutex);
-static void stp_hello_timer_expiry(struct stp *) OVS_REQ_WRLOCK(mutex);
+    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_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static bool stp_is_designated_for_some_port(const struct stp *)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_forward_delay_timer_expiry(struct stp_port *)
-    OVS_REQ_WRLOCK(mutex);
-static void stp_tcn_timer_expiry(struct stp *) OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
+static void stp_tcn_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
 static void stp_topology_change_timer_expiry(struct stp *)
-    OVS_REQ_WRLOCK(mutex);
-static void stp_hold_timer_expiry(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+    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_REQ_WRLOCK(mutex);
-static void stp_become_root_bridge(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_update_bridge_timers(struct stp *) OVS_REQ_WRLOCK(mutex);
+    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);
@@ -226,7 +233,7 @@ 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)
-    OVS_REQ_WRLOCK(mutex);
+    OVS_REQUIRES(mutex);
 static void stp_unixctl_tcn(struct unixctl_conn *, int argc,
                             const char *argv[], void *aux);
 
@@ -252,7 +259,7 @@ stp_init(void)
  */
 struct stp *
 stp_create(const char *name, stp_identifier bridge_id,
-           void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
+           void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux),
            void *aux)
 {
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
@@ -264,7 +271,7 @@ stp_create(const char *name, stp_identifier bridge_id,
          * 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(&mutex,  PTHREAD_MUTEX_RECURSIVE);
+        ovs_mutex_init_recursive(&mutex);
         ovsthread_once_done(&once);
     }
 
@@ -304,7 +311,7 @@ stp_create(const char *name, stp_identifier bridge_id,
         p->path_cost = 19;      /* Recommended default for 100 Mb/s link. */
         stp_initialize_port(p, STP_DISABLED);
     }
-    atomic_init(&stp->ref_cnt, 1);
+    ovs_refcount_init(&stp->ref_cnt);
 
     list_push_back(all_stps, &stp->node);
     ovs_mutex_unlock(&mutex);
@@ -316,9 +323,7 @@ stp_ref(const struct stp *stp_)
 {
     struct stp *stp = CONST_CAST(struct stp *, stp_);
     if (stp) {
-        int orig;
-        atomic_add(&stp->ref_cnt, 1, &orig);
-        ovs_assert(orig > 0);
+        ovs_refcount_ref(&stp->ref_cnt);
     }
     return stp;
 }
@@ -327,19 +332,17 @@ stp_ref(const struct stp *stp_)
 void
 stp_unref(struct stp *stp)
 {
-    int orig;
-
-    if (!stp) {
-        return;
-    }
+    if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) {
+        size_t i;
 
-    atomic_sub(&stp->ref_cnt, 1, &orig);
-    ovs_assert(orig > 0);
-    if (orig == 1) {
         ovs_mutex_lock(&mutex);
         list_remove(&stp->node);
         ovs_mutex_unlock(&mutex);
         free(stp->name);
+
+        for (i = 0; i < STP_MAX_PORTS; i++) {
+            free(stp->ports[i].port_name);
+        }
         free(stp);
     }
 }
@@ -393,7 +396,7 @@ out:
 
 static void
 set_bridge_id(struct stp *stp, stp_identifier new_bridge_id)
-    OVS_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     if (new_bridge_id != stp->bridge_id) {
         bool root;
@@ -666,30 +669,40 @@ stp_state_name(enum stp_state state)
     case STP_BLOCKING:
         return "blocking";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
 /* Returns true if 'state' is one in which packets received on a port should
  * be forwarded, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
 bool
 stp_forward_in_state(enum stp_state state)
 {
-    return (state & (STP_DISABLED | STP_FORWARDING)) != 0;
+    return (state & STP_FORWARDING) != 0;
 }
 
 /* Returns true if 'state' is one in which MAC learning should be done on
  * packets received on a port, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
 bool
 stp_learn_in_state(enum stp_state state)
 {
-    return (state & (STP_DISABLED | STP_LEARNING | STP_FORWARDING)) != 0;
+    return (state & (STP_LEARNING | STP_FORWARDING)) != 0;
+}
+
+/* Returns true if 'state' is one in which bpdus should be forwarded on a
+ * port, false otherwise.
+ *
+ * Returns true if 'state' is STP_DISABLED, since in that case the port does
+ * not generate the bpdu and should just forward it (e.g. patch port on pif
+ * bridge). */
+bool
+stp_should_forward_bpdu(enum stp_state state)
+{
+    return (state &
+            ( STP_DISABLED | STP_LISTENING | STP_LEARNING
+              | STP_FORWARDING)) != 0;
 }
 
 /* Returns the name for the given 'role' (for use in debugging and log
@@ -707,7 +720,7 @@ stp_role_name(enum stp_role role)
     case STP_ROLE_DISABLED:
         return "disabled";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -727,7 +740,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
     }
 
     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++;
         goto out;
     }
@@ -747,7 +760,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
     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++;
             goto out;
@@ -757,7 +770,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
 
     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++;
             goto out;
@@ -789,6 +802,18 @@ stp_port_get_stp(struct stp_port *p)
     return stp;
 }
 
+void
+stp_port_set_name(struct stp_port *p, const char *name)
+{
+    char *old;
+
+    ovs_mutex_lock(&mutex);
+    old = p->port_name;
+    p->port_name = xstrdup(name);
+    free(old);
+    ovs_mutex_unlock(&mutex);
+}
+
 /* Sets the 'aux' member of 'p'.
  *
  * The 'aux' member will be reset to NULL when stp_port_disable() is
@@ -1005,7 +1030,7 @@ stp_port_disable_change_detection(struct stp_port *p)
 }
 \f
 static void
-stp_transmit_config(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     struct stp *stp = p->stp;
     bool root = stp_is_root_bridge(stp);
@@ -1013,6 +1038,8 @@ stp_transmit_config(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
         return;
     }
     if (p->hold_timer.active) {
+        VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu pending",
+                    stp->name, p->port_name);
         p->config_pending = true;
     } else {
         struct stp_config_bpdu config;
@@ -1043,6 +1070,8 @@ stp_transmit_config(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
         if (ntohs(config.message_age) < stp->max_age) {
             p->topology_change_ack = false;
             p->config_pending = false;
+            VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu",
+                        stp->name, p->port_name);
             stp_send_bpdu(p, &config, sizeof config);
             stp_start_timer(&p->hold_timer, 0);
         }
@@ -1052,7 +1081,7 @@ stp_transmit_config(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 static bool
 stp_supersedes_port_info(const struct stp_port *p,
                          const struct stp_config_bpdu *config)
-     OVS_REQ_WRLOCK(mutex)
+     OVS_REQUIRES(mutex)
 {
     if (ntohll(config->root_id) != p->designated_root) {
         return ntohll(config->root_id) < p->designated_root;
@@ -1069,7 +1098,7 @@ stp_supersedes_port_info(const struct stp_port *p,
 static void
 stp_record_config_information(struct stp_port *p,
                               const struct stp_config_bpdu *config)
-     OVS_REQ_WRLOCK(mutex)
+     OVS_REQUIRES(mutex)
 {
     p->designated_root = ntohll(config->root_id);
     p->designated_cost = ntohl(config->root_path_cost);
@@ -1081,7 +1110,7 @@ stp_record_config_information(struct stp_port *p,
 static void
 stp_record_config_timeout_values(struct stp *stp,
                                  const struct stp_config_bpdu  *config)
-     OVS_REQ_WRLOCK(mutex)
+     OVS_REQUIRES(mutex)
 {
     stp->max_age = ntohs(config->max_age);
     stp->hello_time = ntohs(config->hello_time);
@@ -1090,14 +1119,14 @@ stp_record_config_timeout_values(struct stp *stp,
 }
 
 static bool
-stp_is_designated_port(const struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+stp_config_bpdu_generation(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p;
 
@@ -1109,13 +1138,16 @@ stp_config_bpdu_generation(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_transmit_tcn(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p = stp->root_port;
     struct stp_tcn_bpdu tcn_bpdu;
+
     if (!p) {
         return;
     }
+    VLOG_DBG_RL(&stp_rl, "bridge: %s, root port: %s, transmit tcn", stp->name,
+                p->port_name);
     tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID);
     tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION;
     tcn_bpdu.header.bpdu_type = STP_TYPE_TCN;
@@ -1123,15 +1155,16 @@ stp_transmit_tcn(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_configuration_update(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+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_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     int p_cost = p->designated_cost + p->path_cost;
     int root_cost = root->designated_cost + root->path_cost;
@@ -1150,7 +1183,7 @@ stp_supersedes_root(const struct stp_port *root, const struct stp_port *p)
 }
 
 static void
-stp_root_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_root_selection(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p, *root;
 
@@ -1176,7 +1209,7 @@ stp_root_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_designated_port_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_designated_port_selection(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p;
 
@@ -1195,7 +1228,7 @@ stp_designated_port_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_become_designated_port(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_become_designated_port(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     struct stp *stp = p->stp;
     p->designated_root = stp->designated_root;
@@ -1205,7 +1238,7 @@ stp_become_designated_port(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_port_state_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_port_state_selection(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p;
 
@@ -1226,7 +1259,7 @@ stp_port_state_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_make_forwarding(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_make_forwarding(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     if (p->state == STP_BLOCKING) {
         stp_set_port_state(p, STP_LISTENING);
@@ -1235,7 +1268,7 @@ stp_make_forwarding(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_make_blocking(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_make_blocking(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     if (!(p->state & (STP_DISABLED | STP_BLOCKING))) {
         if (p->state & (STP_FORWARDING | STP_LEARNING)) {
@@ -1250,19 +1283,20 @@ stp_make_blocking(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 
 static void
 stp_set_port_state(struct stp_port *p, enum stp_state state)
-    OVS_REQ_WRLOCK(mutex)
+    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) OVS_REQ_WRLOCK(mutex)
+stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -1275,18 +1309,19 @@ stp_topology_change_detection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
     }
     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) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+stp_acknowledge_topology_change(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     p->topology_change_ack = true;
     stp_transmit_config(p);
@@ -1295,7 +1330,7 @@ stp_acknowledge_topology_change(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 static void
 stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
                          const struct stp_config_bpdu *config)
-    OVS_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     if (ntohs(config->message_age) >= ntohs(config->max_age)) {
         VLOG_WARN("%s: received config BPDU with message age (%u) greater "
@@ -1336,7 +1371,7 @@ stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
 
 static void
 stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
-    OVS_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     if (p->state != STP_DISABLED) {
         if (stp_is_designated_port(p)) {
@@ -1347,17 +1382,20 @@ stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
 }
 
 static void
-stp_hello_timer_expiry(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     struct stp *stp = p->stp;
     bool root = stp_is_root_bridge(stp);
+
+    VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, message age timer expired",
+                stp->name, p->port_name);
     stp_become_designated_port(p);
     stp_configuration_update(stp);
     stp_port_state_selection(stp);
@@ -1373,7 +1411,7 @@ stp_message_age_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 }
 
 static bool
-stp_is_designated_for_some_port(const struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_is_designated_for_some_port(const struct stp *stp) OVS_REQUIRES(mutex)
 {
     const struct stp_port *p;
 
@@ -1386,7 +1424,7 @@ stp_is_designated_for_some_port(const struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     if (p->state == STP_LISTENING) {
         stp_set_port_state(p, STP_LEARNING);
@@ -1402,21 +1440,21 @@ stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_tcn_timer_expiry(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+stp_hold_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     if (p->config_pending) {
         stp_transmit_config(p);
@@ -1425,11 +1463,16 @@ stp_hold_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
 
 static void
 stp_initialize_port(struct stp_port *p, enum stp_state state)
-    OVS_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
     stp_become_designated_port(p);
-    stp_set_port_state(p, state);
+
+    if (!p->state && state == STP_DISABLED) {
+        p->state = state; /* Do not trigger state change when initializing. */
+    } else {
+        stp_set_port_state(p, state);
+    }
     p->topology_change_ack = false;
     p->config_pending = false;
     p->change_detection_enabled = true;
@@ -1441,7 +1484,7 @@ stp_initialize_port(struct stp_port *p, enum stp_state state)
 }
 
 static void
-stp_become_root_bridge(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_become_root_bridge(struct stp *stp) OVS_REQUIRES(mutex)
 {
     stp->max_age = stp->bridge_max_age;
     stp->hello_time = stp->bridge_hello_time;
@@ -1453,21 +1496,21 @@ stp_become_root_bridge(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 }
 
 static void
-stp_start_timer(struct stp_timer *timer, int value) OVS_REQ_WRLOCK(mutex)
+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) OVS_REQ_WRLOCK(mutex)
+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_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     if (timer->active) {
         timer->value += elapsed;
@@ -1502,7 +1545,7 @@ clamp(int x, int min, int max)
 }
 
 static void
-stp_update_bridge_timers(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_update_bridge_timers(struct stp *stp) OVS_REQUIRES(mutex)
 {
     int ht, ma, fd;
 
@@ -1523,22 +1566,23 @@ stp_update_bridge_timers(struct stp *stp) OVS_REQ_WRLOCK(mutex)
 
 static void
 stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
-    OVS_REQ_WRLOCK(mutex)
+    OVS_REQUIRES(mutex)
 {
     struct eth_header *eth;
     struct llc_header *llc;
-    struct ofpbuf *pkt;
+    struct dp_packet *pkt;
 
     /* Skeleton. */
-    pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
-    pkt->l2 = eth = ofpbuf_put_zeros(pkt, sizeof *eth);
-    llc = ofpbuf_put_zeros(pkt, sizeof *llc);
-    pkt->l3 = ofpbuf_put(pkt, bpdu, bpdu_size);
+    pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
+    eth = dp_packet_put_zeros(pkt, sizeof *eth);
+    llc = dp_packet_put_zeros(pkt, sizeof *llc);
+    dp_packet_reset_offsets(pkt);
+    dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size));
 
     /* 802.2 header. */
-    memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
+    eth->eth_dst = eth_addr_stp;
     /* p->stp->send_bpdu() must fill in source address. */
-    eth->eth_type = htons(pkt->size - ETH_HEADER_LEN);
+    eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN);
 
     /* LLC header. */
     llc->llc_dsap = STP_LLC_DSAP;
@@ -1552,7 +1596,7 @@ stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
 /* Unixctl. */
 
 static struct stp *
-stp_find(const char *name) OVS_REQ_WRLOCK(mutex)
+stp_find(const char *name) OVS_REQUIRES(mutex)
 {
     struct stp *stp;