tag_type iface_tag; /* Tag associated with iface_idx. */
};
+enum bond_mode {
+ BM_SLB, /* Source Load Balance (Default). */
+ BM_AB /* Active Backup. */
+};
+
#define MAX_MIRRORS 32
typedef uint32_t mirror_mask_t;
#define MIRROR_MASK_C(X) UINT32_C(X)
size_t n_ifaces, allocated_ifaces;
/* Bonding info. */
- struct bond_entry *bond_hash; /* An array of (BOND_MASK + 1) elements. */
+ enum bond_mode bond_mode; /* Type of the bond. BM_SLB is the default. */
int active_iface; /* Ifidx on which bcasts accepted, or -1. */
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. */
+ long long int bond_miimon_next_update; /* Time of next miimon update. */
long long int bond_next_fake_iface_update; /* Time of next update. */
+ struct netdev_monitor *monitor; /* Tracks carrier up/down status. */
+
+ /* SLB specific bonding info. */
+ struct bond_entry *bond_hash; /* An array of (BOND_MASK + 1) elements. */
int bond_rebalance_interval; /* Interval between rebalances, in ms. */
long long int bond_next_rebalance; /* Next rebalancing time. */
- struct netdev_monitor *monitor; /* Tracks carrier up/down status. */
/* Port mirroring info. */
mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */
static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
struct shash *);
+static void shash_to_ovs_idl_map(struct shash *,
+ char ***keys, char ***values, size_t *n);
+
/* Hooks into ofproto processing. */
static struct ofhooks bridge_ofhooks;
}
static void
-iface_refresh_tunnel_egress(struct iface *iface)
+iface_refresh_status(struct iface *iface)
{
- const char *name = netdev_get_tnl_iface(iface->netdev);
+ struct shash sh;
+
+ enum netdev_flags flags;
+ uint32_t current;
+ int64_t bps;
+ int mtu;
+ int64_t mtu_64;
+ int error;
+
+ shash_init(&sh);
- ovsrec_interface_set_tunnel_egress_iface(iface->cfg, name);
+ if (!netdev_get_status(iface->netdev, &sh)) {
+ size_t n;
+ char **keys, **values;
+
+ shash_to_ovs_idl_map(&sh, &keys, &values, &n);
+ ovsrec_interface_set_status(iface->cfg, keys, values, n);
+
+ free(keys);
+ free(values);
+ } else {
+ ovsrec_interface_set_status(iface->cfg, NULL, NULL, 0);
+ }
+
+ shash_destroy_free_data(&sh);
+
+ error = netdev_get_flags(iface->netdev, &flags);
+ if (!error) {
+ ovsrec_interface_set_admin_state(iface->cfg, flags & NETDEV_UP ? "up" : "down");
+ }
+ else {
+ ovsrec_interface_set_admin_state(iface->cfg, NULL);
+ }
+
+ error = netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL);
+ if (!error) {
+ ovsrec_interface_set_duplex(iface->cfg,
+ netdev_features_is_full_duplex(current)
+ ? "full" : "half");
+ /* warning: uint64_t -> int64_t conversion */
+ bps = netdev_features_to_bps(current);
+ ovsrec_interface_set_link_speed(iface->cfg, &bps, 1);
+ }
+ else {
+ ovsrec_interface_set_duplex(iface->cfg, NULL);
+ ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
+ }
+
+
+ ovsrec_interface_set_link_state(iface->cfg,
+ netdev_get_carrier(iface->netdev)
+ ? "up" : "down");
+
+ error = netdev_get_mtu(iface->netdev, &mtu);
+ if (!error) {
+ mtu_64 = mtu;
+ ovsrec_interface_set_mtu(iface->cfg, &mtu_64, 1);
+ }
+ else {
+ ovsrec_interface_set_mtu(iface->cfg, NULL, 0);
+ }
}
static void
&datum);
}
+static inline const char *
+nx_role_to_str(enum nx_role role)
+{
+ switch (role) {
+ case NX_ROLE_OTHER:
+ return "other";
+ case NX_ROLE_MASTER:
+ return "master";
+ case NX_ROLE_SLAVE:
+ return "slave";
+ default:
+ return "*** INVALID ROLE ***";
+ }
+}
+
+static void
+bridge_refresh_controller_status(const struct bridge *br)
+{
+ struct shash info;
+ const struct ovsrec_controller *cfg;
+
+ ofproto_get_ofproto_controller_info(br->ofproto, &info);
+
+ OVSREC_CONTROLLER_FOR_EACH(cfg, idl) {
+ struct ofproto_controller_info *cinfo = shash_find_data(&info, cfg->target);
+
+ ovsrec_controller_set_is_connected(cfg, cinfo->is_connected);
+ ovsrec_controller_set_role(cfg, nx_role_to_str(cinfo->role));
+ ovsrec_controller_set_status(cfg, (char **) cinfo->pairs.keys,
+ (char **) cinfo->pairs.values,
+ cinfo->pairs.n);
+ }
+
+ ofproto_free_ofproto_controller_info(&info);
+}
+
void
bridge_run(void)
{
struct iface *iface = port->ifaces[j];
iface_refresh_stats(iface);
iface_refresh_cfm_stats(iface);
- iface_refresh_tunnel_egress(iface);
+ iface_refresh_status(iface);
}
}
+ bridge_refresh_controller_status(br);
}
refresh_system_stats(cfg);
ovsdb_idl_txn_commit(txn);
lookup_bond_entry(const struct port *port, const uint8_t mac[ETH_ADDR_LEN],
uint16_t vlan)
{
+ assert(port->bond_mode == BM_SLB);
return &port->bond_hash[bond_hash(mac, vlan)];
}
assert(port->n_ifaces);
if (port->n_ifaces == 1) {
iface = port->ifaces[0];
- } else {
+ } else if (port->bond_mode == BM_AB) {
+ if (port->active_iface < 0) {
+ *tags |= port->no_ifaces_tag;
+ return false;
+ }
+ iface = port->ifaces[port->active_iface];
+ } else if (port->bond_mode == BM_SLB){
struct bond_entry *e = lookup_bond_entry(port, dl_src, vlan);
if (e->iface_idx < 0 || e->iface_idx >= port->n_ifaces
|| !port->ifaces[e->iface_idx]->enabled) {
}
*tags |= e->iface_tag;
iface = port->ifaces[e->iface_idx];
+ } else {
+ NOT_REACHED();
}
*dp_ifidx = iface->dp_ifidx;
*tags |= iface->tag; /* Currently only used for bonding. */
/* Nothing to do. */
return;
}
- VLOG_INFO_RL(&rl, "interface %s: carrier %s",
- iface->name, carrier ? "detected" : "dropped");
+ VLOG_INFO_RL(&rl, "interface %s: link state %s",
+ iface->name, carrier ? "up" : "down");
if (carrier == iface->enabled) {
iface->delay_expires = LLONG_MAX;
VLOG_INFO_RL(&rl, "interface %s: will not be %s",
if (port->n_ifaces >= 2) {
char *devname;
- /* Track carrier going up and down on interfaces. */
- while (!netdev_monitor_poll(port->monitor, &devname)) {
- struct iface *iface;
+ if (port->monitor) {
+ assert(!port->miimon);
- iface = port_lookup_iface(port, devname);
- if (iface) {
- bool carrier = netdev_get_carrier(iface->netdev);
+ /* Track carrier going up and down on interfaces. */
+ while (!netdev_monitor_poll(port->monitor, &devname)) {
+ struct iface *iface;
- bond_link_status_update(iface, carrier);
- port_update_bond_compat(port);
+ iface = port_lookup_iface(port, devname);
+ if (iface) {
+ bool up = netdev_get_carrier(iface->netdev);
+
+ bond_link_status_update(iface, up);
+ port_update_bond_compat(port);
+ }
+ free(devname);
+ }
+ } else {
+ assert(port->miimon);
+
+ if (time_msec() >= port->bond_miimon_next_update) {
+ for (j = 0; j < port->n_ifaces; j++) {
+ struct iface *iface = port->ifaces[j];
+ bool up = netdev_get_miimon(iface->netdev);
+
+ bond_link_status_update(iface, up);
+ port_update_bond_compat(port);
+ }
+ port->bond_miimon_next_update = time_msec() +
+ port->bond_miimon_interval;
}
- free(devname);
}
for (j = 0; j < port->n_ifaces; j++) {
if (port->n_ifaces < 2) {
continue;
}
- netdev_monitor_poll_wait(port->monitor);
+
+ if (port->monitor) {
+ netdev_monitor_poll_wait(port->monitor);
+ }
+
+ if (port->miimon) {
+ poll_timer_wait_until(port->bond_miimon_next_update);
+ }
+
for (j = 0; j < port->n_ifaces; j++) {
struct iface *iface = port->ifaces[j];
if (iface->delay_expires != LLONG_MAX) {
NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
if (nl_attr_type(a) == ODPAT_OUTPUT) {
struct port *out_port = port_from_dp_ifidx(br, nl_attr_get_u32(a));
- if (out_port && out_port->n_ifaces >= 2) {
+ if (out_port && out_port->n_ifaces >= 2 &&
+ out_port->bond_mode == BM_SLB) {
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 && now >= port->bond_next_rebalance) {
+ if (port->n_ifaces > 1 && port->bond_mode == BM_SLB
+ && now >= port->bond_next_rebalance) {
port->bond_next_rebalance = now + port->bond_rebalance_interval;
bond_rebalance_port(port);
}
size_t n_hashes;
};
+static const char *
+bond_mode_to_string(enum bond_mode bm) {
+ static char *bm_slb = "balance-slb";
+ static char *bm_ab = "active-backup";
+
+ switch (bm) {
+ case BM_SLB: return bm_slb;
+ case BM_AB: return bm_ab;
+ }
+
+ NOT_REACHED();
+ return NULL;
+}
+
/* Sorts pointers to pointers to bond_entries in ascending order by the
* interface to which they are assigned, and within a single interface in
* ascending order of bytes transmitted. */
struct port *port = from->iface->port;
uint64_t delta = hash->tx_bytes;
+ assert(port->bond_mode == BM_SLB);
+
VLOG_INFO("bond %s: shift %"PRIu64"kB of load (with hash %td) "
"from %s to %s (now carrying %"PRIu64"kB and "
"%"PRIu64"kB load, respectively)",
struct bond_entry *e;
size_t i;
+ assert(port->bond_mode == BM_SLB);
+
/* Sets up 'bals' to describe each of the port's interfaces, sorted in
* descending order of tx_bytes, so that bals[0] represents the most
* heavily loaded slave and bals[n_bals - 1] represents the least heavily
struct ds ds = DS_EMPTY_INITIALIZER;
const struct bridge *br;
- ds_put_cstr(&ds, "bridge\tbond\tslaves\n");
+ ds_put_cstr(&ds, "bridge\tbond\ttype\tslaves\n");
LIST_FOR_EACH (br, node, &all_bridges) {
size_t i;
if (port->n_ifaces > 1) {
size_t j;
- ds_put_format(&ds, "%s\t%s\t", br->name, port->name);
+ ds_put_format(&ds, "%s\t%s\t%s\t", br->name, port->name,
+ bond_mode_to_string(port->bond_mode));
for (j = 0; j < port->n_ifaces; j++) {
const struct iface *iface = port->ifaces[j];
if (j) {
return;
}
+ ds_put_format(&ds, "bond_mode: %s\n",
+ bond_mode_to_string(port->bond_mode));
+ ds_put_format(&ds, "bond-detect-mode: %s\n",
+ port->miimon ? "miimon" : "carrier");
+
+ if (port->miimon) {
+ ds_put_format(&ds, "bond-miimon-interval: %lld\n",
+ port->bond_miimon_interval);
+ }
+
ds_put_format(&ds, "updelay: %d ms\n", port->updelay);
ds_put_format(&ds, "downdelay: %d ms\n", port->downdelay);
- ds_put_format(&ds, "next rebalance: %lld ms\n",
- port->bond_next_rebalance - time_msec());
+
+ if (port->bond_mode == BM_SLB) {
+ ds_put_format(&ds, "next rebalance: %lld ms\n",
+ port->bond_next_rebalance - time_msec());
+ }
+
for (j = 0; j < port->n_ifaces; j++) {
const struct iface *iface = port->ifaces[j];
struct bond_entry *be;
iface->delay_expires - time_msec());
}
+ if (port->bond_mode != BM_SLB) {
+ continue;
+ }
+
/* Hashes. */
for (be = port->bond_hash; be <= &port->bond_hash[BOND_MASK]; be++) {
int hash = be - port->bond_hash;
return;
}
+ if (port->bond_mode != BM_SLB) {
+ unixctl_command_reply(conn, 501, "not an SLB bond");
+ return;
+ }
+
if (strspn(hash_s, "0123456789") == strlen(hash_s)) {
hash = atoi(hash_s) & BOND_MASK;
} else {
static void
port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
{
+ const char *detect_mode;
struct shash new_ifaces;
- long long int next_rebalance;
+ long long int next_rebalance, miimon_next_update;
unsigned long *trunks;
int vlan;
size_t i;
port->bond_next_rebalance = next_rebalance;
}
+ detect_mode = get_port_other_config(cfg, "bond-detect-mode",
+ "carrier");
+
+ if (!strcmp(detect_mode, "carrier")) {
+ port->miimon = false;
+ } else if (!strcmp(detect_mode, "miimon")) {
+ port->miimon = true;
+ } else {
+ port->miimon = false;
+ VLOG_WARN("port %s: unsupported bond-detect-mode %s, defaulting to "
+ "carrier", port->name, detect_mode);
+ }
+
+ port->bond_miimon_interval = atoi(
+ get_port_other_config(cfg, "bond-miimon-interval", "200"));
+ if (port->bond_miimon_interval < 100) {
+ port->bond_miimon_interval = 100;
+ }
+ miimon_next_update = time_msec() + port->bond_miimon_interval;
+ if (port->bond_miimon_next_update > miimon_next_update) {
+ port->bond_miimon_next_update = miimon_next_update;
+ }
+
+ if (!port->cfg->bond_mode ||
+ !strcmp(port->cfg->bond_mode, bond_mode_to_string(BM_SLB))) {
+ port->bond_mode = BM_SLB;
+ } else if (!strcmp(port->cfg->bond_mode, bond_mode_to_string(BM_AB))) {
+ port->bond_mode = BM_AB;
+ } else {
+ port->bond_mode = BM_SLB;
+ VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s",
+ port->name, port->cfg->bond_mode,
+ bond_mode_to_string(port->bond_mode));
+ }
+
/* Add new interfaces and update 'cfg' member of existing ones. */
shash_init(&new_ifaces);
for (i = 0; i < cfg->n_interfaces; i++) {
free(port->bond_hash);
port->bond_hash = NULL;
port->bond_compat_is_stale = true;
- port->bond_fake_iface = false;
}
+
+ port->bond_fake_iface = false;
} else {
size_t i;
- if (!port->bond_hash) {
+ if (port->bond_mode == BM_SLB && !port->bond_hash) {
port->bond_hash = xcalloc(BOND_MASK + 1, sizeof *port->bond_hash);
for (i = 0; i <= BOND_MASK; i++) {
struct bond_entry *e = &port->bond_hash[i];
if (port->cfg->bond_fake_iface) {
port->bond_next_fake_iface_update = time_msec();
}
+ } else if (port->bond_mode != BM_SLB) {
+ free(port->bond_hash);
+ port->bond_hash = NULL;
}
port->bond_compat_is_stale = true;
port->bond_fake_iface = port->cfg->bond_fake_iface;
- port->monitor = netdev_monitor_create();
- for (i = 0; i < port->n_ifaces; i++) {
- netdev_monitor_add(port->monitor, port->ifaces[i]->netdev);
+ if (!port->miimon) {
+ port->monitor = netdev_monitor_create();
+ for (i = 0; i < port->n_ifaces; i++) {
+ netdev_monitor_add(port->monitor, port->ifaces[i]->netdev);
+ }
}
}
}
struct compat_bond bond;
size_t i;
- if (port->n_ifaces < 2) {
+ if (port->n_ifaces < 2 || port->bond_mode != BM_SLB) {
proc_net_compat_update_bond(port->name, NULL);
return;
}
bool del_active = port->active_iface == iface->port_ifidx;
struct iface *del;
+ if (port->monitor) {
+ netdev_monitor_remove(port->monitor, iface->netdev);
+ }
+
shash_find_and_delete_assert(&br->iface_by_name, iface->name);
if (iface->dp_ifidx >= 0) {
}
}
+/* Creates 'keys' and 'values' arrays from 'shash'.
+ *
+ * Sets 'keys' and 'values' to heap allocated arrays representing the key-value
+ * pairs in 'shash'. The caller takes ownership of 'keys' and 'values'. They
+ * are populated with with strings taken directly from 'shash' and thus have
+ * the same ownership of the key-value pairs in shash.
+ */
+static void
+shash_to_ovs_idl_map(struct shash *shash,
+ char ***keys, char ***values, size_t *n)
+{
+ size_t i, count;
+ char **k, **v;
+ struct shash_node *sn;
+
+ count = shash_count(shash);
+
+ k = xmalloc(count * sizeof *k);
+ v = xmalloc(count * sizeof *v);
+
+ i = 0;
+ SHASH_FOR_EACH(sn, shash) {
+ k[i] = sn->name;
+ v[i] = sn->data;
+ i++;
+ }
+
+ *n = count;
+ *keys = k;
+ *values = v;
+}
+
struct iface_delete_queues_cbdata {
struct netdev *netdev;
const struct ovsdb_datum *queues;