/* 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. */
#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 *);
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 *);
{
/* 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);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids);
+ ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_health);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current);
ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids);
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;
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.
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);
}
}
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);
}
}
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. */
}
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
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".
*
* 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;
|| !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);
* 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;
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);
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);
/* We already reported a related error, don't bother
* duplicating it. */
}
+ 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);
+ }
iface_clear_db_record(iface->cfg);
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;
}
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 {
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);
int fault, error;
const uint64_t *rmps;
size_t n_rmps;
+ int health;
if (iface_is_synthetic(iface)) {
return;
} else {
ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0);
}
+
+ health = ofproto_port_get_cfm_health(iface->port->bridge->ofproto,
+ iface->ofp_port);
+ if (health >= 0) {
+ int64_t cfm_health = health;
+ ovsrec_interface_set_cfm_health(cfg, &cfm_health, 1);
+ } else {
+ ovsrec_interface_set_cfm_health(cfg, NULL, 0);
+ }
}
static 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;
}
}
- 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);
bridge_wait(void)
{
ovsdb_idl_wait(idl);
+
+ if (need_reconfigure) {
+ poll_immediate_wake();
+ }
+
if (!hmap_is_empty(&all_bridges)) {
struct bridge *br;
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;
+ }
}
}
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;
}
}
hmap_remove(&br->ports, &port->hmap_node);
-
- VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
-
free(port->name);
free(port);
}
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)) {
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;
}