bridge: Fix high cpu utilization.
[cascardo/ovs.git] / vswitchd / bridge.c
index 84e9ab8..c5c6096 100644 (file)
@@ -34,6 +34,7 @@
 #include "lacp.h"
 #include "list.h"
 #include "mac-learning.h"
+#include "mcast-snooping.h"
 #include "meta-flow.h"
 #include "netdev.h"
 #include "ofp-print.h"
@@ -41,6 +42,7 @@
 #include "ofpbuf.h"
 #include "ofproto/bond.h"
 #include "ofproto/ofproto.h"
+#include "ovs-numa.h"
 #include "poll-loop.h"
 #include "seq.h"
 #include "sha1.h"
@@ -173,8 +175,13 @@ static uint64_t connectivity_seqno = LLONG_MIN;
  * update if the previous transaction status is 'TXN_INCOMPLETE'.
  *
  * 'statux_txn' is NULL if there is no ongoing status update.
+ *
+ * If the previous database transaction was failed (is not 'TXN_SUCCESS',
+ * 'TXN_UNCHANGED' or 'TXN_INCOMPLETE'), 'status_txn_try_again' is set to true,
+ * which will cause the main thread wake up soon and retry the status update.
  */
 static struct ovsdb_idl_txn *status_txn;
+static bool status_txn_try_again;
 
 /* When the status update transaction returns 'TXN_INCOMPLETE', should register a
  * timeout in 'STATUS_CHECK_AGAIN_MSEC' to check again. */
@@ -182,13 +189,8 @@ static struct ovsdb_idl_txn *status_txn;
 
 /* Each time this timer expires, the bridge fetches interface and mirror
  * statistics and pushes them into the database. */
-#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
-static long long int iface_stats_timer = LLONG_MIN;
-
-/* Set to true to allow experimental use of OpenFlow 1.4.
- * This is false initially because OpenFlow 1.4 is not yet safe to use: it can
- * abort due to unimplemented features. */
-static bool allow_of14;
+static int stats_timer_interval;
+static long long int stats_timer = LLONG_MIN;
 
 /* In some datapaths, creating and destroying OpenFlow ports can be extremely
  * expensive.  This can cause bridge_reconfigure() to take a long time during
@@ -223,9 +225,11 @@ static void bridge_configure_datapath_id(struct bridge *);
 static void bridge_configure_netflow(struct bridge *);
 static void bridge_configure_forward_bpdu(struct bridge *);
 static void bridge_configure_mac_table(struct bridge *);
+static void bridge_configure_mcast_snooping(struct bridge *);
 static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
 static void bridge_configure_ipfix(struct bridge *);
 static void bridge_configure_stp(struct bridge *);
+static void bridge_configure_rstp(struct bridge *);
 static void bridge_configure_tables(struct bridge *);
 static void bridge_configure_dp_desc(struct bridge *);
 static void bridge_configure_remotes(struct bridge *,
@@ -279,7 +283,7 @@ static struct iface *iface_from_ofp_port(const struct bridge *,
                                          ofp_port_t ofp_port);
 static void iface_set_mac(const struct bridge *, const struct port *, struct iface *);
 static void iface_set_ofport(const struct ovsrec_interface *, ofp_port_t ofport);
-static void iface_clear_db_record(const struct ovsrec_interface *if_cfg);
+static void iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp);
 static void iface_configure_qos(struct iface *, const struct ovsrec_qos *);
 static void iface_configure_cfm(struct iface *);
 static void iface_refresh_cfm_stats(struct iface *);
@@ -376,9 +380,14 @@ bridge_init(const char *remote)
 
     ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id);
     ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_status);
+    ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_status);
+    ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_stp_enable);
+    ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_enable);
     ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids);
 
     ovsdb_idl_omit_alert(idl, &ovsrec_port_col_status);
+    ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_status);
+    ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_statistics);
     ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics);
     ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
 
@@ -401,6 +410,7 @@ bridge_init(const char *remote)
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_opstate);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_bfd_status);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_error);
     ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids);
 
     ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_is_connected);
@@ -438,7 +448,9 @@ bridge_init(const char *remote)
     lacp_init();
     bond_init();
     cfm_init();
+    ovs_numa_init();
     stp_init();
+    rstp_init();
 }
 
 void
@@ -452,14 +464,6 @@ bridge_exit(void)
     ovsdb_idl_destroy(idl);
 }
 
-/* Enables use of OpenFlow 1.4.  This is off by default because OpenFlow 1.4 is
- * not yet safe to use: it can abort due to unimplemented features. */
-void
-bridge_enable_of14(void)
-{
-    allow_of14 = true;
-}
-
 /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP
  * addresses and ports into '*managersp' and '*n_managersp'.  The caller is
  * responsible for freeing '*managersp' (with free()).
@@ -500,12 +504,15 @@ collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg,
 
         managers = xmalloc(sset_count(&targets) * sizeof *managers);
         SSET_FOR_EACH (target, &targets) {
-            struct sockaddr_storage ss;
+            union {
+                struct sockaddr_storage ss;
+                struct sockaddr_in in;
+            } sa;
 
             if (stream_parse_target_with_default_port(target, OVSDB_OLD_PORT,
-                                                      &ss)
-                && ss.ss_family == AF_INET) {
-                managers[n_managers++] = *(struct sockaddr_in *) &ss;
+                                                      &sa.ss)
+                && sa.ss.ss_family == AF_INET) {
+                managers[n_managers++] = sa.in;
             }
         }
     }
@@ -530,6 +537,9 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                                         OFPROTO_FLOW_LIMIT_DEFAULT));
     ofproto_set_max_idle(smap_get_int(&ovs_cfg->other_config, "max-idle",
                                       OFPROTO_MAX_IDLE_DEFAULT));
+    ofproto_set_n_dpdk_rxqs(smap_get_int(&ovs_cfg->other_config,
+                                         "n-dpdk-rxqs", 0));
+    ofproto_set_cpu_mask(smap_get(&ovs_cfg->other_config, "pmd-cpu-mask"));
 
     ofproto_set_threads(
         smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0),
@@ -609,6 +619,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 
             LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
                 iface_set_ofport(iface->cfg, iface->ofp_port);
+                /* Clear eventual previous errors */
+                ovsrec_interface_set_error(iface->cfg, NULL);
                 iface_configure_cfm(iface);
                 iface_configure_qos(iface, port->cfg->qos);
                 iface_set_mac(br, port, iface);
@@ -619,20 +631,15 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         bridge_configure_mirrors(br);
         bridge_configure_forward_bpdu(br);
         bridge_configure_mac_table(br);
+        bridge_configure_mcast_snooping(br);
         bridge_configure_remotes(br, managers, n_managers);
         bridge_configure_netflow(br);
         bridge_configure_sflow(br, &sflow_bridge_number);
         bridge_configure_ipfix(br);
         bridge_configure_stp(br);
+        bridge_configure_rstp(br);
         bridge_configure_tables(br);
         bridge_configure_dp_desc(br);
-
-        if (smap_get(&br->cfg->other_config, "flow-eviction-threshold")) {
-            /* XXX: Remove this warning message eventually. */
-            VLOG_WARN_ONCE("As of June 2013, flow-eviction-threshold has been"
-                           " moved to the Open_vSwitch table.  Ignoring its"
-                           " setting in the bridge table.");
-        }
     }
     free(managers);
 
@@ -731,7 +738,7 @@ bridge_delete_or_reconfigure_ports(struct bridge *br)
         }
 
         if (strcmp(ofproto_port.type, iface->type)
-            || netdev_set_config(iface->netdev, &iface->cfg->options)) {
+            || netdev_set_config(iface->netdev, &iface->cfg->options, NULL)) {
             /* The interface is the wrong type or can't be configured.
              * Delete it. */
             goto delete;
@@ -990,17 +997,11 @@ bridge_configure_datapath_id(struct bridge *br)
 static uint32_t
 bridge_get_allowed_versions(struct bridge *br)
 {
-    uint32_t allowed_versions;
-
     if (!br->cfg->n_protocols)
         return 0;
 
-    allowed_versions = ofputil_versions_from_strings(br->cfg->protocols,
-                                                     br->cfg->n_protocols);
-    if (!allow_of14) {
-        allowed_versions &= ~(1u << OFP14_VERSION);
-    }
-    return allowed_versions;
+    return ofputil_versions_from_strings(br->cfg->protocols,
+                                         br->cfg->n_protocols);
 }
 
 /* Set NetFlow configuration on 'br'. */
@@ -1175,6 +1176,15 @@ bridge_configure_ipfix(struct bridge *br)
         if (be_cfg->cache_max_flows) {
             be_opts.cache_max_flows = *be_cfg->cache_max_flows;
         }
+
+        be_opts.enable_tunnel_sampling = smap_get_bool(&be_cfg->other_config,
+                                             "enable-tunnel-sampling", true);
+
+        be_opts.enable_input_sampling = !smap_get_bool(&be_cfg->other_config,
+                                              "enable-input-sampling", false);
+
+        be_opts.enable_output_sampling = !smap_get_bool(&be_cfg->other_config,
+                                              "enable-output-sampling", false);
     }
 
     if (n_fe_opts > 0) {
@@ -1303,12 +1313,106 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
     }
 }
 
+static void
+port_configure_rstp(const struct ofproto *ofproto, struct port *port,
+        struct ofproto_port_rstp_settings *port_s, int *port_num_counter)
+{
+    const char *config_str;
+    struct iface *iface;
+
+    if (!smap_get_bool(&port->cfg->other_config, "rstp-enable", true)) {
+        port_s->enable = false;
+        return;
+    } else {
+        port_s->enable = true;
+    }
+
+    /* RSTP over bonds is not supported. */
+    if (!list_is_singleton(&port->ifaces)) {
+        VLOG_ERR("port %s: cannot enable RSTP on bonds, disabling",
+                port->name);
+        port_s->enable = false;
+        return;
+    }
+
+    iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+
+    /* Internal ports shouldn't participate in spanning tree, so
+     * skip them. */
+    if (!strcmp(iface->type, "internal")) {
+        VLOG_DBG("port %s: disable RSTP on internal ports", port->name);
+        port_s->enable = false;
+        return;
+    }
+
+    /* RSTP on mirror output ports is not supported. */
+    if (ofproto_is_mirror_output_bundle(ofproto, port)) {
+        VLOG_DBG("port %s: disable RSTP on mirror ports", port->name);
+        port_s->enable = false;
+        return;
+    }
+
+    config_str = smap_get(&port->cfg->other_config, "rstp-port-num");
+    if (config_str) {
+        unsigned long int port_num = strtoul(config_str, NULL, 0);
+        if (port_num < 1 || port_num > RSTP_MAX_PORTS) {
+            VLOG_ERR("port %s: invalid rstp-port-num", port->name);
+            port_s->enable = false;
+            return;
+        }
+        port_s->port_num = port_num;
+    } else {
+        if (*port_num_counter >= RSTP_MAX_PORTS) {
+            VLOG_ERR("port %s: too many RSTP ports, disabling", port->name);
+            port_s->enable = false;
+            return;
+        }
+        /* If rstp-port-num is not specified, use 0.
+         * rstp_port_set_port_number() will look for the first free one. */
+        port_s->port_num = 0;
+    }
+
+    config_str = smap_get(&port->cfg->other_config, "rstp-path-cost");
+    if (config_str) {
+        port_s->path_cost = strtoul(config_str, NULL, 10);
+    } else {
+        enum netdev_features current;
+        unsigned int mbps;
+
+        netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
+        mbps = netdev_features_to_bps(current, 100 * 1000 * 1000) / 1000000;
+        port_s->path_cost = rstp_convert_speed_to_cost(mbps);
+    }
+
+    config_str = smap_get(&port->cfg->other_config, "rstp-port-priority");
+    if (config_str) {
+        port_s->priority = strtoul(config_str, NULL, 0);
+    } else {
+        port_s->priority = RSTP_DEFAULT_PORT_PRIORITY;
+    }
+
+    port_s->admin_edge_port = smap_get_bool(&port->cfg->other_config,
+                                            "rstp-port-admin-edge", false);
+    port_s->auto_edge = smap_get_bool(&port->cfg->other_config,
+                                      "rstp-port-auto-edge", true);
+    port_s->mcheck = smap_get_bool(&port->cfg->other_config,
+                                   "rstp-port-mcheck", false);
+}
+
 /* Set spanning tree configuration on 'br'. */
 static void
 bridge_configure_stp(struct bridge *br)
 {
+    struct ofproto_rstp_status rstp_status;
+
+    ofproto_get_rstp_status(br->ofproto, &rstp_status);
     if (!br->cfg->stp_enable) {
         ofproto_set_stp(br->ofproto, NULL);
+    } else if (rstp_status.enabled) {
+        /* Do not activate STP if RSTP is enabled. */
+        VLOG_ERR("STP cannot be enabled if RSTP is running.");
+        ofproto_set_stp(br->ofproto, NULL);
+        ovsrec_bridge_set_stp_enable(br->cfg, false);
     } else {
         struct ofproto_stp_settings br_s;
         const char *config_str;
@@ -1398,6 +1502,113 @@ bridge_configure_stp(struct bridge *br)
     }
 }
 
+static void
+bridge_configure_rstp(struct bridge *br)
+{
+    struct ofproto_stp_status stp_status;
+
+    ofproto_get_stp_status(br->ofproto, &stp_status);
+    if (!br->cfg->rstp_enable) {
+        ofproto_set_rstp(br->ofproto, NULL);
+    } else if (stp_status.enabled) {
+        /* Do not activate RSTP if STP is enabled. */
+        VLOG_ERR("RSTP cannot be enabled if STP is running.");
+        ofproto_set_rstp(br->ofproto, NULL);
+        ovsrec_bridge_set_rstp_enable(br->cfg, false);
+    } else {
+        struct ofproto_rstp_settings br_s;
+        const char *config_str;
+        struct port *port;
+        int port_num_counter;
+
+        config_str = smap_get(&br->cfg->other_config, "rstp-address");
+        if (config_str) {
+            uint8_t ea[ETH_ADDR_LEN];
+
+            if (eth_addr_from_string(config_str, ea)) {
+                br_s.address = eth_addr_to_uint64(ea);
+            }
+            else {
+                br_s.address = eth_addr_to_uint64(br->ea);
+                VLOG_ERR("bridge %s: invalid rstp-address, defaulting "
+                        "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea));
+            }
+        }
+        else {
+            br_s.address = eth_addr_to_uint64(br->ea);
+        }
+
+        config_str = smap_get(&br->cfg->other_config, "rstp-priority");
+        if (config_str) {
+            br_s.priority = strtoul(config_str, NULL, 0);
+        } else {
+            br_s.priority = RSTP_DEFAULT_PRIORITY;
+        }
+
+        config_str = smap_get(&br->cfg->other_config, "rstp-ageing-time");
+        if (config_str) {
+            br_s.ageing_time = strtoul(config_str, NULL, 0);
+        } else {
+            br_s.ageing_time = RSTP_DEFAULT_AGEING_TIME;
+        }
+
+        config_str = smap_get(&br->cfg->other_config,
+                              "rstp-force-protocol-version");
+        if (config_str) {
+            br_s.force_protocol_version = strtoul(config_str, NULL, 0);
+        } else {
+            br_s.force_protocol_version = FPV_DEFAULT;
+        }
+
+        config_str = smap_get(&br->cfg->other_config, "rstp-max-age");
+        if (config_str) {
+            br_s.bridge_max_age = strtoul(config_str, NULL, 10);
+        } else {
+            br_s.bridge_max_age = RSTP_DEFAULT_BRIDGE_MAX_AGE;
+        }
+
+        config_str = smap_get(&br->cfg->other_config, "rstp-forward-delay");
+        if (config_str) {
+            br_s.bridge_forward_delay = strtoul(config_str, NULL, 10);
+        } else {
+            br_s.bridge_forward_delay = RSTP_DEFAULT_BRIDGE_FORWARD_DELAY;
+        }
+
+        config_str = smap_get(&br->cfg->other_config,
+                              "rstp-transmit-hold-count");
+        if (config_str) {
+            br_s.transmit_hold_count = strtoul(config_str, NULL, 10);
+        } else {
+            br_s.transmit_hold_count = RSTP_DEFAULT_TRANSMIT_HOLD_COUNT;
+        }
+
+        /* Configure RSTP on the bridge. */
+        if (ofproto_set_rstp(br->ofproto, &br_s)) {
+            VLOG_ERR("bridge %s: could not enable RSTP", br->name);
+            return;
+        }
+
+        port_num_counter = 0;
+        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+            struct ofproto_port_rstp_settings port_s;
+            struct iface *iface;
+
+            port_configure_rstp(br->ofproto, port, &port_s,
+                    &port_num_counter);
+
+            /* As bonds are not supported, just apply configuration to
+             * all interfaces. */
+            LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+                if (ofproto_port_set_rstp(br->ofproto, iface->ofp_port,
+                            &port_s)) {
+                    VLOG_ERR("port %s: could not enable RSTP", port->name);
+                    continue;
+                }
+            }
+        }
+    }
+}
+
 static bool
 bridge_has_bond_fake_iface(const struct bridge *br, const char *name)
 {
@@ -1460,9 +1671,9 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
  * Returns 0 if successful, otherwise a positive errno value. */
 static int
 iface_set_netdev_config(const struct ovsrec_interface *iface_cfg,
-                        struct netdev *netdev)
+                        struct netdev *netdev, char **errp)
 {
-    return netdev_set_config(netdev, &iface_cfg->options);
+    return netdev_set_config(netdev, &iface_cfg->options, errp);
 }
 
 /* Opens a network device for 'if_cfg' and configures it.  Adds the network
@@ -1474,7 +1685,8 @@ static int
 iface_do_create(const struct bridge *br,
                 const struct ovsrec_interface *iface_cfg,
                 const struct ovsrec_port *port_cfg,
-                ofp_port_t *ofp_portp, struct netdev **netdevp)
+                ofp_port_t *ofp_portp, struct netdev **netdevp,
+                char **errp)
 {
     struct netdev *netdev = NULL;
     int error;
@@ -1489,12 +1701,12 @@ iface_do_create(const struct bridge *br,
     error = netdev_open(iface_cfg->name,
                         iface_get_type(iface_cfg, br->cfg), &netdev);
     if (error) {
-        VLOG_WARN("could not open network device %s (%s)",
-                  iface_cfg->name, ovs_strerror(error));
+        VLOG_WARN_BUF(errp, "could not open network device %s (%s)",
+                      iface_cfg->name, ovs_strerror(error));
         goto error;
     }
 
-    error = iface_set_netdev_config(iface_cfg, netdev);
+    error = iface_set_netdev_config(iface_cfg, netdev, errp);
     if (error) {
         goto error;
     }
@@ -1535,13 +1747,15 @@ iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg,
     struct iface *iface;
     ofp_port_t ofp_port;
     struct port *port;
+    char *errp = NULL;
     int error;
 
     /* Do the bits that can fail up front. */
     ovs_assert(!iface_lookup(br, iface_cfg->name));
-    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
+    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev, &errp);
     if (error) {
-        iface_clear_db_record(iface_cfg);
+        iface_clear_db_record(iface_cfg, errp);
+        free(errp);
         return false;
     }
 
@@ -1629,6 +1843,52 @@ bridge_configure_mac_table(struct bridge *br)
     ofproto_set_mac_table_config(br->ofproto, idle_time, mac_table_size);
 }
 
+/* Set multicast snooping table configuration for 'br'. */
+static void
+bridge_configure_mcast_snooping(struct bridge *br)
+{
+    if (!br->cfg->mcast_snooping_enable) {
+        ofproto_set_mcast_snooping(br->ofproto, NULL);
+    } else {
+        struct port *port;
+        struct ofproto_mcast_snooping_settings br_s;
+        const char *idle_time_str;
+        const char *max_entries_str;
+
+        idle_time_str = smap_get(&br->cfg->other_config,
+                                 "mcast-snooping-aging-time");
+        br_s.idle_time = (idle_time_str && atoi(idle_time_str)
+                          ? atoi(idle_time_str)
+                          : MCAST_ENTRY_DEFAULT_IDLE_TIME);
+
+        max_entries_str = smap_get(&br->cfg->other_config,
+                                   "mcast-snooping-table-size");
+        br_s.max_entries = (max_entries_str && atoi(max_entries_str)
+                            ? atoi(max_entries_str)
+                            : MCAST_DEFAULT_MAX_ENTRIES);
+
+        br_s.flood_unreg = !smap_get_bool(&br->cfg->other_config,
+                                    "mcast-snooping-disable-flood-unregistered",
+                                    false);
+
+        /* Configure multicast snooping on the bridge */
+        if (ofproto_set_mcast_snooping(br->ofproto, &br_s)) {
+            VLOG_ERR("bridge %s: could not enable multicast snooping",
+                     br->name);
+            return;
+        }
+
+        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+            bool flood = smap_get_bool(&port->cfg->other_config,
+                                       "mcast-snooping-flood", false);
+            if (ofproto_port_set_mcast_snooping(br->ofproto, port, flood)) {
+                VLOG_ERR("port %s: could not configure mcast snooping",
+                         port->name);
+            }
+        }
+    }
+}
+
 static void
 find_local_hw_addr(const struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
                    const struct port *fake_br, struct iface **hw_addr_iface)
@@ -1840,7 +2100,8 @@ iface_refresh_netdev_status(struct iface *iface)
         return;
     }
 
-    if (iface->change_seq == netdev_get_change_seq(iface->netdev)) {
+    if (iface->change_seq == netdev_get_change_seq(iface->netdev)
+        && !status_txn_try_again) {
         return;
     }
 
@@ -1913,8 +2174,7 @@ iface_refresh_netdev_status(struct iface *iface)
 static void
 iface_refresh_ofproto_status(struct iface *iface)
 {
-    struct smap smap;
-    int current, error;
+    int current;
 
     if (iface_is_synthetic(iface)) {
         return;
@@ -1929,15 +2189,23 @@ iface_refresh_ofproto_status(struct iface *iface)
         ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
     }
 
-    iface_refresh_cfm_stats(iface);
+    if (ofproto_port_cfm_status_changed(iface->port->bridge->ofproto,
+                                        iface->ofp_port)
+        || status_txn_try_again) {
+        iface_refresh_cfm_stats(iface);
+    }
 
-    smap_init(&smap);
-    error = ofproto_port_get_bfd_status(iface->port->bridge->ofproto,
-                                        iface->ofp_port, &smap);
-    if (error >= 0) {
+    if (ofproto_port_bfd_status_changed(iface->port->bridge->ofproto,
+                                        iface->ofp_port)
+        || status_txn_try_again) {
+        struct smap smap;
+
+        smap_init(&smap);
+        ofproto_port_get_bfd_status(iface->port->bridge->ofproto,
+                                    iface->ofp_port, &smap);
         ovsrec_interface_set_bfd_status(iface->cfg, &smap);
+        smap_destroy(&smap);
     }
-    smap_destroy(&smap);
 }
 
 /* Writes 'iface''s CFM statistics to the database. 'iface' must not be
@@ -1946,14 +2214,12 @@ static void
 iface_refresh_cfm_stats(struct iface *iface)
 {
     const struct ovsrec_interface *cfg = iface->cfg;
-    struct ofproto_cfm_status status;
+    struct cfm_status status;
     int error;
 
     error = ofproto_port_get_cfm_status(iface->port->bridge->ofproto,
                                         iface->ofp_port, &status);
-    if (error < 0) {
-        /* Do nothing if there is no status change since last update. */
-    } else if (error > 0) {
+    if (error > 0) {
         ovsrec_interface_set_cfm_fault(cfg, NULL, 0);
         ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
         ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
@@ -2154,6 +2420,90 @@ port_refresh_stp_stats(struct port *port)
                                ARRAY_SIZE(int_values));
 }
 
+static void
+br_refresh_rstp_status(struct bridge *br)
+{
+    struct smap smap = SMAP_INITIALIZER(&smap);
+    struct ofproto *ofproto = br->ofproto;
+    struct ofproto_rstp_status status;
+
+    if (ofproto_get_rstp_status(ofproto, &status)) {
+        return;
+    }
+    if (!status.enabled) {
+        ovsrec_bridge_set_rstp_status(br->cfg, NULL);
+        return;
+    }
+    smap_add_format(&smap, "rstp_bridge_id", RSTP_ID_FMT,
+                    RSTP_ID_ARGS(status.bridge_id));
+    smap_add_format(&smap, "rstp_root_path_cost", "%d",
+                    status.root_path_cost);
+    smap_add_format(&smap, "rstp_root_id", RSTP_ID_FMT,
+                    RSTP_ID_ARGS(status.root_id));
+    smap_add_format(&smap, "rstp_designated_id", RSTP_ID_FMT,
+                    RSTP_ID_ARGS(status.designated_id));
+    smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT,
+                    status.designated_port_id);
+    smap_add_format(&smap, "rstp_bridge_port_id", RSTP_PORT_ID_FMT,
+                    status.bridge_port_id);
+    ovsrec_bridge_set_rstp_status(br->cfg, &smap);
+    smap_destroy(&smap);
+}
+
+static void
+port_refresh_rstp_status(struct port *port)
+{
+    struct ofproto *ofproto = port->bridge->ofproto;
+    struct iface *iface;
+    struct ofproto_port_rstp_status status;
+    char *keys[3];
+    int64_t int_values[3];
+    struct smap smap;
+
+    if (port_is_synthetic(port)) {
+        return;
+    }
+
+    /* RSTP doesn't currently support bonds. */
+    if (!list_is_singleton(&port->ifaces)) {
+        ovsrec_port_set_rstp_status(port->cfg, NULL);
+        return;
+    }
+
+    iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+    if (ofproto_port_get_rstp_status(ofproto, iface->ofp_port, &status)) {
+        return;
+    }
+
+    if (!status.enabled) {
+        ovsrec_port_set_rstp_status(port->cfg, NULL);
+        ovsrec_port_set_rstp_statistics(port->cfg, NULL, NULL, 0);
+        return;
+    }
+    /* Set Status column. */
+    smap_init(&smap);
+
+    smap_add_format(&smap, "rstp_port_id", RSTP_PORT_ID_FMT,
+                    status.port_id);
+    smap_add_format(&smap, "rstp_port_role", "%s",
+                    rstp_port_role_name(status.role));
+    smap_add_format(&smap, "rstp_port_state", "%s",
+                    rstp_state_name(status.state));
+
+    ovsrec_port_set_rstp_status(port->cfg, &smap);
+    smap_destroy(&smap);
+
+    /* Set Statistics column. */
+    keys[0] = "rstp_tx_count";
+    int_values[0] = status.tx_count;
+    keys[1] = "rstp_rx_count";
+    int_values[1] = status.rx_count;
+    keys[2] = "rstp_uptime";
+    int_values[2] = status.uptime;
+    ovsrec_port_set_rstp_statistics(port->cfg, keys, int_values,
+            ARRAY_SIZE(int_values));
+}
+
 static bool
 enable_system_stats(const struct ovsrec_open_vswitch *cfg)
 {
@@ -2229,20 +2579,10 @@ refresh_controller_status(void)
             shash_find_data(&info, cfg->target);
 
         if (cinfo) {
-            struct smap smap = SMAP_INITIALIZER(&smap);
-            const char **values = cinfo->pairs.values;
-            const char **keys = cinfo->pairs.keys;
-            size_t i;
-
-            for (i = 0; i < cinfo->pairs.n; i++) {
-                smap_add(&smap, keys[i], values[i]);
-            }
-
             ovsrec_controller_set_is_connected(cfg, cinfo->is_connected);
             ovsrec_controller_set_role(cfg, ofp12_controller_role_to_str(
                                            cinfo->role));
-            ovsrec_controller_set_status(cfg, &smap);
-            smap_destroy(&smap);
+            ovsrec_controller_set_status(cfg, &cinfo->pairs);
         } else {
             ovsrec_controller_set_is_connected(cfg, false);
             ovsrec_controller_set_role(cfg, NULL);
@@ -2253,6 +2593,137 @@ refresh_controller_status(void)
     ofproto_free_ofproto_controller_info(&info);
 }
 \f
+/* Update interface and mirror statistics if necessary. */
+static void
+run_stats_update(void)
+{
+    static struct ovsdb_idl_txn *stats_txn;
+    const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl);
+    int stats_interval;
+
+    if (!cfg) {
+        return;
+    }
+
+    /* Statistics update interval should always be greater than or equal to
+     * 5000 ms. */
+    stats_interval = MAX(smap_get_int(&cfg->other_config,
+                                      "stats-update-interval",
+                                      5000), 5000);
+    if (stats_timer_interval != stats_interval) {
+        stats_timer_interval = stats_interval;
+        stats_timer = LLONG_MIN;
+    }
+
+    if (time_msec() >= stats_timer) {
+        enum ovsdb_idl_txn_status status;
+
+        /* Rate limit the update.  Do not start a new update if the
+         * previous one is not done. */
+        if (!stats_txn) {
+            struct bridge *br;
+
+            stats_txn = ovsdb_idl_txn_create(idl);
+            HMAP_FOR_EACH (br, node, &all_bridges) {
+                struct port *port;
+                struct mirror *m;
+
+                HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+                    struct iface *iface;
+
+                    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+                        iface_refresh_stats(iface);
+                    }
+                    port_refresh_stp_stats(port);
+                }
+                HMAP_FOR_EACH (m, hmap_node, &br->mirrors) {
+                    mirror_refresh_stats(m);
+                }
+            }
+            refresh_controller_status();
+        }
+
+        status = ovsdb_idl_txn_commit(stats_txn);
+        if (status != TXN_INCOMPLETE) {
+            stats_timer = time_msec() + stats_timer_interval;
+            ovsdb_idl_txn_destroy(stats_txn);
+            stats_txn = NULL;
+        }
+    }
+}
+
+/* Update bridge/port/interface status if necessary. */
+static void
+run_status_update(void)
+{
+    if (!status_txn) {
+        uint64_t seq;
+
+        /* Rate limit the update.  Do not start a new update if the
+         * previous one is not done. */
+        seq = seq_read(connectivity_seq_get());
+        if (seq != connectivity_seqno || status_txn_try_again) {
+            struct bridge *br;
+
+            connectivity_seqno = seq;
+            status_txn = ovsdb_idl_txn_create(idl);
+            HMAP_FOR_EACH (br, node, &all_bridges) {
+                struct port *port;
+
+                br_refresh_stp_status(br);
+                br_refresh_rstp_status(br);
+                HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+                    struct iface *iface;
+
+                    port_refresh_stp_status(port);
+                    port_refresh_rstp_status(port);
+                    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+                        iface_refresh_netdev_status(iface);
+                        iface_refresh_ofproto_status(iface);
+                    }
+                }
+            }
+        }
+    }
+
+    /* Commit the transaction and get the status. If the transaction finishes,
+     * then destroy the transaction. Otherwise, keep it so that we can check
+     * progress the next time that this function is called. */
+    if (status_txn) {
+        enum ovsdb_idl_txn_status status;
+
+        status = ovsdb_idl_txn_commit(status_txn);
+        if (status != TXN_INCOMPLETE) {
+            ovsdb_idl_txn_destroy(status_txn);
+            status_txn = NULL;
+
+            /* Sets the 'status_txn_try_again' if the transaction fails. */
+            if (status == TXN_SUCCESS || status == TXN_UNCHANGED) {
+                status_txn_try_again = false;
+            } else {
+                status_txn_try_again = true;
+            }
+        }
+    }
+}
+
+static void
+status_update_wait(void)
+{
+    /* If the 'status_txn' is non-null (transaction incomplete), waits for the
+     * transaction to complete.  If the status update to database needs to be
+     * run again (transaction fails), registers a timeout in
+     * 'STATUS_CHECK_AGAIN_MSEC'.  Otherwise, waits on the global connectivity
+     * sequence number. */
+    if (status_txn) {
+        ovsdb_idl_txn_wait(status_txn);
+    } else if (status_txn_try_again) {
+        poll_timer_wait_until(time_msec() + STATUS_CHECK_AGAIN_MSEC);
+    } else {
+        seq_wait(connectivity_seq_get(), connectivity_seqno);
+    }
+}
+
 static void
 bridge_run__(void)
 {
@@ -2281,7 +2752,6 @@ bridge_run(void)
     const struct ovsrec_open_vswitch *cfg;
 
     bool vlan_splinters_changed;
-    struct bridge *br;
 
     ovsrec_open_vswitch_init(&null_cfg);
 
@@ -2302,6 +2772,9 @@ bridge_run(void)
          * with the current situation of multiple ovs-vswitchd daemons,
          * disable system stats collection. */
         system_stats_enable(false);
+        /* This prevents the process from constantly waking up on
+         * connectivity seq. */
+        connectivity_seqno = seq_read(connectivity_seq_get());
         return;
     } else if (!ovsdb_idl_has_lock(idl)) {
         return;
@@ -2340,6 +2813,8 @@ bridge_run(void)
      * usage has changed. */
     vlan_splinters_changed = false;
     if (vlan_splinters_enabled_anywhere) {
+        struct bridge *br;
+
         HMAP_FOR_EACH (br, node, &all_bridges) {
             if (ofproto_has_vlan_usage_changed(br->ofproto)) {
                 vlan_splinters_changed = true;
@@ -2363,6 +2838,9 @@ bridge_run(void)
          * of ovs-vswitchd, then keep the transaction around to monitor
          * it for completion. */
         if (initial_config_done) {
+            /* Always sets the 'status_txn_try_again' to check again,
+             * in case that this transaction fails. */
+            status_txn_try_again = true;
             ovsdb_idl_txn_commit(txn);
             ovsdb_idl_txn_destroy(txn);
         } else {
@@ -2387,76 +2865,8 @@ bridge_run(void)
         }
     }
 
-    /* Refresh interface and mirror stats if necessary. */
-    if (time_msec() >= iface_stats_timer) {
-        if (cfg) {
-            struct ovsdb_idl_txn *txn;
-
-            txn = ovsdb_idl_txn_create(idl);
-            HMAP_FOR_EACH (br, node, &all_bridges) {
-                struct port *port;
-                struct mirror *m;
-
-                HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-                    struct iface *iface;
-
-                    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-                        iface_refresh_stats(iface);
-                    }
-
-                    port_refresh_stp_stats(port);
-                }
-
-                HMAP_FOR_EACH (m, hmap_node, &br->mirrors) {
-                    mirror_refresh_stats(m);
-                }
-
-            }
-            refresh_controller_status();
-            ovsdb_idl_txn_commit(txn);
-            ovsdb_idl_txn_destroy(txn); /* XXX */
-        }
-
-        iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
-    }
-
-    if (!status_txn) {
-        uint64_t seq;
-
-        /* Check the need to update status. */
-        seq = seq_read(connectivity_seq_get());
-        if (seq != connectivity_seqno) {
-            connectivity_seqno = seq;
-            status_txn = ovsdb_idl_txn_create(idl);
-            HMAP_FOR_EACH (br, node, &all_bridges) {
-                struct port *port;
-
-                br_refresh_stp_status(br);
-                HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-                    struct iface *iface;
-
-                    port_refresh_stp_status(port);
-                    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-                        iface_refresh_netdev_status(iface);
-                        iface_refresh_ofproto_status(iface);
-                    }
-                }
-            }
-        }
-    }
-
-    if (status_txn) {
-        enum ovsdb_idl_txn_status status;
-
-        status = ovsdb_idl_txn_commit(status_txn);
-        /* Do not destroy "status_txn" if the transaction is
-         * "TXN_INCOMPLETE". */
-        if (status != TXN_INCOMPLETE) {
-            ovsdb_idl_txn_destroy(status_txn);
-            status_txn = NULL;
-        }
-    }
-
+    run_stats_update();
+    run_status_update();
     run_system_stats();
 }
 
@@ -2484,19 +2894,11 @@ bridge_wait(void)
         HMAP_FOR_EACH (br, node, &all_bridges) {
             ofproto_wait(br->ofproto);
         }
-        poll_timer_wait_until(iface_stats_timer);
-    }
 
-    /* If the status database transaction is 'TXN_INCOMPLETE' in this run,
-     * register a timeout in 'STATUS_CHECK_AGAIN_MSEC'.  Else, wait on the
-     * global connectivity sequence number.  Note, this also helps batch
-     * multiple status changes into one transaction. */
-    if (status_txn) {
-        poll_timer_wait_until(time_msec() + STATUS_CHECK_AGAIN_MSEC);
-    } else {
-        seq_wait(connectivity_seq_get(), connectivity_seqno);
+        poll_timer_wait_until(stats_timer);
     }
 
+    status_update_wait();
     system_stats_wait();
 }
 
@@ -2845,6 +3247,7 @@ bridge_ofproto_controller_for_mgmt(const struct bridge *br,
     oc->rate_limit = 0;
     oc->burst_limit = 0;
     oc->enable_async_msgs = true;
+    oc->dscp = 0;
 }
 
 /* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'.  */
@@ -3074,6 +3477,7 @@ bridge_configure_tables(struct bridge *br)
     j = 0;
     for (i = 0; i < n_tables; i++) {
         struct ofproto_table_settings s;
+        bool use_default_prefixes = true;
 
         s.name = NULL;
         s.max_flows = UINT_MAX;
@@ -3115,7 +3519,14 @@ bridge_configure_tables(struct bridge *br)
             s.n_prefix_fields = 0;
             for (k = 0; k < cfg->n_prefixes; k++) {
                 const char *name = cfg->prefixes[k];
-                const struct mf_field *mf = mf_from_name(name);
+                const struct mf_field *mf;
+
+                if (strcmp(name, "none") == 0) {
+                    use_default_prefixes = false;
+                    s.n_prefix_fields = 0;
+                    break;
+                }
+                mf = mf_from_name(name);
                 if (!mf) {
                     VLOG_WARN("bridge %s: 'prefixes' with unknown field: %s",
                               br->name, name);
@@ -3131,21 +3542,30 @@ bridge_configure_tables(struct bridge *br)
                               "field not used: %s", br->name, name);
                     continue;
                 }
+                use_default_prefixes = false;
                 s.prefix_fields[s.n_prefix_fields++] = mf->id;
             }
-            if (s.n_prefix_fields > 0) {
-                int k;
-                struct ds ds = DS_EMPTY_INITIALIZER;
-                for (k = 0; k < s.n_prefix_fields; k++) {
-                    if (k) {
-                        ds_put_char(&ds, ',');
-                    }
-                    ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name);
+        }
+        if (use_default_prefixes) {
+            /* Use default values. */
+            s.n_prefix_fields = ARRAY_SIZE(default_prefix_fields);
+            memcpy(s.prefix_fields, default_prefix_fields,
+                   sizeof default_prefix_fields);
+        } else {
+            int k;
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            for (k = 0; k < s.n_prefix_fields; k++) {
+                if (k) {
+                    ds_put_char(&ds, ',');
                 }
-                VLOG_INFO("bridge %s table %d: Prefix lookup with: %s.",
-                          br->name, i, ds_cstr(&ds));
-                ds_destroy(&ds);
+                ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name);
             }
+            if (s.n_prefix_fields == 0) {
+                ds_put_cstr(&ds, "none");
+            }
+            VLOG_INFO("bridge %s table %d: Prefix lookup with: %s.",
+                      br->name, i, ds_cstr(&ds));
+            ds_destroy(&ds);
         }
 
         ofproto_configure_table(br->ofproto, i, &s);
@@ -3396,8 +3816,6 @@ port_configure_bond(struct port *port, struct bond_settings *s)
         s->rebalance_interval = 1000;
     }
 
-    s->fake_iface = port->cfg->bond_fake_iface;
-
     s->lacp_fallback_ab_cfg = smap_get_bool(&port->cfg->other_config,
                                        "lacp-fallback-ab", false);
 
@@ -3462,7 +3880,9 @@ iface_destroy__(struct iface *iface)
         list_remove(&iface->port_elem);
         hmap_remove(&br->iface_by_name, &iface->name_node);
 
-        netdev_close(iface->netdev);
+        /* The user is changing configuration here, so netdev_remove needs to be
+         * used as opposed to netdev_close */
+        netdev_remove(iface->netdev);
 
         free(iface->name);
         free(iface);
@@ -3580,10 +4000,11 @@ iface_set_ofport(const struct ovsrec_interface *if_cfg, ofp_port_t ofport)
  * This is appropriate when 'if_cfg''s interface cannot be created or is
  * otherwise invalid. */
 static void
-iface_clear_db_record(const struct ovsrec_interface *if_cfg)
+iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp)
 {
     if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
         iface_set_ofport(if_cfg, OFPP_NONE);
+        ovsrec_interface_set_error(if_cfg, errp);
         ovsrec_interface_set_status(if_cfg, NULL);
         ovsrec_interface_set_admin_state(if_cfg, NULL);
         ovsrec_interface_set_duplex(if_cfg, NULL);