testsuite: Add timeout to add_of_br() command.
[cascardo/ovs.git] / lib / rstp-state-machines.c
index a0fe3d9..9768e46 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2014 M3S, Srl - Italy
+ * Copyright (c) 2011-2015 M3S, Srl - Italy
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
  * Authors:
  *         Martino Fornasa <mf@fornasa.it>
  *         Daniele Venturino <daniele.venturino@m3s.it>
+ *         Carlo Andreotti <c.andreotti@m3s.it>
  *
  * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
  * E.g. [17.3], [Table 17-1], etc.
 #include "byte-order.h"
 #include "connectivity.h"
 #include "ofpbuf.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(rstp_sm);
 
@@ -62,6 +64,8 @@ enum bpdu_size {
     RAPID_SPANNING_TREE_BPDU_SIZE = 36
 };
 
+/* Same is a subset of SUPERIOR, so can be used as a boolean when the
+ * distinction is not significant. */
 enum vector_comparison {
     INFERIOR = 0,
     SUPERIOR = 1,
@@ -69,37 +73,66 @@ enum vector_comparison {
 };
 
 static void decrement_timer(uint16_t *);
-static void rstp_send_bpdu(struct rstp_port *, const void *, size_t);
-static int validate_received_bpdu(struct rstp_port *, const void *, size_t);
+static void rstp_send_bpdu(struct rstp_port *, const void *, size_t)
+    OVS_REQUIRES(rstp_mutex);
+static int validate_received_bpdu(struct rstp_port *, const void *, size_t)
+    OVS_REQUIRES(rstp_mutex);
 static ovs_be16 time_encode(uint8_t);
 static uint8_t time_decode(ovs_be16);
 static enum vector_comparison
-compare_rstp_priority_vector(struct rstp_priority_vector *,
-                             struct rstp_priority_vector *);
+compare_rstp_priority_vectors(const struct rstp_priority_vector *,
+                              const struct rstp_priority_vector *);
 static bool rstp_times_equal(struct rstp_times *, struct rstp_times *);
 
 /* Per-Bridge State Machine */
-static int port_role_selection_sm(struct rstp *);
+static int port_role_selection_sm(struct rstp *)
+    OVS_REQUIRES(rstp_mutex);
 /* Per-Port State Machines */
-static int port_receive_sm(struct rstp_port *);
-static int port_protocol_migration_sm(struct rstp_port *);
-static int bridge_detection_sm(struct rstp_port *);
-static int port_transmit_sm(struct rstp_port *);
-static int port_information_sm(struct rstp_port *);
-static int port_role_transition_sm(struct rstp_port *);
-static int port_state_transition_sm(struct rstp_port *);
-static int topology_change_sm(struct rstp_port *);
+static int port_receive_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int port_protocol_migration_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int bridge_detection_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int port_transmit_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int port_information_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int port_role_transition_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int port_state_transition_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
+static int topology_change_sm(struct rstp_port *)
+    OVS_REQUIRES(rstp_mutex);
 /* port_timers_sm() not defined as a state machine */
 
 void
-process_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+process_received_bpdu__(struct rstp_port *p, const void *bpdu_,
+                        size_t bpdu_size)
+    OVS_REQUIRES(rstp_mutex)
 {
-    struct rstp *rstp =  p->rstp;
+    struct rstp *rstp = p->rstp;
+    struct rstp_bpdu *bpdu = (struct rstp_bpdu *)bpdu_;
 
-    if (!p->port_enabled)
+    if (!p->port_enabled) {
         return;
-    if (p->rcvd_bpdu)
+    }
+    if (p->rcvd_bpdu) {
         return;
+    }
+
+    /* [9.2.9 Encoding of Port Role values]
+     * NOTE. If the Unknown value of the Port Role parameter is received, the
+     * state machines will effectively treat the RST BPDU as if it were a
+     * Configuration BPDU.
+     */
+    if (bpdu->bpdu_type == RAPID_SPANNING_TREE_BPDU) {
+        uint8_t role = (bpdu->flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
+
+        if (role == PORT_UNKN) {
+            bpdu->bpdu_type = CONFIGURATION_BPDU;
+        }
+    }
 
     if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) {
         p->rcvd_bpdu = true;
@@ -108,16 +141,18 @@ process_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
         memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu));
 
         rstp->changes = true;
-        move_rstp(rstp);
+        move_rstp__(rstp);
     } else {
-        VLOG_DBG("%s, port %u: Bad BPDU received", p->rstp->name,
+        VLOG_DBG("%s, port %u: Bad STP or RSTP BPDU received", p->rstp->name,
                  p->port_number);
         p->error_count++;
     }
 }
 
+/* Returns 0 on success. */
 static int
 validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* Validation of received BPDU, see [9.3.4]. */
     const struct rstp_bpdu *temp;
@@ -127,18 +162,16 @@ validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
             ntohs(temp->protocol_identifier) != 0) {
         return -1;
     } else {
-        if (temp->bpdu_type == CONFIGURATION_BPDU &&
-            bpdu_size >= CONFIGURATION_BPDU_SIZE &&
-            (time_decode(temp->message_age) <  time_decode(temp->max_age)))
-        {
+        if (temp->bpdu_type == CONFIGURATION_BPDU
+            && bpdu_size >= CONFIGURATION_BPDU_SIZE
+            && (time_decode(temp->message_age) <  time_decode(temp->max_age))) {
             if ((ntohll(temp->designated_bridge_id) !=
-                        p->rstp->bridge_identifier) ||
-                ((ntohll(temp->designated_bridge_id) ==
-                         p->rstp->bridge_identifier) &&
-                (ntohs(temp->designated_port_id) != p->port_id))) {
+                 p->rstp->bridge_identifier)
+                || ((ntohll(temp->designated_bridge_id) ==
+                     p->rstp->bridge_identifier)
+                    && (ntohs(temp->designated_port_id) != p->port_id))) {
                 return 0;
-            }
-            else {
+            } else {
                 return -1;
             }
         } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
@@ -146,46 +179,46 @@ validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
         } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU &&
                    bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) {
             return 0;
-        }
-        else {
+        } else {
             return -1;
         }
     }
 }
 
 /*
- * move_rstp()
- * This method is invoked to move the State Machines. The SMs  move only if the
+ * move_rstp__()
+ * This method is invoked to move the State Machines.  The SMs move only if the
  * boolean 'changes' is true, meaning that something changed and the SMs need
  * to work to process this change.
  * The boolean 'changes' is set every time a SM modifies its state, a BPDU is
- * received, a timer expires or port down event is detected. If a parameter is
+ * received, a timer expires or port down event is detected.  If a parameter is
  * set by management, then 'changes' is set.
  */
 #define MAX_RSTP_ITERATIONS 1000 /* safeguard */
 int
-move_rstp(struct rstp *rstp)
+move_rstp__(struct rstp *rstp)
+    OVS_REQUIRES(rstp_mutex)
 {
-    struct rstp_port *p;
     int num_iterations;
     num_iterations = 0;
 
     while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) {
+        struct rstp_port *p;
+
         VLOG_DBG("%s: move_rstp()", rstp->name);
+
         rstp->changes = false;
         port_role_selection_sm(rstp);
-        if (rstp->ports_count > 0) {
-            LIST_FOR_EACH (p, node, &rstp->ports) {
-                if (p->rstp_state != RSTP_DISABLED) {
-                    port_receive_sm(p);
-                    bridge_detection_sm(p);
-                    port_information_sm(p);
-                    port_role_transition_sm(p);
-                    port_state_transition_sm(p);
-                    topology_change_sm(p);
-                    port_transmit_sm(p);
-                    port_protocol_migration_sm(p);
-                }
+        HMAP_FOR_EACH (p, node, &rstp->ports) {
+            if (p->rstp_state != RSTP_DISABLED) {
+                port_receive_sm(p);
+                bridge_detection_sm(p);
+                port_information_sm(p);
+                port_role_transition_sm(p);
+                port_state_transition_sm(p);
+                topology_change_sm(p);
+                port_transmit_sm(p);
+                port_protocol_migration_sm(p);
             }
         }
         num_iterations++;
@@ -198,26 +231,25 @@ move_rstp(struct rstp *rstp)
     return 0;
 }
 
-void decrease_rstp_port_timers(struct rstp *r)
+void decrease_rstp_port_timers__(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
 
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            decrement_timer(&p->hello_when);
-            decrement_timer(&p->tc_while);
-            decrement_timer(&p->fd_while);
-            decrement_timer(&p->rcvd_info_while);
-            decrement_timer(&p->rr_while);
-            decrement_timer(&p->rb_while);
-            decrement_timer(&p->mdelay_while);
-            decrement_timer(&p->edge_delay_while);
-            decrement_timer(&p->tx_count);
-            p->uptime+=1;
-        }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        decrement_timer(&p->hello_when);
+        decrement_timer(&p->tc_while);
+        decrement_timer(&p->fd_while);
+        decrement_timer(&p->rcvd_info_while);
+        decrement_timer(&p->rr_while);
+        decrement_timer(&p->rb_while);
+        decrement_timer(&p->mdelay_while);
+        decrement_timer(&p->edge_delay_while);
+        decrement_timer(&p->tx_count);
+        p->uptime += 1;
     }
     r->changes = true;
-    move_rstp(r);
+    move_rstp__(r);
 }
 
 static void
@@ -233,129 +265,143 @@ decrement_timer(uint16_t *timer)
 
 static void
 updt_role_disabled_tree(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
 
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            p->selected_role = ROLE_DISABLED;
-        }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        p->selected_role = ROLE_DISABLED;
     }
 }
 
 static void
 clear_reselect_tree(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
 
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            p->reselect = false;
-        }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        p->reselect = false;
     }
 }
 
 void
-updt_roles_tree(struct rstp *r)
+updt_roles_tree__(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
     int vsel;
     struct rstp_priority_vector best_vector, candidate_vector;
+    enum rstp_port_role new_root_old_role = ROLE_DESIGNATED;
+    uint16_t old_root_port_number = 0;
+    uint16_t new_root_port_number = 0;
 
+    old_root_port_number = r->root_port_id & 0x00ff;
+    if (old_root_port_number) {
+        r->old_root_aux = rstp_get_port_aux__(r, old_root_port_number);
+    }
     vsel = -1;
     best_vector = r->bridge_priority;
     /* Letter c1) */
     r->root_times = r->bridge_times;
     /* Letters a) b) c) */
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            uint32_t old_root_path_cost;
-            uint32_t root_path_cost;
-            if (p->info_is != INFO_IS_RECEIVED) {
-                continue;
-            }
-            /* [17.6] */
-            candidate_vector = p->port_priority;
-            candidate_vector.bridge_port_id = p->port_id;
-            old_root_path_cost = candidate_vector.root_path_cost;
-            root_path_cost = old_root_path_cost + p->port_path_cost;
-            candidate_vector.root_path_cost = root_path_cost;
-
-            if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) ==
-                (r->bridge_priority.designated_bridge_id & 0xffffffffffffULL))
-            {
-                break;
-            }
-            if (compare_rstp_priority_vector(&candidate_vector, &best_vector)
-                == SUPERIOR) {
-                best_vector = candidate_vector;
-                r->root_times = p->port_times;
-                r->root_times.message_age++;
-                vsel = p->port_number;
-            }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        uint32_t old_root_path_cost;
+        uint32_t root_path_cost;
+
+        if (p->info_is != INFO_IS_RECEIVED) {
+            continue;
+        }
+        /* [17.6] */
+        candidate_vector = p->port_priority;
+        candidate_vector.bridge_port_id = p->port_id;
+        old_root_path_cost = candidate_vector.root_path_cost;
+        root_path_cost = old_root_path_cost + p->port_path_cost;
+        candidate_vector.root_path_cost = root_path_cost;
+
+        if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) ==
+            (r->bridge_priority.designated_bridge_id & 0xffffffffffffULL)) {
+            continue;
+        }
+        if (compare_rstp_priority_vectors(&candidate_vector,
+                                          &best_vector) == SUPERIOR) {
+            best_vector = candidate_vector;
+            r->root_times = p->port_times;
+            r->root_times.message_age++;
+            vsel = p->port_number;
+            new_root_old_role = p->role;
         }
     }
     r->root_priority = best_vector;
     r->root_port_id = best_vector.bridge_port_id;
     VLOG_DBG("%s: new Root is "RSTP_ID_FMT, r->name,
              RSTP_ID_ARGS(r->root_priority.root_bridge_id));
+    new_root_port_number = r->root_port_id & 0x00ff;
+    if (new_root_port_number) {
+        r->new_root_aux = rstp_get_port_aux__(r, new_root_port_number);
+    }
+    /* Shift learned MAC addresses from an old Root Port to an existing
+     * Alternate Port. */
+    if (!r->root_changed
+        && new_root_old_role == ROLE_ALTERNATE
+        && new_root_port_number
+        && old_root_port_number
+        && new_root_port_number != old_root_port_number) {
+        r->root_changed = true;
+    }
     /* Letters d) e) */
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            p->designated_priority_vector.root_bridge_id =
-                r->root_priority.root_bridge_id;
-            p->designated_priority_vector.root_path_cost =
-                r->root_priority.root_path_cost;
-            p->designated_priority_vector.designated_bridge_id =
-                r->bridge_identifier;
-            p->designated_priority_vector.designated_port_id =
-                p->port_id;
-            p->designated_times = r->root_times;
-            p->designated_times.hello_time = r->bridge_times.hello_time;
-        }
-    }
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            switch (p->info_is) {
-            case INFO_IS_DISABLED:
-                p->selected_role = ROLE_DISABLED;
-                break;
-            case INFO_IS_AGED:
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        p->designated_priority_vector.root_bridge_id =
+            r->root_priority.root_bridge_id;
+        p->designated_priority_vector.root_path_cost =
+            r->root_priority.root_path_cost;
+        p->designated_priority_vector.designated_bridge_id =
+            r->bridge_identifier;
+        p->designated_priority_vector.designated_port_id =
+            p->port_id;
+        p->designated_times = r->root_times;
+        p->designated_times.hello_time = r->bridge_times.hello_time;
+    }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        switch (p->info_is) {
+        case INFO_IS_DISABLED:
+            p->selected_role = ROLE_DISABLED;
+            break;
+        case INFO_IS_AGED:
+            p->updt_info = true;
+            p->selected_role = ROLE_DESIGNATED;
+            break;
+        case INFO_IS_MINE:
+            p->selected_role = ROLE_DESIGNATED;
+            if (compare_rstp_priority_vectors(
+                    &p->port_priority, &p->designated_priority_vector) != SAME
+                || !rstp_times_equal(&p->designated_times, &r->root_times)) {
                 p->updt_info = true;
-                p->selected_role = ROLE_DESIGNATED;
-                break;
-            case INFO_IS_MINE:
-                p->selected_role = ROLE_DESIGNATED;
-                if ((compare_rstp_priority_vector(&p->port_priority,
-                     &p->designated_priority_vector) != SAME) ||
-                     !rstp_times_equal(&p->designated_times, &r->root_times)) {
-                    p->updt_info = true;
-                }
-                break;
-            case INFO_IS_RECEIVED:
-                if (vsel == p->port_number) { /* Letter i) */
-                    p->selected_role = ROLE_ROOT;
+            }
+            break;
+        case INFO_IS_RECEIVED:
+            if (vsel == p->port_number) { /* Letter i) */
+                p->selected_role = ROLE_ROOT;
+                p->updt_info = false;
+            } else if (compare_rstp_priority_vectors(
+                           &p->designated_priority_vector,
+                           &p->port_priority) == INFERIOR) {
+                if (p->port_priority.designated_bridge_id !=
+                    r->bridge_identifier) {
+                    p->selected_role = ROLE_ALTERNATE;
                     p->updt_info = false;
-                } else if (compare_rstp_priority_vector(
-                            &p->designated_priority_vector, &p->port_priority)
-                            == INFERIOR) {
-                    if (p->port_priority.designated_bridge_id !=
-                            r->bridge_identifier) {
-                        p->selected_role = ROLE_ALTERNATE;
-                        p->updt_info = false;
-                    } else {
-                        p->selected_role = ROLE_BACKUP;
-                        p->updt_info = false;
-                    }
                 } else {
-                    p->selected_role = ROLE_DESIGNATED;
-                    p->updt_info = true;
+                    p->selected_role = ROLE_BACKUP;
+                    p->updt_info = false;
                 }
-                break;
-            default:
-                OVS_NOT_REACHED();
-                /* no break */
+            } else {
+                p->selected_role = ROLE_DESIGNATED;
+                p->updt_info = true;
             }
+            break;
+        default:
+            OVS_NOT_REACHED();
+            /* no break */
         }
     }
     seq_change(connectivity_seq_get());
@@ -363,23 +409,23 @@ updt_roles_tree(struct rstp *r)
 
 static void
 set_selected_tree(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
 
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            if (p->reselect) {
-                return;
-            }
-        }
-        LIST_FOR_EACH (p, node, &r->ports) {
-            p->selected = true;
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        if (p->reselect) {
+            return;
         }
     }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        p->selected = true;
+    }
 }
 
 static int
 port_role_selection_sm(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_role_selection_state_machine old_state;
     struct rstp_port *p;
@@ -388,9 +434,10 @@ port_role_selection_sm(struct rstp *r)
 
     switch (r->port_role_selection_sm_state) {
     case PORT_ROLE_SELECTION_SM_INIT:
-        if (r->begin)
+        if (r->begin) {
             r->port_role_selection_sm_state =
                 PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC;
+        }
         break;
     case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC:
         updt_role_disabled_tree(r);
@@ -402,19 +449,17 @@ port_role_selection_sm(struct rstp *r)
         break;
     case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC:
         clear_reselect_tree(r);
-        updt_roles_tree(r);
+        updt_roles_tree__(r);
         set_selected_tree(r);
         r->port_role_selection_sm_state =
             PORT_ROLE_SELECTION_SM_ROLE_SELECTION;
         /* no break */
     case PORT_ROLE_SELECTION_SM_ROLE_SELECTION:
-        if (r->ports_count > 0) {
-            LIST_FOR_EACH (p, node, &r->ports) {
-                if (p->reselect) {
-                    r->port_role_selection_sm_state =
-                        PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
-                    break;
-                }
+        HMAP_FOR_EACH (p, node, &r->ports) {
+            if (p->reselect) {
+                r->port_role_selection_sm_state =
+                    PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
+                break;
             }
         }
         break;
@@ -436,6 +481,7 @@ port_role_selection_sm(struct rstp *r)
 
 static void
 updt_bpdu_version(struct rstp_port *p)  /* [17.21.22] */
+    OVS_REQUIRES(rstp_mutex)
 {
     switch (p->received_bpdu_buffer.bpdu_type) {
     case CONFIGURATION_BPDU:
@@ -455,6 +501,7 @@ updt_bpdu_version(struct rstp_port *p)  /* [17.21.22] */
 
 static int
 port_receive_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_receive_state_machine old_state;
     struct rstp *r;
@@ -476,7 +523,11 @@ port_receive_sm(struct rstp_port *p)
         p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD;
         /* no break */
     case PORT_RECEIVE_SM_DISCARD:
-        if (p->rcvd_bpdu && p->port_enabled) {
+        if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time))
+            && !p->port_enabled) {
+            /* Global transition. */
+            p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC;
+        } else if (p->rcvd_bpdu && p->port_enabled) {
             p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
         }
         break;
@@ -488,7 +539,11 @@ port_receive_sm(struct rstp_port *p)
         p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE;
         /* no break */
     case PORT_RECEIVE_SM_RECEIVE:
-        if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) {
+        if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time))
+            && !p->port_enabled) {
+            /* Global transition. */
+            p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC;
+        } else if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) {
             p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
         }
         break;
@@ -507,6 +562,7 @@ port_receive_sm(struct rstp_port *p)
 /* [17.24 - Port Protocol Migration state machine] */
 static int
 port_protocol_migration_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_protocol_migration_state_machine old_state;
     struct rstp *r;
@@ -516,8 +572,8 @@ port_protocol_migration_sm(struct rstp_port *p)
 
     switch (p->port_protocol_migration_sm_state) {
     case PORT_PROTOCOL_MIGRATION_SM_INIT:
-    p->port_protocol_migration_sm_state =
-        PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
+        p->port_protocol_migration_sm_state =
+            PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
         /* no break */
     case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC:
         p->mcheck = false;
@@ -528,16 +584,15 @@ port_protocol_migration_sm(struct rstp_port *p)
         /* no break */
     case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP:
         if (p->mdelay_while == 0) {
-        p->port_protocol_migration_sm_state =
-            PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
-        }
-        else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) {
+            p->port_protocol_migration_sm_state =
+                PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
+        } else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) {
             p->port_protocol_migration_sm_state =
                 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
         }
         break;
     case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC:
-    p->send_rstp = false;
+        p->send_rstp = false;
         p->mdelay_while = r->migrate_time;
         p->port_protocol_migration_sm_state =
             PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP;
@@ -556,11 +611,10 @@ port_protocol_migration_sm(struct rstp_port *p)
         /* no break */
     case PORT_PROTOCOL_MIGRATION_SM_SENSING:
         if (!p->port_enabled || p->mcheck || ((r->rstp_version) &&
-                    !p->send_rstp && p->rcvd_rstp)) {
+                                              !p->send_rstp && p->rcvd_rstp)) {
             p->port_protocol_migration_sm_state =
                 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
-        }
-        else if (p->send_rstp && p->rcvd_stp) {
+        } else if (p->send_rstp && p->rcvd_stp) {
             p->port_protocol_migration_sm_state =
                 PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC;
         }
@@ -582,6 +636,7 @@ port_protocol_migration_sm(struct rstp_port *p)
 /* [17.25 - Bridge Detection state machine] */
 static int
 bridge_detection_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum bridge_detection_state_machine old_state;
     struct rstp *r;
@@ -611,8 +666,9 @@ bridge_detection_sm(struct rstp_port *p)
         p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE;
         /* no break */
     case BRIDGE_DETECTION_SM_NOT_EDGE:
-        if ((!p->port_enabled && p->admin_edge) || ((p->edge_delay_while == 0)
-                    && p->auto_edge && p->send_rstp && p->proposing)) {
+        if ((!p->port_enabled && p->admin_edge)
+            || ((p->edge_delay_while == 0) && p->auto_edge && p->send_rstp
+                && p->proposing)) {
             p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
         }
         break;
@@ -631,38 +687,40 @@ bridge_detection_sm(struct rstp_port *p)
 /* [17.26 - Port Transmit state machine] */
 static void
 rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+    OVS_REQUIRES(rstp_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);
-    eth = ofpbuf_put_zeros(pkt, sizeof *eth);
-    llc = ofpbuf_put_zeros(pkt, sizeof *llc);
-    ofpbuf_set_frame(pkt, eth);
-    ofpbuf_set_l3(pkt, 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->rstp->send_bpdu() must fill in source address. */
-    eth->eth_type = htons(ofpbuf_size(pkt) - ETH_HEADER_LEN);
+    eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN);
 
     /* LLC header. */
     llc->llc_dsap = STP_LLC_DSAP;
     llc->llc_ssap = STP_LLC_SSAP;
     llc->llc_cntl = STP_LLC_CNTL;
-    p->rstp->send_bpdu(pkt, rstp_port_number(p), p->rstp->aux);
+    p->rstp->send_bpdu(pkt, p->aux, p->rstp->aux);
 }
 
 static void
 record_agreement(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
 
     r = p->rstp;
     if (r->rstp_version && p->oper_point_to_point_mac &&
-            ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) {
+        ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) {
         p->agreed = true;
         p->proposing = false;
     } else {
@@ -672,13 +730,14 @@ record_agreement(struct rstp_port *p)
 
 static void
 set_tc_flags(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology
      * Change Acknowledgment flags, respectively, are set in a ConfigBPDU or
      * RST BPDU.
      */
     if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU ||
-            p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) {
+        p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) {
         if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) {
             p->rcvd_tc = true;
         }
@@ -687,34 +746,41 @@ set_tc_flags(struct rstp_port *p)
         }
     }
     /* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */
-    if (p->received_bpdu_buffer.bpdu_type ==
-            TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
+    if (p->received_bpdu_buffer.bpdu_type
+        == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
         p->rcvd_tcn = true;
     }
 }
 
 static void
 record_dispute(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) {
-        p->agreed = true;
-        p->proposing = false;
+        /* 802.1D-2004 says to set the agreed flag and to clear the proposing
+         * flag. 802.1q-2008 instead says to set the disputed variable and to
+         * clear the agreed variable. */
+        p->disputed = true;
+        p->agreed = false;
     }
 }
 
 static void
 record_proposal(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_flag role =
         ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
-    if ((role == PORT_DES) &&
-            ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
+
+    if ((role == PORT_DES)
+        && ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
         p->proposed = true;
     }
 }
 
 static void
 record_priority(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id;
     p->port_priority.root_path_cost = p->msg_priority.root_path_cost;
@@ -725,6 +791,7 @@ record_priority(struct rstp_port *p)
 
 static void
 record_times(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     p->port_times = p->msg_times;
     if (p->msg_times.hello_time == 0) {
@@ -734,6 +801,7 @@ record_times(struct rstp_port *p)
 
 static void
 updt_rcvd_info_while(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* [17.21.23]
      * The value assigned to rcvdInfoWhile is the three times the Hello Time,
@@ -742,9 +810,9 @@ updt_rcvd_info_while(struct rstp_port *p)
      */
     if (p->port_times.message_age < p->port_times.max_age) {
         p->rcvd_info_while = p->port_times.hello_time * 3;
-     } else {
+    } else {
         p->rcvd_info_while = 0;
-     }
+    }
 }
 
 /* Times are internally held in seconds, while the protocol uses 1/256 seconds.
@@ -766,6 +834,7 @@ time_decode(ovs_be16 encoded)
 /* [17.21.19] */
 static void
 tx_config(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_bpdu bpdu;
 
@@ -795,6 +864,7 @@ tx_config(struct rstp_port *p)
 /* [17.21.20] */
 static void
 tx_rstp(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_bpdu bpdu;
 
@@ -812,6 +882,7 @@ tx_rstp(struct rstp_port *p)
     bpdu.hello_time = time_encode(p->designated_times.hello_time);
     bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
     bpdu.flags = 0;
+
     switch (p->role) {
     case ROLE_ROOT:
         bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT;
@@ -851,6 +922,7 @@ tx_rstp(struct rstp_port *p)
 /* [17.21.21] */
 static void
 tx_tcn(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_bpdu bpdu;
 
@@ -864,6 +936,7 @@ tx_tcn(struct rstp_port *p)
 
 static int
 port_transmit_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_transmit_state_machine old_state;
     struct rstp *r;
@@ -902,24 +975,20 @@ port_transmit_sm(struct rstp_port *p)
             VLOG_DBG("%s, port %u: port_transmit_sm ROLE == DISABLED.",
                      p->rstp->name, p->port_number);
             break;
-        }
-        else if (p->send_rstp && p->new_info &&
-                (p->tx_count < r->transmit_hold_count) && (p->hello_when != 0)
-                && p->selected && !p->updt_info) {
+        } else if (p->send_rstp && p->new_info
+                   && p->tx_count < r->transmit_hold_count
+                   && p->hello_when != 0 && p->selected && !p->updt_info) {
             p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC;
-        }
-        else if (p->hello_when == 0 && p->selected && !p->updt_info) {
+        } else if (p->hello_when == 0 && p->selected && !p->updt_info) {
             p->port_transmit_sm_state =
                 PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC;
-        }
-        else if (!p->send_rstp && p->new_info && (p->role == ROLE_ROOT) &&
-                (p->tx_count < r->transmit_hold_count) && (p->hello_when != 0)
-                && p->selected && !p->updt_info) {
+        } else if (!p->send_rstp && p->new_info && p->role == ROLE_ROOT
+                   && p->tx_count < r->transmit_hold_count
+                   && p->hello_when != 0 && p->selected && !p->updt_info) {
             p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC;
-        }
-        else if (!p->send_rstp && p->new_info && (p->role == ROLE_DESIGNATED)
-                && (p->tx_count < r->transmit_hold_count) &&
-                (p->hello_when != 0) && p->selected && !p->updt_info) {
+        } else if (!p->send_rstp && p->new_info && p->role == ROLE_DESIGNATED
+                   && p->tx_count < r->transmit_hold_count
+                   && p->hello_when != 0 && p->selected && !p->updt_info) {
             p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC;
         }
         break;
@@ -970,6 +1039,7 @@ port_transmit_sm(struct rstp_port *p)
 
 static int
 rcv_info(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum vector_comparison cp;
     bool ct;
@@ -991,10 +1061,23 @@ rcv_info(struct rstp_port *p)
     p->msg_times.message_age =
         time_decode(p->received_bpdu_buffer.message_age);
 
-    cp = compare_rstp_priority_vector(&p->msg_priority, &p->port_priority);
+    cp = compare_rstp_priority_vectors(&p->msg_priority, &p->port_priority);
     ct = rstp_times_equal(&p->port_times, &p->msg_times);
-    role =
-        ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
+    /* Configuration BPDU conveys a Designated Port Role. */
+    if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) {
+        role = PORT_DES;
+    } else {
+        role =
+            (p->received_bpdu_buffer.flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
+    }
+
+    /* 802.1D-2004 does not report this behaviour.
+     * 802.1Q-2008 says set rcvdTcn. */
+    if (p->received_bpdu_buffer.bpdu_type ==
+            TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
+        p->rcvd_tcn = true;
+        return OTHER_INFO;
+    }
 
     /* Returns SuperiorDesignatedInfo if:
      * a) The received message conveys a Designated Port Role, and
@@ -1006,72 +1089,65 @@ rcv_info(struct rstp_port *p)
      *     17.19.22).
      * NOTE: Configuration BPDU explicitly conveys a Designated Port Role.
      */
-    if ((role == PORT_DES ||
-         p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) &&
-            ((cp == SUPERIOR) || ((cp == SAME) && ct == false))) {
+    if (role == PORT_DES && (cp == SUPERIOR || (cp == SAME && ct == false))) {
         return SUPERIOR_DESIGNATED_INFO;
-    }
 
-    /* Returns RepeatedDesignatedInfo if:
-     * b) The received message conveys Designated Port Role, and a message
-     *     priority vector and timer parameters that are the same as the Port's
-     *     port priority vector or timer values.
-     */
-    else if ((role == PORT_DES) && (cp == SAME) && (ct == true)) {
+        /* Returns RepeatedDesignatedInfo if:
+         * b) The received message conveys Designated Port Role, and a message
+         *     priority vector and timer parameters that are the same as the
+         *     Port's port priority vector or timer values. */
+    } else if (role == PORT_DES && cp == SAME && ct == true) {
         return REPEATED_DESIGNATED_INFO;
-    }
 
-    /* Returns InferiorDesignatedInfo if:
-     * c) The received message conveys a Designated Port Role, and a message
-     *    priority vector that is worse than the Port's port priority vector.
-     */
-    else if ((role == PORT_DES) && (cp == INFERIOR)) {
+        /* Returns InferiorDesignatedInfo if:
+         * c) The received message conveys a Designated Port Role, and a
+         *    message priority vector that is worse than the Port's port
+         *    priority vector. */
+    } else if (role == PORT_DES && cp == INFERIOR) {
         return INFERIOR_DESIGNATED_INFO;
-    }
 
-    /* Returns InferiorRootAlternateInfo if:
-     * d) The received message conveys a Root Port, Alternate Port, or Backup
-     *    Port Role and a message priority that is the same as or worse than
-     *    the port priority vector.
-     */
-    else if ((role == PORT_ROOT || role == PORT_ALT_BACK) &&
-            (cp == INFERIOR || cp == SAME)) {
+        /* Returns InferiorRootAlternateInfo if:
+         * d) The received message conveys a Root Port, Alternate Port, or
+         *    Backup Port Role and a message priority that is the same as or
+         *    worse than the port priority vector. */
+    } else if ((role == PORT_ROOT || role == PORT_ALT_BACK) &&
+               (cp == INFERIOR || cp == SAME)) {
         return INFERIOR_ROOT_ALTERNATE_INFO;
-    }
 
-    /* Otherwise, returns OtherInfo. */
-    else {
+        /* Otherwise, returns OtherInfo. */
+    else {
         return OTHER_INFO;
     }
 }
 
 static int
 better_or_same_info(struct rstp_port *p, int new_info_is)
+    OVS_REQUIRES(rstp_mutex)
 {
-    /* >= SUPERIOR means that the vector is better or the same. */
-    return ((new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED &&
-                compare_rstp_priority_vector(&p->msg_priority,
-                    &p->port_priority) >= SUPERIOR) ||
-            (new_info_is == MINE && p->info_is == INFO_IS_MINE &&
-             compare_rstp_priority_vector(&p->designated_priority_vector,
-                 &p->port_priority) >= SUPERIOR));
+    return
+        (new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED
+         && compare_rstp_priority_vectors(&p->msg_priority,
+                                          &p->port_priority))
+        || (new_info_is == MINE && p->info_is == INFO_IS_MINE
+            && compare_rstp_priority_vectors(&p->designated_priority_vector,
+                                             &p->port_priority));
 }
 
 static int
 port_information_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_information_state_machine old_state;
     struct rstp *r;
+    struct rstp_port *p1;
 
     old_state = p->port_information_sm_state;
     r = p->rstp;
 
-    if (!p->port_enabled && (p->info_is != INFO_IS_DISABLED)) {
-        p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
-    }
     switch (p->port_information_sm_state) {
     case PORT_INFORMATION_SM_INIT:
-        if (r->begin) {
+        if (r->begin
+            || (!p->port_enabled && p->info_is != INFO_IS_DISABLED)) {
             p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
         }
         break;
@@ -1085,7 +1161,10 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED;
         /* no break */
     case PORT_INFORMATION_SM_DISABLED:
-        if (p->port_enabled) {
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else if (p->port_enabled) {
             p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
         } else if (p->rcvd_msg) {
             p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
@@ -1098,7 +1177,10 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_AGED;
         /* no break */
     case PORT_INFORMATION_SM_AGED:
-        if (p->selected && p->updt_info) {
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else if (p->selected && p->updt_info) {
             p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
         }
         break;
@@ -1122,13 +1204,21 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE;
         /* no break */
     case PORT_INFORMATION_SM_UPDATE:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     case PORT_INFORMATION_SM_CURRENT_EXEC:
         p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT;
         /* no break */
     case PORT_INFORMATION_SM_CURRENT:
-        if (p->rcvd_msg && !p->updt_info) {
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else if (p->rcvd_msg && !p->updt_info) {
             p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC;
         } else if (p->selected && p->updt_info) {
             p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
@@ -1143,29 +1233,47 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE;
         /* no break */
     case PORT_INFORMATION_SM_RECEIVE:
-        switch (p->rcvd_info) {
-        case SUPERIOR_DESIGNATED_INFO:
-            p->port_information_sm_state =
-                PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC;
-            break;
-        case REPEATED_DESIGNATED_INFO:
-            p->port_information_sm_state =
-                PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC;
-            break;
-        case INFERIOR_DESIGNATED_INFO:
-            p->port_information_sm_state =
-                PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC;
-            break;
-        case INFERIOR_ROOT_ALTERNATE_INFO:
-            p->port_information_sm_state =
-                PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC;
-            break;
-        case OTHER_INFO:
-            p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC;
-            break;
-        default:
-            OVS_NOT_REACHED();
-            /* no break */
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            switch (p->rcvd_info) {
+            case SUPERIOR_DESIGNATED_INFO:
+                /* 802.1q-2008 has a checkBPDUConsistency() function, called on
+                 * a BPDU reception.  checkBPDUConsistency() clears the agreed
+                 * variable if the received message priority vector is superior
+                 * to the port priority vector, the BPDU is an ST BPDU or an
+                 * RST BPDU, its port role is Designated and its Learning flag
+                 * is set. */
+                if (p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) {
+                    HMAP_FOR_EACH (p1, node, &r->ports) {
+                        if (p1->port_number != p->port_number) {
+                            p1->agreed = false;
+                        }
+                    }
+                }
+                p->port_information_sm_state =
+                    PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC;
+                break;
+            case REPEATED_DESIGNATED_INFO:
+                p->port_information_sm_state =
+                    PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC;
+                break;
+            case INFERIOR_DESIGNATED_INFO:
+                p->port_information_sm_state =
+                    PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC;
+                break;
+            case INFERIOR_ROOT_ALTERNATE_INFO:
+                p->port_information_sm_state =
+                    PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC;
+                break;
+            case OTHER_INFO:
+                p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC;
+                break;
+            default:
+                OVS_NOT_REACHED();
+                /* no break */
+            }
         }
         break;
     case PORT_INFORMATION_SM_OTHER_EXEC:
@@ -1173,7 +1281,12 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_OTHER;
         /* no break */
     case PORT_INFORMATION_SM_OTHER:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC:
         record_agreement(p);
@@ -1182,7 +1295,12 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED;
         /* no break */
     case PORT_INFORMATION_SM_NOT_DESIGNATED:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC:
         record_dispute(p);
@@ -1190,17 +1308,30 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED;
         /* no break */
     case PORT_INFORMATION_SM_INFERIOR_DESIGNATED:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC:
         record_proposal(p);
         set_tc_flags(p);
+        /* This record_agreement() is missing in 802.1D-2004, but it's present
+         * in 802.1q-2008. */
+        record_agreement(p);
         updt_rcvd_info_while(p);
         p->rcvd_msg = false;
         p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED;
         /* no break */
     case PORT_INFORMATION_SM_REPEATED_DESIGNATED:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC:
         p->agreed = p->proposing = false;
@@ -1208,6 +1339,10 @@ port_information_sm(struct rstp_port *p)
         set_tc_flags(p);
         /* RECEIVED is not specified in Standard 802.1D-2004. */
         p->agree = p->agree && better_or_same_info(p, RECEIVED);
+        /* This record_agreement() and the synced assignment are  missing in
+         * 802.1D-2004, but they're present in 802.1q-2008. */
+        record_agreement(p);
+        p->synced = p->synced && p->agreed;
         record_priority(p);
         record_times(p);
         updt_rcvd_info_while(p);
@@ -1218,7 +1353,12 @@ port_information_sm(struct rstp_port *p)
         p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED;
         /* no break */
     case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED:
-        p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
+            /* Global transition. */
+            p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+        } else {
+            p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+        }
         break;
     default:
         OVS_NOT_REACHED();
@@ -1236,46 +1376,47 @@ port_information_sm(struct rstp_port *p)
 
 static void
 set_re_root_tree(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
     struct rstp_port *p1;
 
     r = p->rstp;
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p1, node, &r->ports) {
-            p1->re_root = true;
-        }
+    HMAP_FOR_EACH (p1, node, &r->ports) {
+        p1->re_root = true;
     }
 }
 
 static void
 set_sync_tree(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
     struct rstp_port *p1;
 
     r = p->rstp;
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p1, node, &r->ports) {
-            p1->sync = true;
-        }
+    HMAP_FOR_EACH (p1, node, &r->ports) {
+        p1->sync = true;
     }
 }
 
 static int
 hello_time(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     return p->designated_times.hello_time;
 }
 
 static int
 fwd_delay(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     return p->designated_times.forward_delay;
 }
 
 static int
 forward_delay(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     if (p->send_rstp) {
         return hello_time(p);
@@ -1286,6 +1427,7 @@ forward_delay(struct rstp_port *p)
 
 static int
 edge_delay(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
 
@@ -1299,9 +1441,10 @@ edge_delay(struct rstp_port *p)
 
 static int
 check_selected_role_change(struct rstp_port *p, int current_role_state)
+    OVS_REQUIRES(rstp_mutex)
 {
-    if (p->selected && !p->updt_info && (p->role != p->selected_role) &&
-            (p->selected_role != current_role_state)) {
+    if (p->selected && !p->updt_info && p->role != p->selected_role
+        && p->selected_role != current_role_state) {
         VLOG_DBG("%s, port %u: case: current = %s role =  %s selected =  %d",
                  p->rstp->name, p->port_number,
                  rstp_port_role_name(current_role_state),
@@ -1334,16 +1477,15 @@ check_selected_role_change(struct rstp_port *p, int current_role_state)
 
 static int
 re_rooted(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
     struct rstp_port *p1;
 
     r = p->rstp;
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p1, node, &r->ports) {
-            if ((p1 != p) && (p1->rr_while != 0)) {
-                return false;
-            }
+    HMAP_FOR_EACH (p1, node, &r->ports) {
+        if ((p1 != p) && (p1->rr_while != 0)) {
+            return false;
         }
     }
     return true;
@@ -1351,15 +1493,14 @@ re_rooted(struct rstp_port *p)
 
 static int
 all_synced(struct rstp *r)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp_port *p;
 
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p, node, &r->ports) {
-            if (!(p->selected && p->role == p->selected_role &&
-                        (p->role == ROLE_ROOT || p->synced == true))) {
-                return false;
-            }
+    HMAP_FOR_EACH (p, node, &r->ports) {
+        if (!(p->selected && p->role == p->selected_role &&
+              (p->role == ROLE_ROOT || p->synced == true))) {
+            return false;
         }
     }
     return true;
@@ -1367,6 +1508,7 @@ all_synced(struct rstp *r)
 
 static int
 port_role_transition_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_role_transition_state_machine old_state;
     struct rstp *r;
@@ -1402,10 +1544,9 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_DISABLE_PORT:
         if (check_selected_role_change(p, ROLE_DISABLED)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info && !p->learning &&
-                !p->forwarding) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info && !p->learning
+                   && !p->forwarding) {
             p->port_role_transition_sm_state =
                 PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
         }
@@ -1420,11 +1561,10 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_DISABLED_PORT:
         if (check_selected_role_change(p, ROLE_DISABLED)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info &&
-            ((p->fd_while != p->designated_times.max_age) || p->sync ||
-                 p->re_root || !p->synced)) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info
+                   && (p->fd_while != p->designated_times.max_age || p->sync
+                       || p->re_root || !p->synced)) {
             p->port_role_transition_sm_state =
                 PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
         }
@@ -1436,84 +1576,101 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_ROOT_PORT:
         if (check_selected_role_change(p, ROLE_ROOT)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info) {
             if (p->rr_while != p->designated_times.forward_delay) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
                 break;
-            }
-            else if (p->re_root && p->forward) {
+            } else if (p->re_root && p->forward) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_REROOTED_EXEC;
                 break;
-            }
-            else if (((p->fd_while == 0) || ((re_rooted(p) &&
-                      (p->rb_while == 0)) &&
-                      (r->rstp_version))) && !p->learn) {
+            } else if ((p->fd_while == 0
+                        || ((re_rooted(p) && p->rb_while == 0)
+                            && r->rstp_version)) && !p->learn) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC;
                 break;
-            }
-            else if (((p->fd_while == 0) || ((re_rooted(p) &&
-                      (p->rb_while == 0)) &&
-                      (r->rstp_version))) && p->learn && !p->forward) {
+            } else if ((p->fd_while == 0
+                        || ((re_rooted(p) && p->rb_while == 0)
+                            && r->rstp_version)) && p->learn && !p->forward) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC;
                 break;
-            }
-            else if (p->proposed && !p->agree) {
+            } else if (p->proposed && !p->agree) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC;
                 break;
-            }
-            else if ((all_synced(r) && !p->agree) ||
-                     (p->proposed && p->agree)) {
+            } else if ((all_synced(r) && !p->agree) ||
+                       (p->proposed && p->agree)) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC;
                 break;
-            }
-            else if (!p->forward && !p->re_root) {
+            } else if (!p->forward && !p->re_root) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_REROOT_EXEC;
                 break;
             }
         }
-    break;
+        break;
     case PORT_ROLE_TRANSITION_SM_REROOT_EXEC:
-        set_re_root_tree(p);
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            set_re_root_tree(p);
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC:
-        p->proposed = p->sync = false;
-        p->agree = p->new_info = true;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            p->proposed = p->sync = false;
+            p->agree = p->new_info = true;
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC:
         set_sync_tree(p);
         p->proposed = false;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC:
         p->fd_while = 0;
         p->forward = true;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC:
         p->fd_while = forward_delay(p);
         p->learn = true;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC:
         p->re_root = false;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ROOT)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC:
         p->role = ROLE_DESIGNATED;
@@ -1522,39 +1679,34 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT:
         if (check_selected_role_change(p, ROLE_DESIGNATED)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info) {
-            if (((p->sync && !p->synced) || (p->re_root &&
-                 (p->rr_while != 0)) || p->disputed) &&
-                !p->oper_edge && (p->learn || p->forward)) {
-            p->port_role_transition_sm_state =
-                PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC;
-            }
-            else if (((p->fd_while == 0)|| p->agreed || p->oper_edge) &&
-                ((p->rr_while == 0) || !p->re_root) &&
-                    !p->sync && !p->learn) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info) {
+            if (((p->sync && !p->synced)
+                 || (p->re_root && p->rr_while != 0) || p->disputed)
+                && !p->oper_edge && (p->learn || p->forward)) {
+                p->port_role_transition_sm_state =
+                    PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC;
+            } else if ((p->fd_while == 0 || p->agreed || p->oper_edge)
+                       && (p->rr_while == 0 || !p->re_root)
+                       && !p->sync && !p->learn) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC;
-            }
-            else if (((p->fd_while == 0) || p->agreed || p->oper_edge) &&
-                ((p->rr_while == 0) || !p->re_root) &&
-                    !p->sync && (p->learn && !p->forward)) {
+            } else if ((p->fd_while == 0 || p->agreed || p->oper_edge)
+                       && (p->rr_while == 0 || !p->re_root)
+                       && !p->sync && (p->learn && !p->forward)) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC;
-            }
-            else if (!p->forward && !p->agreed && !p->proposing &&
-                     !p->oper_edge) {
+            } else if (!p->forward && !p->agreed && !p->proposing &&
+                       !p->oper_edge) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC;
-            }
-            else if ((!p->learning && !p->forwarding && !p->synced) ||
-                (p->agreed && !p->synced) || (p->oper_edge && !p->synced) ||
-                    (p->sync && p->synced)) {
+            } else if ((!p->learning && !p->forwarding && !p->synced)
+                       || (p->agreed && !p->synced)
+                       || (p->oper_edge && !p->synced)
+                       || (p->sync && p->synced)) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC;
-            }
-            else if ((p->rr_while == 0) && p->re_root) {
+            } else if (p->rr_while == 0 && p->re_root) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC;
             }
@@ -1562,41 +1714,65 @@ port_role_transition_sm(struct rstp_port *p)
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC:
         p->re_root = false;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC:
         p->rr_while = 0;
         p->synced = true;
         p->sync = false;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC:
         p->proposing = true;
         p->edge_delay_while = edge_delay(p);
         p->new_info = true;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC:
         p->forward = true;
         p->fd_while = 0;
         p->agreed = p->send_rstp;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC:
         p->learn = true;
         p->fd_while = forward_delay(p);
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC:
         p->learn = p->forward = p->disputed = false;
         p->fd_while = forward_delay(p);
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC:
         p->fd_while = p->designated_times.forward_delay;
@@ -1608,42 +1784,46 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT:
         if (check_selected_role_change(p, ROLE_ALTERNATE)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info) {
-            if ((p->rb_while != (2 * p->designated_times.hello_time)) &&
-                    (p->role == ROLE_BACKUP)) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info) {
+            if (p->rb_while != 2 * p->designated_times.hello_time
+                && p->role == ROLE_BACKUP) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC;
-            }
-            else if ((p->fd_while != forward_delay(p)) || p->sync ||
-                     p->re_root || !p->synced) {
+            } else if ((p->fd_while != forward_delay(p)) || p->sync
+                       || p->re_root || !p->synced) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
-            }
-            else if (p->proposed && !p->agree) {
+            } else if (p->proposed && !p->agree) {
                 p->port_role_transition_sm_state =
                     PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC;
-            }
-            else if (( all_synced(r) && !p->agree) ||
-                     (p->proposed && p->agree)) {
+            } else if ((all_synced(r) && !p->agree)
+                       || (p->proposed && p->agree)) {
                 p->port_role_transition_sm_state =
-                    PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED;
+                    PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC;
             }
         }
         break;
-    case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED:
+    case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC:
         p->proposed = false;
         p->agree = true;
         p->new_info = true;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ALTERNATE)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC:
         set_sync_tree(p);
         p->proposed = false;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ALTERNATE)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        }
         break;
     case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC:
         p->role = p->selected_role;
@@ -1652,18 +1832,21 @@ port_role_transition_sm(struct rstp_port *p)
         /* no break */
     case PORT_ROLE_TRANSITION_SM_BLOCK_PORT:
         if (check_selected_role_change(p, ROLE_ALTERNATE)) {
-            break;
-        }
-        else if (p->selected && !p->updt_info && !p->learning &&
-                 !p->forwarding) {
+            /* Global transition. */
+        } else if (p->selected && !p->updt_info && !p->learning &&
+                   !p->forwarding) {
             p->port_role_transition_sm_state =
                 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
         }
         break;
     case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC:
         p->rb_while = 2 * p->designated_times.hello_time;
-        p->port_role_transition_sm_state =
-            PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        if (check_selected_role_change(p, ROLE_ALTERNATE)) {
+            /* Global transition. */
+        } else {
+            p->port_role_transition_sm_state =
+                PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+        }
         break;
     default:
         OVS_NOT_REACHED();
@@ -1676,7 +1859,6 @@ port_role_transition_sm(struct rstp_port *p)
                  p->port_role_transition_sm_state);
     }
     if (last_role != p->role) {
-        last_role = p->role;
         VLOG_DBG("%s, port %u, port role ["RSTP_PORT_ID_FMT"] = %s",
                  p->rstp->name, p->port_number, p->port_id,
                  rstp_port_role_name(p->role));
@@ -1688,50 +1870,55 @@ port_role_transition_sm(struct rstp_port *p)
 
 static void
 enable_learning(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* [17.21.6 enableLearning()] An implementation dependent procedure that
      * causes the Learning Process (7.8) to start learning from frames received
      * on the Port. The procedure does not complete until learning has been
      * enabled.
      */
-    rstp_port_set_state(p, RSTP_LEARNING);
+    rstp_port_set_state__(p, RSTP_LEARNING);
 }
 
 static void
 enable_forwarding(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* [17.21.5 enableForwarding()] An implementation dependent procedure that
      * causes the Forwarding Process (7.7) to start forwarding frames through
      * the Port. The procedure does not complete until forwarding has been
      * enabled.
      */
-    rstp_port_set_state(p, RSTP_FORWARDING);
+    rstp_port_set_state__(p, RSTP_FORWARDING);
 }
 
 static void
 disable_learning(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* [17.21.4 - disableLearning()] An implementation dependent procedure that
      * causes the Learning Process (7.8) to stop learning from the source
      * address of frames received on the Port. The procedure does not complete
      * until learning has stopped.
      */
-    rstp_port_set_state(p, RSTP_DISCARDING);
+    rstp_port_set_state__(p, RSTP_DISCARDING);
 }
 
 static void
 disable_forwarding(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     /* [17.21.3 - disableForwarding()] An implementation dependent procedure
      *  that causes the Forwarding Process (7.7) to stop forwarding frames
      * through the Port. The procedure does not complete until forwarding has
      * stopped.
      */
-    rstp_port_set_state(p, RSTP_DISCARDING);
+    rstp_port_set_state__(p, RSTP_DISCARDING);
 }
 
 static int
 port_state_transition_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum port_state_transition_state_machine old_state;
     struct rstp *r;
@@ -1769,8 +1956,7 @@ port_state_transition_sm(struct rstp_port *p)
         if (!p->learn) {
             p->port_state_transition_sm_state =
                 PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
-        }
-        else if (p->forward) {
+        } else if (p->forward) {
             p->port_state_transition_sm_state =
                 PORT_STATE_TRANSITION_SM_FORWARDING_EXEC;
         }
@@ -1804,6 +1990,7 @@ port_state_transition_sm(struct rstp_port *p)
 
 static void
 new_tc_while(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
 
@@ -1811,8 +1998,7 @@ new_tc_while(struct rstp_port *p)
     if (p->tc_while == 0 && p->send_rstp == true) {
         p->tc_while = r->bridge_hello_time + 1;
         p->new_info = true;
-    }
-    else if (p->tc_while == 0 && p->send_rstp == false) {
+    } else if (p->tc_while == 0 && p->send_rstp == false) {
         p->tc_while = r->bridge_max_age + r->bridge_forward_delay;
     }
 }
@@ -1822,31 +2008,31 @@ new_tc_while(struct rstp_port *p)
  */
 static void
 set_tc_prop_tree(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     struct rstp *r;
     struct rstp_port *p1;
 
     r = p->rstp;
-    if (r->ports_count > 0) {
-        LIST_FOR_EACH (p1, node, &r->ports) {
-            /* Set tc_prop on every port, except the one calling this
-             * function.
-             */
-            if (p1->port_number != p->port_number) {
-                p1->tc_prop = true;
-            }
+    HMAP_FOR_EACH (p1, node, &r->ports) {
+        /* Set tc_prop on every port, except the one calling this
+         * function. */
+        if (p1->port_number != p->port_number) {
+            p1->tc_prop = true;
         }
     }
 }
 
 static void
 set_tc_prop_bridge(struct rstp_port *p)  /* not specified in 802.1D-2004. */
+    OVS_REQUIRES(rstp_mutex)
 {
     set_tc_prop_tree(p); /* see 802.1w-2001. */
 }
 
 static int
 topology_change_sm(struct rstp_port *p)
+    OVS_REQUIRES(rstp_mutex)
 {
     enum topology_change_state_machine old_state;
     struct rstp *r;
@@ -1877,16 +2063,14 @@ topology_change_sm(struct rstp_port *p)
         p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING;
         /* no break */
     case TOPOLOGY_CHANGE_SM_LEARNING:
-        if ((p->role != ROLE_ROOT) && (p->role != ROLE_DESIGNATED) &&
+        if (p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED &&
             !(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn ||
-                p->rcvd_tc_ack || p->tc_prop)) {
+                                            p->rcvd_tc_ack || p->tc_prop)) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
-        }
-        else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) {
+        } else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
-        }
-        else if (((p->role == ROLE_ROOT) || (p->role == ROLE_DESIGNATED)) &&
-                p->forward && !p->oper_edge) {
+        } else if ((p->role == ROLE_ROOT || p->role == ROLE_DESIGNATED)
+                   && p->forward && !p->oper_edge) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC;
         }
         break;
@@ -1900,20 +2084,16 @@ topology_change_sm(struct rstp_port *p)
         p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
         /* no break */
     case TOPOLOGY_CHANGE_SM_ACTIVE:
-        if (((p->role != ROLE_ROOT) && (p->role != ROLE_DESIGNATED)) ||
-                p->oper_edge) {
+        if ((p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED)
+            || p->oper_edge) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
-        }
-        else if (p->rcvd_tcn) {
+        } else if (p->rcvd_tcn) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC;
-        }
-        else if (p->rcvd_tc) {
+        } else if (p->rcvd_tc) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
-        }
-        else if (p->tc_prop && !p->oper_edge) {
+        } else if (p->tc_prop && !p->oper_edge) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC;
-        }
-        else if (p->rcvd_tc_ack) {
+        } else if (p->rcvd_tc_ack) {
             p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC;
         }
         break;
@@ -1938,7 +2118,7 @@ topology_change_sm(struct rstp_port *p)
         break;
     case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC:
         new_tc_while(p);
-        p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+        p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
         break;
     default:
         OVS_NOT_REACHED();
@@ -1956,82 +2136,84 @@ topology_change_sm(struct rstp_port *p)
  * [17.6] Priority vector calculation helper functions
  ****************************************************************************/
 
-/* [17.6]
- * This message priority vector is superior to the port priority vector and
- * will replace it if, and only if, the message priority vector is better
- * than the port priority vector, or the message has been transmitted from the
- * same Designated Bridge and Designated Port as the port priority vector,
- * i.e.,if the following is true:
- *    ((RD  < RootBridgeID)) ||
- *    ((RD == RootBridgeID) && (RPCD < RootPathCost)) ||
- *    ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
- *         (D < designated_bridge_id)) ||
- *    ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
- *         (D == designated_bridge_id) && (PD < designated_port_id)) ||
- *    ((D  == designated_bridge_id.BridgeAddress) &&
- *         (PD == designated_port_id.PortNumber))
- */
-
-/* compare_rstp_priority_vector() compares two struct rstp_priority_vector and
- * returns a value indicating if the first rstp_priority_vector is superior,
- * same or inferior to the second one.
+/* compare_rstp_priority_vectors() compares two struct rstp_priority_vectors
+ * and returns a value indicating if the first rstp_priority_vector is
+ * superior, same or inferior to the second one.
+ *
+ * Zero return value indicates INFERIOR, a non-zero return value indicates
+ * SUPERIOR.  When it makes a difference the non-zero return value SAME
+ * indicates the priority vectors are identical (a subset of SUPERIOR).
  */
 static enum vector_comparison
-compare_rstp_priority_vector(struct rstp_priority_vector *v1,
-                             struct rstp_priority_vector *v2)
+compare_rstp_priority_vectors(const struct rstp_priority_vector *v1,
+                             const struct rstp_priority_vector *v2)
 {
-    VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
+    VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d",
              RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost,
-             RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id);
-    VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
+             RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id,
+             v1->bridge_port_id);
+    VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d",
              RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost,
-             RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id);
-
-    if ((v1->root_bridge_id < v2->root_bridge_id) ||
-        ((v1->root_bridge_id == v2->root_bridge_id) &&
-         (v1->root_path_cost < v2->root_path_cost)) ||
-        ((v1->root_bridge_id == v2->root_bridge_id) &&
-         (v1->root_path_cost == v2->root_path_cost) &&
-         (v1->designated_bridge_id < v2->designated_bridge_id)) ||
-        ((v1->root_bridge_id == v2->root_bridge_id) &&
-         (v1->root_path_cost == v2->root_path_cost) &&
-         (v1->designated_bridge_id == v2->designated_bridge_id) &&
-            (v1->designated_port_id < v2->designated_port_id))) {
-        VLOG_DBG("superior_absolute");
-        return SUPERIOR;
-    }
-    else if (((v1->root_bridge_id > v2->root_bridge_id) ||
-                ((v1->root_bridge_id == v2->root_bridge_id) &&
-                 (v1->root_path_cost > v2->root_path_cost)) ||
-                ((v1->root_bridge_id == v2->root_bridge_id) &&
-                 (v1->root_path_cost == v2->root_path_cost) &&
-                 (v1->designated_bridge_id > v2->designated_bridge_id)) ||
-                ((v1->root_bridge_id == v2->root_bridge_id) &&
-                 (v1->root_path_cost == v2->root_path_cost) &&
-                 (v1->designated_bridge_id == v2->designated_bridge_id) &&
-                 (v1->designated_port_id > v2->designated_port_id))) &&
-            (v1->designated_bridge_id == v2->designated_bridge_id) &&
-            (v1->designated_port_id == v2->designated_port_id)) {
-        VLOG_DBG("superior_same_des");
+             RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id,
+             v2->bridge_port_id);
+
+    /* [17.6]
+     * This message priority vector is superior to the port priority vector and
+     * will replace it if, and only if, the message priority vector is better
+     * than the port priority vector, or the message has been transmitted from
+     * the same Designated Bridge and Designated Port as the port priority
+     * vector, i.e., if the following is true:
+     *
+     *    ((RD  < RootBridgeID)) ||
+     *    ((RD == RootBridgeID) && (RPCD < RootPathCost)) ||
+     *    ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
+     *         (D < designated_bridge_id)) ||
+     *    ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
+     *         (D == designated_bridge_id) && (PD < designated_port_id)) ||
+     *    ((D  == designated_bridge_id.BridgeAddress) &&
+     *         (PD == designated_port_id.PortNumber))
+     */
+    if ((v1->root_bridge_id < v2->root_bridge_id)
+        || (v1->root_bridge_id == v2->root_bridge_id
+            && v1->root_path_cost < v2->root_path_cost)
+        || (v1->root_bridge_id == v2->root_bridge_id
+            && v1->root_path_cost == v2->root_path_cost
+            && v1->designated_bridge_id < v2->designated_bridge_id)
+        || (v1->root_bridge_id == v2->root_bridge_id
+            && v1->root_path_cost == v2->root_path_cost
+            && v1->designated_bridge_id == v2->designated_bridge_id
+            && v1->designated_port_id < v2->designated_port_id)
+        || (v1->designated_bridge_id == v2->designated_bridge_id
+            && v1->designated_port_id == v2->designated_port_id)) {
+        /* SAME is a subset of SUPERIOR. */
+        if (v1->root_bridge_id == v2->root_bridge_id
+            && v1->root_path_cost == v2->root_path_cost
+            && v1->designated_bridge_id == v2->designated_bridge_id
+            && v1->designated_port_id == v2->designated_port_id) {
+            if (v1->bridge_port_id < v2->bridge_port_id) {
+                VLOG_DBG("superior");
+                return SUPERIOR;
+            }
+            else if (v1->bridge_port_id > v2->bridge_port_id) {
+                VLOG_DBG("inferior");
+                return INFERIOR;
+            }
+            VLOG_DBG("superior_same");
+            return SAME;
+        }
+        VLOG_DBG("superior");
         return SUPERIOR;
     }
-    else if ((v1->root_bridge_id == v2->root_bridge_id) &&
-             (v1->root_path_cost == v2->root_path_cost) &&
-             (v1->designated_bridge_id == v2->designated_bridge_id) &&
-             (v1->designated_port_id == v2->designated_port_id)) {
-        VLOG_DBG("same");
-        return SAME;
-    }
-    else {
-        VLOG_DBG("inferior");
-        return INFERIOR;
-    }
+
+    VLOG_DBG("inferior");
+    return INFERIOR;
 }
 
 static bool
-rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2) {
-    return ((t1->forward_delay == t2->forward_delay) &&
-            (t1->hello_time == t2->hello_time) &&
-            (t1->max_age == t2->max_age) &&
-            (t1->message_age == t2->message_age));
+rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2)
+{
+    return t1->forward_delay == t2->forward_delay
+        && t1->hello_time == t2->hello_time
+        && t1->max_age == t2->max_age
+        && t1->message_age == t2->message_age;
 }