vswitchd: Disable system stats collection on a concurrently running daemon.
[cascardo/ovs.git] / vswitchd / bridge.c
index d11046b..724b4b9 100644 (file)
@@ -146,6 +146,23 @@ static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
 /* OVSDB IDL used to obtain configuration. */
 static struct ovsdb_idl *idl;
 
+/* We want to complete daemonization, fully detaching from our parent process,
+ * only after we have completed our initial configuration, committed our state
+ * to the database, and received confirmation back from the database server
+ * that it applied the commit.  This allows our parent process to know that,
+ * post-detach, ephemeral fields such as datapath-id and ofport are very likely
+ * to have already been filled in.  (It is only "very likely" rather than
+ * certain because there is always a slim possibility that the transaction will
+ * fail or that some other client has added new bridges, ports, etc. while
+ * ovs-vswitchd was configuring using an old configuration.)
+ *
+ * We only need to do this once for our initial configuration at startup, so
+ * 'initial_config_done' tracks whether we've already done it.  While we are
+ * waiting for a response to our commit, 'daemonize_txn' tracks the transaction
+ * itself and is otherwise NULL. */
+static bool initial_config_done;
+static struct ovsdb_idl_txn *daemonize_txn;
+
 /* Most recently processed IDL sequence number. */
 static unsigned int idl_seqno;
 
@@ -602,15 +619,6 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
     }
     free(managers);
 
-    if (done) {
-        /* ovs-vswitchd has completed initialization, so allow the process that
-         * forked us to exit successfully. */
-        daemonize_complete();
-        reconfiguring = false;
-
-        VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION);
-    }
-
     return done;
 }
 
@@ -2034,17 +2042,61 @@ refresh_controller_status(void)
 
     ofproto_free_ofproto_controller_info(&info);
 }
+\f
+/* "Instant" stats.
+ *
+ * Some information in the database must be kept as up-to-date as possible to
+ * allow controllers to respond rapidly to network outages.  We call these
+ * statistics "instant" stats.
+ *
+ * We wish to update these statistics every INSTANT_INTERVAL_MSEC milliseconds,
+ * assuming that they've changed.  The only means we have to determine whether
+ * they have changed are:
+ *
+ *   - Try to commit changes to the database.  If nothing changed, then
+ *     ovsdb_idl_txn_commit() returns TXN_UNCHANGED, otherwise some other
+ *     value.
+ *
+ *   - instant_stats_run() is called late in the run loop, after anything that
+ *     might change any of the instant stats.
+ *
+ * We use these two facts together to avoid waking the process up every
+ * INSTANT_INTERVAL_MSEC whether there is any change or not.
+ */
+
+/* Minimum interval between writing updates to the instant stats to the
+ * database. */
+#define INSTANT_INTERVAL_MSEC 100
+
+/* Current instant stats database transaction, NULL if there is no ongoing
+ * transaction. */
+static struct ovsdb_idl_txn *instant_txn;
+
+/* Next time (in msec on monotonic clock) at which we will update the instant
+ * stats.  */
+static long long int instant_next_txn = LLONG_MIN;
+
+/* True if the run loop has run since we last saw that the instant stats were
+ * unchanged, that is, this is true if we need to wake up at 'instant_next_txn'
+ * to refresh the instant stats. */
+static bool instant_stats_could_have_changed;
 
 static void
-refresh_instant_stats(void)
+instant_stats_run(void)
 {
-    static struct ovsdb_idl_txn *txn = NULL;
+    enum ovsdb_idl_txn_status status;
+
+    instant_stats_could_have_changed = true;
 
-    if (!txn) {
+    if (!instant_txn) {
         struct bridge *br;
 
-        txn = ovsdb_idl_txn_create(idl);
+        if (time_msec() < instant_next_txn) {
+            return;
+        }
+        instant_next_txn = time_msec() + INSTANT_INTERVAL_MSEC;
 
+        instant_txn = ovsdb_idl_txn_create(idl);
         HMAP_FOR_EACH (br, node, &all_bridges) {
             struct iface *iface;
             struct port *port;
@@ -2093,12 +2145,26 @@ refresh_instant_stats(void)
         }
     }
 
-    if (ovsdb_idl_txn_commit(txn) != TXN_INCOMPLETE) {
-        ovsdb_idl_txn_destroy(txn);
-        txn = NULL;
+    status = ovsdb_idl_txn_commit(instant_txn);
+    if (status != TXN_INCOMPLETE) {
+        ovsdb_idl_txn_destroy(instant_txn);
+        instant_txn = NULL;
+    }
+    if (status == TXN_UNCHANGED) {
+        instant_stats_could_have_changed = false;
     }
 }
 
+static void
+instant_stats_wait(void)
+{
+    if (instant_txn) {
+        ovsdb_idl_txn_wait(instant_txn);
+    } else if (instant_stats_could_have_changed) {
+        poll_timer_wait_until(instant_next_txn);
+    }
+}
+\f
 /* Performs periodic activity required by bridges that needs to be done with
  * the least possible latency.
  *
@@ -2152,6 +2218,10 @@ bridge_run(void)
             HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
                 bridge_destroy(br);
             }
+            /* Since we will not be running system_stats_run() in this process
+             * with the current situation of multiple ovs-vswitchd daemons,
+             * disable system stats collection. */
+            system_stats_enable(false);
             return;
         } else if (!ovsdb_idl_has_lock(idl)) {
             return;
@@ -2218,15 +2288,25 @@ bridge_run(void)
     }
 
     if (reconfiguring) {
-        if (cfg) {
-            if (!reconf_txn) {
-                reconf_txn = ovsdb_idl_txn_create(idl);
-            }
-            if (bridge_reconfigure_continue(cfg)) {
+        if (!reconf_txn) {
+            reconf_txn = ovsdb_idl_txn_create(idl);
+        }
+
+        if (bridge_reconfigure_continue(cfg ? cfg : &null_cfg)) {
+            reconfiguring = false;
+
+            if (cfg) {
                 ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
             }
-        } else {
-            bridge_reconfigure_continue(&null_cfg);
+
+            /* If we are completing our initial configuration for this run
+             * of ovs-vswitchd, then keep the transaction around to monitor
+             * it for completion. */
+            if (!initial_config_done) {
+                initial_config_done = true;
+                daemonize_txn = reconf_txn;
+                reconf_txn = NULL;
+            }
         }
     }
 
@@ -2236,6 +2316,20 @@ bridge_run(void)
         reconf_txn = NULL;
     }
 
+    if (daemonize_txn) {
+        enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(daemonize_txn);
+        if (status != TXN_INCOMPLETE) {
+            ovsdb_idl_txn_destroy(daemonize_txn);
+            daemonize_txn = NULL;
+
+            /* ovs-vswitchd has completed initialization, so allow the
+             * process that forked us to exit successfully. */
+            daemonize_complete();
+
+            VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION);
+        }
+    }
+
     /* Refresh interface and mirror stats if necessary. */
     if (time_msec() >= iface_stats_timer) {
         if (cfg) {
@@ -2269,7 +2363,7 @@ bridge_run(void)
     }
 
     run_system_stats();
-    refresh_instant_stats();
+    instant_stats_run();
 }
 
 void
@@ -2279,6 +2373,9 @@ bridge_wait(void)
     const char *type;
 
     ovsdb_idl_wait(idl);
+    if (daemonize_txn) {
+        ovsdb_idl_txn_wait(daemonize_txn);
+    }
 
     if (reconfiguring) {
         poll_immediate_wake();
@@ -2301,6 +2398,7 @@ bridge_wait(void)
     }
 
     system_stats_wait();
+    instant_stats_wait();
 }
 
 /* Adds some memory usage statistics for bridges into 'usage', for use with