#include "cfm.h"
#include "classifier.h"
#include "coverage.h"
+#include "daemon.h"
#include "dirs.h"
#include "dpif.h"
#include "dynamic-string.h"
#include "ovsdb-data.h"
#include "packets.h"
#include "poll-loop.h"
-#include "proc-net-compat.h"
#include "process.h"
#include "sha1.h"
#include "shash.h"
COVERAGE_DEFINE(bridge_flush);
COVERAGE_DEFINE(bridge_process_flow);
+COVERAGE_DEFINE(bridge_process_cfm);
+COVERAGE_DEFINE(bridge_process_lacp);
COVERAGE_DEFINE(bridge_reconfigure);
-
-enum lacp_status {
- LACP_STATUS_CURRENT, /* Partner is up to date. */
- LACP_STATUS_EXPIRED, /* Partner is out of date. Attempt to re-sync. */
- LACP_STATUS_DEFAULTED /* Partner information is unknown. */
-};
+COVERAGE_DEFINE(bridge_lacp_update);
struct dst {
uint16_t vlan;
static void dst_set_add(struct dst_set *, const struct dst *);
static void dst_set_free(struct dst_set *);
+enum lacp_status {
+ LACP_CURRENT = 0x01, /* Current State. */
+ LACP_EXPIRED = 0x02, /* Expired State. */
+ LACP_DEFAULTED = 0x04, /* Partner is defaulted. */
+ LACP_ATTACHED = 0x08, /* Attached. Interface may be choosen for flows. */
+};
+
struct iface {
/* These members are always valid. */
struct port *port; /* Containing port. */
const struct ovsrec_interface *cfg;
/* LACP information. */
- enum lacp_status lacp_status; /* LACP state machine status. */
+ enum lacp_status lacp_status; /* LACP status. */
uint16_t lacp_priority; /* LACP port priority. */
struct lacp_info lacp_actor; /* LACP actor information. */
struct lacp_info lacp_partner; /* LACP partner information. */
long long int lacp_tx; /* Next LACP message transmission time. */
long long int lacp_rx; /* Next LACP message receive time. */
- bool lacp_attached; /* Attached to its aggregator? LACP allows
- this link to be chosen for flows. */
};
#define BOND_MASK 0xff
tag_type active_iface_tag; /* Tag for bcast flows. */
tag_type no_ifaces_tag; /* Tag for flows when all ifaces disabled. */
int updelay, downdelay; /* Delay before iface goes up/down, in ms. */
- bool bond_compat_is_stale; /* Need to call port_update_bond_compat()? */
bool bond_fake_iface; /* Fake a bond interface for legacy compat? */
bool miimon; /* Use miimon instead of carrier? */
long long int bond_miimon_interval; /* Miimon status refresh interval. */
static struct iface *port_lookup_iface(const struct port *, const char *name);
static struct port *port_from_dp_ifidx(const struct bridge *,
uint16_t dp_ifidx);
-static void port_update_bond_compat(struct port *);
-static void port_update_vlan_compat(struct port *);
static void port_update_bonding(struct port *);
static void port_update_lacp(struct port *);
struct port *port = br->ports[i];
int j;
- port_update_vlan_compat(port);
port_update_bonding(port);
port_update_lacp(port);
}
free(managers);
+
+ /* ovs-vswitchd has completed initialization, so allow the process that
+ * forked us to exit successfully. */
+ daemonize_complete();
}
static const char *
if (iface->enabled) {
return i;
} else if (iface->delay_expires < next_delay_expiration
- && (iface->lacp_attached
+ && (iface->lacp_status & LACP_ATTACHED
|| !(port->lacp & LACP_NEGOTIATED))) {
best_down_slave = i;
next_delay_expiration = iface->delay_expires;
return false;
}
e->iface_tag = tag_create_random();
- ((struct port *) port)->bond_compat_is_stale = true;
}
*tags |= e->iface_tag;
iface = port->ifaces[e->iface_idx];
* They are not required to have synchronized partners because they
* have no partners at all. However, they will only be attached if
* negotiations failed on all interfaces in the bond. */
- up = iface->lacp_attached
+ up = iface->lacp_status & LACP_ATTACHED
&& (iface->lacp_partner.state & LACP_STATE_SYNC
- || iface->lacp_status == LACP_STATUS_DEFAULTED);
+ || iface->lacp_status & LACP_DEFAULTED);
}
}
moving_active_iface = false;
- port->bond_compat_is_stale = true;
}
/* Attempts to make the sum of the bond slaves' statistics appear on the fake
return;
}
- if (iface->lacp_status == LACP_STATUS_CURRENT) {
+ if (iface->lacp_status & LACP_CURRENT) {
iface_set_lacp_expired(iface);
}
iface->up = carrier;
iface->lacp_tx = 0;
- iface->port->bond_compat_is_stale = true;
}
static void
port->bond_next_fake_iface_update = time_msec() + 1000;
}
}
-
- if (port->bond_compat_is_stale) {
- port->bond_compat_is_stale = false;
- port_update_bond_compat(port);
- }
}
}
struct ofpbuf *actions, tag_type *tags,
uint16_t *nf_output_iface, void *br_)
{
- struct iface *iface;
struct bridge *br = br_;
COVERAGE_INC(bridge_process_flow);
+ return process_flow(br, flow, packet, actions, tags, nf_output_iface);
+}
+
+static bool
+bridge_special_ofhook_cb(const struct flow *flow,
+ const struct ofpbuf *packet, void *br_)
+{
+ struct iface *iface;
+ struct bridge *br = br_;
iface = iface_from_dp_ifidx(br, flow->in_port);
if (cfm_should_process_flow(flow)) {
- if (packet && iface->cfm) {
+
+ if (iface && packet && iface->cfm) {
+ COVERAGE_INC(bridge_process_cfm);
cfm_process_heartbeat(iface->cfm, packet);
}
return false;
} else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- if (packet) {
+
+ if (iface && packet) {
+ COVERAGE_INC(bridge_process_lacp);
lacp_process_packet(packet, iface);
}
return false;
}
- return process_flow(br, flow, packet, actions, tags, nf_output_iface);
+ return true;
}
static void
if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT) {
struct port *out_port = port_from_dp_ifidx(br, nl_attr_get_u32(a));
if (out_port && out_port->n_ifaces >= 2 &&
- out_port->bond_mode == BM_SLB) {
+ out_port->bond_mode != BM_AB) {
uint16_t vlan = (flow->vlan_tci
? vlan_tci_to_vid(flow->vlan_tci)
: OFP_VLAN_NONE);
now = time_msec();
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
- if (port->n_ifaces > 1 && port->bond_mode == BM_SLB
+ if (port->n_ifaces > 1 && port->bond_mode != BM_AB
&& now >= port->bond_next_rebalance) {
port->bond_next_rebalance = now + port->bond_rebalance_interval;
bond_rebalance_port(port);
static struct ofhooks bridge_ofhooks = {
bridge_normal_ofhook_cb,
+ bridge_special_ofhook_cb,
bridge_account_flow_ofhook_cb,
bridge_account_checkpoint_ofhook_cb,
};
return;
}
- iface->lacp_status = LACP_STATUS_CURRENT;
+ iface->lacp_status |= LACP_CURRENT;
+ iface->lacp_status &= ~(LACP_EXPIRED | LACP_DEFAULTED);
iface->lacp_rx = time_msec() + LACP_SLOW_TIME_RX;
iface->lacp_actor.state = iface_get_lacp_state(iface);
size_t i;
struct iface *lead;
struct lacp_info lead_pri;
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
port->lacp_need_update = false;
+ COVERAGE_INC(bridge_lacp_update);
if (!port->lacp) {
return;
}
+ VLOG_DBG_RL(&rl, "port %s: re-evaluating LACP link status", port->name);
+
lead = NULL;
for (i = 0; i < port->n_ifaces; i++) {
struct iface *iface = port->ifaces[i];
struct lacp_info pri;
- iface->lacp_attached = true;
+ iface->lacp_status |= LACP_ATTACHED;
ofproto_revalidate(port->bridge->ofproto, iface->tag);
/* Don't allow loopback interfaces to send traffic or lead. */
if (eth_addr_equals(iface->lacp_partner.sysid,
iface->lacp_actor.sysid)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
VLOG_WARN_RL(&rl, "iface %s: Loopback detected. Interface is "
"connected to its own bridge", iface->name);
- iface->lacp_attached = false;
+ iface->lacp_status &= ~LACP_ATTACHED;
continue;
}
- if (iface->lacp_status == LACP_STATUS_DEFAULTED) {
+ if (iface->lacp_status & LACP_DEFAULTED) {
continue;
}
for (i = 0; i < port->n_ifaces; i++) {
struct iface *iface = port->ifaces[i];
- if (iface->lacp_status == LACP_STATUS_DEFAULTED
+ if (iface->lacp_status & LACP_DEFAULTED
|| lead->lacp_partner.key != iface->lacp_partner.key
|| !eth_addr_equals(lead->lacp_partner.sysid,
iface->lacp_partner.sysid)) {
- iface->lacp_attached = false;
+ iface->lacp_status &= ~LACP_ATTACHED;
}
}
}
lacp_iface_may_tx(const struct iface *iface)
{
return iface->port->lacp & LACP_ACTIVE
- || iface->lacp_status != LACP_STATUS_DEFAULTED;
+ || iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED);
}
static void
struct iface *iface = port->ifaces[j];
if (time_msec() > iface->lacp_rx) {
- if (iface->lacp_status == LACP_STATUS_CURRENT) {
+ if (iface->lacp_status & LACP_CURRENT) {
iface_set_lacp_expired(iface);
- } else if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+ } else if (iface->lacp_status & LACP_EXPIRED) {
iface_set_lacp_defaulted(iface);
}
}
poll_timer_wait_until(iface->lacp_tx);
}
- if (iface->lacp_status != LACP_STATUS_DEFAULTED) {
+ if (iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED)) {
poll_timer_wait_until(iface->lacp_rx);
}
}
struct port *port = from->iface->port;
uint64_t delta = hash->tx_bytes;
- assert(port->bond_mode == BM_SLB);
+ assert(port->bond_mode != BM_AB);
VLOG_INFO("bond %s: shift %"PRIu64"kB of load (with hash %td) "
"from %s to %s (now carrying %"PRIu64"kB and "
}
if (i < from->n_hashes) {
bond_shift_load(from, to, i);
- port->bond_compat_is_stale = true;
/* If the result of the migration changed the relative order of
* 'from' and 'to' swap them back to maintain invariants. */
bond_mode_to_string(port->bond_mode));
if (port->lacp) {
- ds_put_format(&ds, "\tlacp: %s\n",
+ ds_put_format(&ds, "lacp: %s\n",
port->lacp & LACP_ACTIVE ? "active" : "passive");
} else {
- ds_put_cstr(&ds, "\tlacp: off\n");
+ ds_put_cstr(&ds, "lacp: off\n");
}
if (port->bond_mode != BM_AB) {
struct flow flow;
/* Basic info. */
- ds_put_format(&ds, "slave %s: %s\n",
+ ds_put_format(&ds, "\nslave %s: %s\n",
iface->name, iface->enabled ? "enabled" : "disabled");
if (j == port->active_iface) {
ds_put_cstr(&ds, "\tactive slave\n");
if (port->lacp) {
ds_put_cstr(&ds, "\tstatus: ");
- if (iface->lacp_status == LACP_STATUS_CURRENT) {
+ if (iface->lacp_status & LACP_CURRENT) {
ds_put_cstr(&ds, "current ");
- } else if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+ }
+
+ if (iface->lacp_status & LACP_EXPIRED) {
ds_put_cstr(&ds, "expired ");
- } else {
+ }
+
+ if (iface->lacp_status & LACP_DEFAULTED) {
ds_put_cstr(&ds, "defaulted ");
}
- if (iface->lacp_attached) {
+ if (iface->lacp_status & LACP_ATTACHED) {
ds_put_cstr(&ds, "attached ");
}
ds_put_cstr(&ds, "\tpartner state: ");
ds_put_lacp_state(&ds, iface->lacp_partner.state);
- ds_put_cstr(&ds, "\n\n");
+ ds_put_cstr(&ds, "\n");
}
if (port->bond_mode == BM_AB) {
ofproto_revalidate(port->bridge->ofproto, entry->iface_tag);
entry->iface_idx = iface->port_ifidx;
entry->iface_tag = tag_create_random();
- port->bond_compat_is_stale = true;
unixctl_command_reply(conn, 200, "migrated");
}
struct port *del;
int i;
- proc_net_compat_update_vlan(port->name, NULL, 0);
- proc_net_compat_update_bond(port->name, NULL);
-
for (i = 0; i < MAX_MIRRORS; i++) {
struct mirror *m = br->mirrors[i];
if (m && m->out_port == port) {
bool key_changed;
if (!port->lacp || port->n_ifaces < 1) {
+ for (i = 0; i < port->n_ifaces; i++) {
+ iface_set_lacp_defaulted(port->ifaces[i]);
+ }
return;
}
}
if (port->n_ifaces < 2) {
/* Not a bonded port. */
- if (port->bond_hash) {
- free(port->bond_hash);
- port->bond_hash = NULL;
- port->bond_compat_is_stale = true;
- }
-
+ free(port->bond_hash);
+ port->bond_hash = NULL;
port->bond_fake_iface = false;
} else {
size_t i;
free(port->bond_hash);
port->bond_hash = NULL;
}
- port->bond_compat_is_stale = true;
port->bond_fake_iface = port->cfg->bond_fake_iface;
if (!port->miimon) {
}
}
}
-
-static void
-port_update_bond_compat(struct port *port)
-{
- struct compat_bond_hash compat_hashes[BOND_MASK + 1];
- struct compat_bond bond;
- size_t i;
-
- if (port->n_ifaces < 2 || port->bond_mode != BM_SLB) {
- proc_net_compat_update_bond(port->name, NULL);
- return;
- }
-
- bond.up = false;
- bond.updelay = port->updelay;
- bond.downdelay = port->downdelay;
-
- bond.n_hashes = 0;
- bond.hashes = compat_hashes;
- if (port->bond_hash) {
- const struct bond_entry *e;
- for (e = port->bond_hash; e <= &port->bond_hash[BOND_MASK]; e++) {
- if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
- struct compat_bond_hash *cbh = &bond.hashes[bond.n_hashes++];
- cbh->hash = e - port->bond_hash;
- cbh->netdev_name = port->ifaces[e->iface_idx]->name;
- }
- }
- }
-
- bond.n_slaves = port->n_ifaces;
- bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves);
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
- struct compat_bond_slave *slave = &bond.slaves[i];
- slave->name = iface->name;
-
- /* We need to make the same determination as the Linux bonding
- * code to determine whether a slave should be consider "up".
- * The Linux function bond_miimon_inspect() supports four
- * BOND_LINK_* states:
- *
- * - BOND_LINK_UP: carrier detected, updelay has passed.
- * - BOND_LINK_FAIL: carrier lost, downdelay in progress.
- * - BOND_LINK_DOWN: carrier lost, downdelay has passed.
- * - BOND_LINK_BACK: carrier detected, updelay in progress.
- *
- * The function bond_info_show_slave() only considers BOND_LINK_UP
- * to be "up" and anything else to be "down".
- */
- slave->up = iface->enabled && iface->delay_expires == LLONG_MAX;
- if (slave->up) {
- bond.up = true;
- }
- netdev_get_etheraddr(iface->netdev, slave->mac);
- }
-
- if (port->bond_fake_iface) {
- struct netdev *bond_netdev;
-
- if (!netdev_open_default(port->name, &bond_netdev)) {
- if (bond.up) {
- netdev_turn_flags_on(bond_netdev, NETDEV_UP, true);
- } else {
- netdev_turn_flags_off(bond_netdev, NETDEV_UP, true);
- }
- netdev_close(bond_netdev);
- }
- }
-
- proc_net_compat_update_bond(port->name, &bond);
- free(bond.slaves);
-}
-
-static void
-port_update_vlan_compat(struct port *port)
-{
- struct bridge *br = port->bridge;
- char *vlandev_name = NULL;
-
- if (port->vlan > 0) {
- /* Figure out the name that the VLAN device should actually have, if it
- * existed. This takes some work because the VLAN device would not
- * have port->name in its name; rather, it would have the trunk port's
- * name, and 'port' would be attached to a bridge that also had the
- * VLAN device one of its ports. So we need to find a trunk port that
- * includes port->vlan.
- *
- * There might be more than one candidate. This doesn't happen on
- * XenServer, so if it happens we just pick the first choice in
- * alphabetical order instead of creating multiple VLAN devices. */
- size_t i;
- for (i = 0; i < br->n_ports; i++) {
- struct port *p = br->ports[i];
- if (port_trunks_vlan(p, port->vlan)
- && p->n_ifaces
- && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
- {
- uint8_t ea[ETH_ADDR_LEN];
- netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
- if (!eth_addr_is_multicast(ea) &&
- !eth_addr_is_reserved(ea) &&
- !eth_addr_is_zero(ea)) {
- vlandev_name = p->name;
- }
- }
- }
- }
- proc_net_compat_update_vlan(port->name, vlandev_name, port->vlan);
-}
\f
/* Interface functions. */
static void
iface_set_lacp_defaulted(struct iface *iface)
{
- memset(&iface->lacp_partner, 0xff, sizeof iface->lacp_partner);
- iface->lacp_partner.state = 0;
+ memset(&iface->lacp_partner, 0, sizeof iface->lacp_partner);
- iface->lacp_status = LACP_STATUS_DEFAULTED;
+ iface->lacp_status |= LACP_DEFAULTED;
+ iface->lacp_status &= ~(LACP_CURRENT | LACP_EXPIRED);
iface->lacp_tx = 0;
iface->port->lacp_need_update = true;
}
static void
iface_set_lacp_expired(struct iface *iface)
{
- iface->lacp_status = LACP_STATUS_EXPIRED;
+ iface->lacp_status &= ~LACP_CURRENT;
+ iface->lacp_status |= LACP_EXPIRED;
iface->lacp_partner.state |= LACP_STATE_TIME;
iface->lacp_partner.state &= ~LACP_STATE_SYNC;
state |= LACP_STATE_ACT;
}
- if (iface->lacp_status == LACP_STATUS_DEFAULTED) {
- state |= LACP_STATE_DEF;
- } else if (iface->lacp_attached) {
+ if (iface->lacp_status & LACP_ATTACHED) {
state |= LACP_STATE_SYNC;
}
- if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+ if (iface->lacp_status & LACP_DEFAULTED) {
+ state |= LACP_STATE_DEF;
+ }
+
+ if (iface->lacp_status & LACP_EXPIRED) {
state |= LACP_STATE_EXP;
}