lacp: Remove custom transmission intervals.
[openvswitch] / vswitchd / bridge.c
index 5aa5c89e3d002088446c5640324cf62400707512..c7349a4dc18602cf13243d2635e34753836c637c 100644 (file)
@@ -129,6 +129,9 @@ static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
 /* OVSDB IDL used to obtain configuration. */
 static struct ovsdb_idl *idl;
 
+/* Most recently processed IDL sequence number. */
+static unsigned int idl_seqno;
+
 /* Each time this timer expires, the bridge fetches systems and interface
  * statistics and pushes them into the database. */
 #define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
@@ -140,6 +143,17 @@ static long long int stats_timer = LLONG_MIN;
 #define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
 static long long int db_limiter = 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
+ * which no other work can be done.  To deal with this problem, we limit port
+ * adds and deletions to a window of OFP_PORT_ACTION_WINDOW milliseconds per
+ * call to bridge_reconfigure().  If there is more work to do after the limit
+ * is reached, 'need_reconfigure', is flagged and it's done on the next loop.
+ * This allows the rest of the code to catch up on important things like
+ * forwarding packets. */
+#define OFP_PORT_ACTION_WINDOW 10
+static bool need_reconfigure = false;
+
 static void add_del_bridges(const struct ovsrec_open_vswitch *);
 static void bridge_del_ofprotos(void);
 static bool bridge_add_ofprotos(struct bridge *);
@@ -152,8 +166,10 @@ static size_t bridge_get_controllers(const struct bridge *br,
                                      struct ovsrec_controller ***controllersp);
 static void bridge_add_del_ports(struct bridge *,
                                  const unsigned long int *splinter_vlans);
-static void bridge_add_ofproto_ports(struct bridge *);
-static void bridge_del_ofproto_ports(struct bridge *);
+static void bridge_add_ofproto_ports(struct bridge *,
+                                     long long int *port_action_timer);
+static void bridge_del_ofproto_ports(struct bridge *,
+                                     long long int *port_action_timer);
 static void bridge_refresh_ofp_port(struct bridge *);
 static void bridge_configure_datapath_id(struct bridge *);
 static void bridge_configure_flow_eviction_threshold(struct bridge *);
@@ -248,6 +264,7 @@ bridge_init(const char *remote)
 {
     /* Create connection to database. */
     idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true);
+    idl_seqno = ovsdb_idl_get_seqno(idl);
     ovsdb_idl_set_lock(idl, "ovs_vswitchd");
 
     ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg);
@@ -391,6 +408,7 @@ static void
 bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 {
     unsigned long int *splinter_vlans;
+    long long int port_action_timer;
     struct sockaddr_in *managers;
     struct bridge *br, *next;
     int sflow_bridge_number;
@@ -398,6 +416,9 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 
     COVERAGE_INC(bridge_reconfigure);
 
+    port_action_timer = LLONG_MAX;
+    need_reconfigure = false;
+
     /* Create and destroy "struct bridge"s, "struct port"s, and "struct
      * iface"s according to 'ovs_cfg', with only very minimal configuration
      * otherwise.
@@ -420,7 +441,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     bridge_del_ofprotos();
     HMAP_FOR_EACH (br, node, &all_bridges) {
         if (br->ofproto) {
-            bridge_del_ofproto_ports(br);
+            bridge_del_ofproto_ports(br, &port_action_timer);
         }
     }
 
@@ -433,7 +454,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
         if (!br->ofproto) {
             if (bridge_add_ofprotos(br)) {
-                bridge_del_ofproto_ports(br);
+                bridge_del_ofproto_ports(br, &port_action_timer);
             } else {
                 bridge_destroy(br);
             }
@@ -441,7 +462,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     }
     HMAP_FOR_EACH (br, node, &all_bridges) {
         bridge_refresh_ofp_port(br);
-        bridge_add_ofproto_ports(br);
+        bridge_add_ofproto_ports(br, &port_action_timer);
     }
 
     /* Complete the configuration. */
@@ -477,9 +498,11 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     }
     free(managers);
 
-    /* ovs-vswitchd has completed initialization, so allow the process that
-     * forked us to exit successfully. */
-    daemonize_complete();
+    if (!need_reconfigure) {
+        /* ovs-vswitchd has completed initialization, so allow the process that
+         * forked us to exit successfully. */
+        daemonize_complete();
+    }
 }
 
 /* Iterate over all ofprotos and delete any of them that do not have a
@@ -1028,6 +1051,23 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
     shash_destroy(&new_br);
 }
 
+static bool
+may_port_action(long long int *port_action_timer)
+{
+    if (need_reconfigure) {
+        return false;
+    }
+
+    time_refresh();
+    if (*port_action_timer == LLONG_MAX) {
+        *port_action_timer = time_msec() + OFP_PORT_ACTION_WINDOW;
+    } else if (time_msec() > *port_action_timer) {
+        need_reconfigure = true;
+        return false;
+    }
+    return true;
+}
+
 /* Delete each ofproto port on 'br' that doesn't have a corresponding "struct
  * iface".
  *
@@ -1035,7 +1075,8 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
  * port already belongs to a different datapath, so we must do all port
  * deletions before any port additions. */
 static void
-bridge_del_ofproto_ports(struct bridge *br)
+bridge_del_ofproto_ports(struct bridge *br,
+                         long long int *port_action_timer)
 {
     struct ofproto_port_dump dump;
     struct ofproto_port ofproto_port;
@@ -1066,10 +1107,16 @@ bridge_del_ofproto_ports(struct bridge *br)
                 || !strcmp(netdev_get_type(iface->netdev), type))) {
             continue;
         }
-        error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
-        if (error) {
-            VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
-                      br->name, name, strerror(error));
+
+        if (may_port_action(port_action_timer)) {
+            error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
+            if (error) {
+                VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
+                          br->name, name, strerror(error));
+            } else {
+                VLOG_INFO("bridge %s: removed interface %s (%d)", br->name,
+                          name, ofproto_port.ofp_port);
+            }
         }
         if (iface) {
             netdev_close(iface->netdev);
@@ -1127,7 +1174,8 @@ bridge_refresh_ofp_port(struct bridge *br)
  * Delete any "struct iface" for which this fails.
  * Delete any "struct port" that thereby ends up with no ifaces. */
 static void
-bridge_add_ofproto_ports(struct bridge *br)
+bridge_add_ofproto_ports(struct bridge *br,
+                         long long int *port_action_timer)
 {
     struct port *port, *next_port;
 
@@ -1138,6 +1186,12 @@ bridge_add_ofproto_ports(struct bridge *br)
         LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
             int error;
 
+            if (iface->ofp_port < 0 && !may_port_action(port_action_timer)) {
+                iface_clear_db_record(iface->cfg);
+                iface_destroy(iface);
+                continue;
+            }
+
             /* Open the netdev. */
             if (!iface->netdev) {
                 error = netdev_open(iface->name, iface->type, &iface->netdev);
@@ -1182,6 +1236,8 @@ bridge_add_ofproto_ports(struct bridge *br)
                 error = ofproto_port_add(br->ofproto, iface->netdev,
                                          &ofp_port);
                 if (!error) {
+                    VLOG_INFO("bridge %s: added interface %s (%d)", br->name,
+                              iface->name, ofp_port);
                     iface_set_ofp_port(iface, ofp_port);
                 } else {
                     netdev_close(iface->netdev);
@@ -1210,6 +1266,8 @@ bridge_add_ofproto_ports(struct bridge *br)
                 }
                 if (!ofproto_port_query_by_name(br->ofproto, port->name,
                                                 &ofproto_port)) {
+                    VLOG_INFO("bridge %s: removed interface %s (%d)",
+                              br->name, port->name, ofproto_port.ofp_port);
                     ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
                     ofproto_port_destroy(&ofproto_port);
                 }
@@ -1217,8 +1275,11 @@ bridge_add_ofproto_ports(struct bridge *br)
                 iface_destroy(iface);
             }
         }
+
         if (list_is_empty(&port->ifaces)) {
-            VLOG_WARN("%s port has no interfaces, dropping", port->name);
+            if (!need_reconfigure) {
+                VLOG_WARN("%s port has no interfaces, dropping", port->name);
+            }
             port_destroy(port);
             continue;
         }
@@ -1232,6 +1293,9 @@ bridge_add_ofproto_ports(struct bridge *br)
 
                 error = netdev_open(port->name, "internal", &netdev);
                 if (!error) {
+                    /* There are unlikely to be a great number of fake
+                     * interfaces so we don't bother rate limiting their
+                     * creation. */
                     ofproto_port_add(br->ofproto, netdev, NULL);
                     netdev_close(netdev);
                 } else {
@@ -1401,10 +1465,11 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
         VLOG_DBG("bridge %s: using bridge Ethernet address "ETH_ADDR_FMT,
                  br->name, ETH_ADDR_ARGS(ea));
     } else {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
         memcpy(ea, br->default_ea, ETH_ADDR_LEN);
         *hw_addr_iface = NULL;
-        VLOG_WARN("bridge %s: using default bridge Ethernet "
-                  "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
+        VLOG_WARN_RL(&rl, "bridge %s: using default bridge Ethernet "
+                     "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
     }
 
     hmapx_destroy(&mirror_output_ports);
@@ -1868,11 +1933,10 @@ bridge_run(void)
     const struct ovsrec_open_vswitch *cfg;
 
     bool vlan_splinters_changed;
-    bool database_changed;
     struct bridge *br;
 
     /* (Re)configure if necessary. */
-    database_changed = ovsdb_idl_run(idl);
+    ovsdb_idl_run(idl);
     if (ovsdb_idl_is_lock_contended(idl)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
         struct bridge *br, *next_br;
@@ -1919,7 +1983,9 @@ bridge_run(void)
         }
     }
 
-    if (database_changed || vlan_splinters_changed) {
+    if (need_reconfigure || ovsdb_idl_get_seqno(idl) != idl_seqno
+        || vlan_splinters_changed) {
+        idl_seqno = ovsdb_idl_get_seqno(idl);
         if (cfg) {
             struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
 
@@ -2023,6 +2089,11 @@ void
 bridge_wait(void)
 {
     ovsdb_idl_wait(idl);
+
+    if (need_reconfigure) {
+        poll_immediate_wake();
+    }
+
     if (!hmap_is_empty(&all_bridges)) {
         struct bridge *br;
 
@@ -2365,10 +2436,14 @@ bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
     oc->enable_async_msgs = (!c->enable_async_messages
                              || *c->enable_async_messages);
     config_str = ovsrec_controller_get_other_config_value(c, "dscp", NULL);
+
+    oc->dscp = DSCP_DEFAULT;
     if (config_str) {
-        oc->dscp = atoi(config_str);
-    } else {
-        oc->dscp = DSCP_DEFAULT;
+        int dscp = atoi(config_str);
+
+        if (dscp >= 0 && dscp <= 63) {
+            oc->dscp = dscp;
+        }
     }
 }
 
@@ -2613,9 +2688,6 @@ port_create(struct bridge *br, const struct ovsrec_port *cfg)
     list_init(&port->ifaces);
 
     hmap_insert(&br->ports, &port->hmap_node, hash_string(port->name, 0));
-
-    VLOG_INFO("created port %s on bridge %s", port->name, br->name);
-
     return port;
 }
 
@@ -2708,9 +2780,6 @@ port_destroy(struct port *port)
         }
 
         hmap_remove(&br->ports, &port->hmap_node);
-
-        VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
-
         free(port->name);
         free(port);
     }
@@ -2756,7 +2825,6 @@ static struct lacp_settings *
 port_configure_lacp(struct port *port, struct lacp_settings *s)
 {
     const char *lacp_time, *system_id;
-    long long int custom_time;
     int priority;
 
     if (!enable_lacp(port, &s->active)) {
@@ -2798,18 +2866,7 @@ port_configure_lacp(struct port *port, struct lacp_settings *s)
 
     lacp_time = ovsrec_port_get_other_config_value(port->cfg, "lacp-time",
                                                    "slow");
-    custom_time = atoi(lacp_time);
-    if (!strcmp(lacp_time, "fast")) {
-        s->lacp_time = LACP_TIME_FAST;
-    } else if (!strcmp(lacp_time, "slow")) {
-        s->lacp_time = LACP_TIME_SLOW;
-    } else if (custom_time > 0) {
-        s->lacp_time = LACP_TIME_CUSTOM;
-        s->custom_time = custom_time;
-    } else {
-        s->lacp_time = LACP_TIME_SLOW;
-    }
-
+    s->fast = !strcasecmp(lacp_time, "fast");
     return s;
 }