Option to forward BPDU (Ethernet control class) frames
[sliver-openvswitch.git] / vswitchd / bridge.c
index 9868ef5..d99d6a1 100644 (file)
@@ -148,7 +148,9 @@ static void bridge_add_ofproto_ports(struct bridge *);
 static void bridge_del_ofproto_ports(struct bridge *);
 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 *);
 static void bridge_configure_netflow(struct bridge *);
+static void bridge_configure_forward_bpdu(struct bridge *);
 static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
 static void bridge_configure_remotes(struct bridge *,
                                      const struct sockaddr_in *managers,
@@ -195,7 +197,9 @@ static void iface_set_mac(struct iface *);
 static void iface_set_ofport(const struct ovsrec_interface *, int64_t ofport);
 static void iface_configure_qos(struct iface *, const struct ovsrec_qos *);
 static void iface_configure_cfm(struct iface *);
-static bool iface_refresh_cfm_stats(struct iface *iface);
+static bool iface_refresh_cfm_stats(struct iface *);
+static void iface_refresh_stats(struct iface *);
+static void iface_refresh_status(struct iface *);
 static bool iface_get_carrier(const struct iface *);
 static bool iface_is_synthetic(const struct iface *);
 
@@ -214,6 +218,7 @@ bridge_init(const char *remote)
 {
     /* Create connection to database. */
     idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true);
+    ovsdb_idl_set_lock(idl, "ovs_vswitchd");
 
     ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg);
     ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_statistics);
@@ -409,6 +414,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         }
         bridge_configure_mirrors(br);
         bridge_configure_datapath_id(br);
+        bridge_configure_flow_eviction_threshold(br);
+        bridge_configure_forward_bpdu(br);
         bridge_configure_remotes(br, managers, n_managers);
         bridge_configure_netflow(br);
         bridge_configure_sflow(br, &sflow_bridge_number);
@@ -841,29 +848,36 @@ bridge_add_ofproto_ports(struct bridge *br)
         struct ofproto_port ofproto_port;
 
         LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
-            struct shash args;
             int error;
 
-            /* Open the netdev or reconfigure it. */
-            shash_init(&args);
-            shash_from_ovs_idl_map(iface->cfg->key_options,
-                                   iface->cfg->value_options,
-                                   iface->cfg->n_options, &args);
+            /* Open the netdev. */
             if (!iface->netdev) {
-                struct netdev_options options;
-                options.name = iface->name;
-                options.type = iface->type;
-                options.args = &args;
-                options.ethertype = NETDEV_ETH_TYPE_NONE;
-                error = netdev_open(&options, &iface->netdev);
+                error = netdev_open(iface->name, iface->type, &iface->netdev);
+                if (error) {
+                    VLOG_WARN("could not open network device %s (%s)",
+                              iface->name, strerror(error));
+                }
             } else {
-                error = netdev_set_config(iface->netdev, &args);
+                error = 0;
             }
-            shash_destroy(&args);
-            if (error) {
-                VLOG_WARN("could not %s network device %s (%s)",
-                          iface->netdev ? "reconfigure" : "open",
-                          iface->name, strerror(error));
+
+            /* Configure the netdev. */
+            if (iface->netdev) {
+                struct shash args;
+
+                shash_init(&args);
+                shash_from_ovs_idl_map(iface->cfg->key_options,
+                                       iface->cfg->value_options,
+                                       iface->cfg->n_options, &args);
+                error = netdev_set_config(iface->netdev, &args);
+                shash_destroy(&args);
+
+                if (error) {
+                    VLOG_WARN("could not configure network device %s (%s)",
+                              iface->name, strerror(error));
+                    netdev_close(iface->netdev);
+                    iface->netdev = NULL;
+                }
             }
 
             /* Add the port, if necessary. */
@@ -881,7 +895,13 @@ bridge_add_ofproto_ports(struct bridge *br)
                 }
             }
 
-            /* Delete the iface if  */
+            /* Populate stats columns in new Interface rows. */
+            if (iface->netdev && !iface->cfg->mtu) {
+                iface_refresh_stats(iface);
+                iface_refresh_status(iface);
+            }
+
+            /* Delete the iface if we failed. */
             if (iface->netdev && iface->ofp_port >= 0) {
                 VLOG_DBG("bridge %s: interface %s is on port %d",
                          br->name, iface->name, iface->ofp_port);
@@ -907,15 +927,10 @@ bridge_add_ofproto_ports(struct bridge *br)
         if (port_is_bond_fake_iface(port)) {
             if (ofproto_port_query_by_name(br->ofproto, port->name,
                                            &ofproto_port)) {
-                struct netdev_options options;
                 struct netdev *netdev;
                 int error;
 
-                options.name = port->name;
-                options.type = "internal";
-                options.args = NULL;
-                options.ethertype = NETDEV_ETH_TYPE_NONE;
-                error = netdev_open(&options, &netdev);
+                error = netdev_open(port->name, "internal", &netdev);
                 if (!error) {
                     ofproto_port_add(br->ofproto, netdev, NULL);
                     netdev_close(netdev);
@@ -953,6 +968,36 @@ bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key)
                                 &ovsrec_bridge_col_other_config, key);
 }
 
+/* Set Flow eviction threshold */
+static void
+bridge_configure_flow_eviction_threshold(struct bridge *br)
+{
+    const char *threshold_str;
+    unsigned threshold;
+
+    threshold_str = bridge_get_other_config(br->cfg, "flow-eviction-threshold");
+    if (threshold_str) {
+        threshold = strtoul(threshold_str, NULL, 10);
+    } else {
+        threshold = OFPROTO_FLOW_EVICTON_THRESHOLD_DEFAULT;
+    }
+    ofproto_set_flow_eviction_threshold(br->ofproto, threshold);
+}
+
+/* Set forward BPDU option. */
+static void
+bridge_configure_forward_bpdu(struct bridge *br)
+{
+    const char *forward_bpdu_str;
+    bool forward_bpdu = false;
+
+    forward_bpdu_str = bridge_get_other_config(br->cfg, "forward-bpdu");
+    if (forward_bpdu_str && !strcmp(forward_bpdu_str, "true")) {
+        forward_bpdu = true;
+    }
+    ofproto_set_forward_bpdu(br->ofproto, forward_bpdu);
+}
+
 static void
 bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
                           struct iface **hw_addr_iface)
@@ -1023,9 +1068,6 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
             /* Grab MAC. */
             error = netdev_get_etheraddr(iface->netdev, iface_ea);
             if (error) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
-                            iface->name, strerror(error));
                 continue;
             }
         }
@@ -1294,6 +1336,23 @@ iface_refresh_stats(struct iface *iface)
 #undef IFACE_STATS
 }
 
+static bool
+enable_system_stats(const struct ovsrec_open_vswitch *cfg)
+{
+    const char *enable;
+
+    /* Use other-config:enable-system-stats by preference. */
+    enable = get_ovsrec_key_value(&cfg->header_,
+                                  &ovsrec_open_vswitch_col_other_config,
+                                  "enable-statistics");
+    if (enable) {
+        return !strcmp(enable, "true");
+    }
+
+    /* Disable by default. */
+    return false;
+}
+
 static void
 refresh_system_stats(const struct ovsrec_open_vswitch *cfg)
 {
@@ -1301,7 +1360,9 @@ refresh_system_stats(const struct ovsrec_open_vswitch *cfg)
     struct shash stats;
 
     shash_init(&stats);
-    get_system_stats(&stats);
+    if (enable_system_stats(cfg)) {
+        get_system_stats(&stats);
+    }
 
     ovsdb_datum_from_shash(&datum, &stats);
     ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
@@ -1324,13 +1385,20 @@ nx_role_to_str(enum nx_role role)
 }
 
 static void
-bridge_refresh_controller_status(const struct bridge *br)
+refresh_controller_status(void)
 {
+    struct bridge *br;
     struct shash info;
     const struct ovsrec_controller *cfg;
 
-    ofproto_get_ofproto_controller_info(br->ofproto, &info);
+    shash_init(&info);
+
+    /* Accumulate status for controllers on all bridges. */
+    HMAP_FOR_EACH (br, node, &all_bridges) {
+        ofproto_get_ofproto_controller_info(br->ofproto, &info);
+    }
 
+    /* Update each controller in the database with current status. */
     OVSREC_CONTROLLER_FOR_EACH(cfg, idl) {
         struct ofproto_controller_info *cinfo =
             shash_find_data(&info, cfg->target);
@@ -1360,6 +1428,24 @@ bridge_run(void)
     bool database_changed;
     struct bridge *br;
 
+    /* (Re)configure if necessary. */
+    database_changed = 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;
+
+        VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, "
+                    "disabling this process until it goes away");
+
+        HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
+            bridge_destroy(br);
+        }
+        return;
+    } else if (!ovsdb_idl_has_lock(idl)) {
+        return;
+    }
+    cfg = ovsrec_open_vswitch_first(idl);
+
     /* Let each bridge do the work that it needs to do. */
     datapath_destroyed = false;
     HMAP_FOR_EACH (br, node, &all_bridges) {
@@ -1372,10 +1458,6 @@ bridge_run(void)
         }
     }
 
-    /* (Re)configure if necessary. */
-    database_changed = ovsdb_idl_run(idl);
-    cfg = ovsrec_open_vswitch_first(idl);
-
     /* Re-configure SSL.  We do this on every trip through the main loop,
      * instead of just when the database changes, because the contents of the
      * key and certificate files can change without the database changing.
@@ -1424,9 +1506,9 @@ bridge_run(void)
                         iface_refresh_status(iface);
                     }
                 }
-                bridge_refresh_controller_status(br);
             }
             refresh_system_stats(cfg);
+            refresh_controller_status();
             ovsdb_idl_txn_commit(txn);
             ovsdb_idl_txn_destroy(txn); /* XXX */
         }
@@ -1464,16 +1546,18 @@ bridge_run(void)
 void
 bridge_wait(void)
 {
-    struct bridge *br;
-
-    HMAP_FOR_EACH (br, node, &all_bridges) {
-        ofproto_wait(br->ofproto);
-    }
     ovsdb_idl_wait(idl);
-    poll_timer_wait_until(stats_timer);
+    if (!hmap_is_empty(&all_bridges)) {
+        struct bridge *br;
+
+        HMAP_FOR_EACH (br, node, &all_bridges) {
+            ofproto_wait(br->ofproto);
+        }
+        poll_timer_wait_until(stats_timer);
 
-    if (db_limiter > time_msec()) {
-        poll_timer_wait_until(db_limiter);
+        if (db_limiter > time_msec()) {
+            poll_timer_wait_until(db_limiter);
+        }
     }
 }
 \f
@@ -2190,6 +2274,11 @@ port_configure_bond(struct port *port, struct bond_settings *s,
                   port->name, port->cfg->bond_mode,
                   bond_mode_to_string(s->balance));
     }
+    if (s->balance == BM_SLB && port->bridge->cfg->n_flood_vlans) {
+        VLOG_WARN("port %s: SLB bonds are incompatible with flood_vlans, "
+                  "please use another bond type or disable flood_vlans",
+                  port->name);
+    }
 
     miimon_interval = atoi(get_port_other_config(port->cfg,
                                                  "bond-miimon-interval", "0"));
@@ -2443,6 +2532,7 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
     } else {
         struct iface_delete_queues_cbdata cbdata;
         struct shash details;
+        bool queue_zero;
         size_t i;
 
         /* Configure top-level Qos for 'iface'. */
@@ -2458,16 +2548,28 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
         netdev_dump_queues(iface->netdev, iface_delete_queues, &cbdata);
 
         /* Configure queues for 'iface'. */
+        queue_zero = false;
         for (i = 0; i < qos->n_queues; i++) {
             const struct ovsrec_queue *queue = qos->value_queues[i];
             unsigned int queue_id = qos->key_queues[i];
 
+            if (queue_id == 0) {
+                queue_zero = true;
+            }
+
             shash_from_ovs_idl_map(queue->key_other_config,
                                    queue->value_other_config,
                                    queue->n_other_config, &details);
             netdev_set_queue(iface->netdev, queue_id, &details);
             shash_destroy(&details);
         }
+        if (!queue_zero) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "interface %s: QoS configured without a default "
+                         "queue (queue 0).  Packets not directed to a "
+                         "correctly configured queue may be dropped.",
+                         iface->name);
+        }
     }
 
     netdev_set_policing(iface->netdev,
@@ -2687,7 +2789,6 @@ mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg)
                              &s.srcs, &s.n_srcs);
         mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port,
                              &s.dsts, &s.n_dsts);
-
     }
 
     /* Get VLAN selection. */