#include <stdlib.h>
#include <math.h>
+#include "connectivity.h"
#include "coverage.h"
#include "dynamic-string.h"
#include "flow.h"
#include "ofpbuf.h"
#include "packets.h"
#include "poll-loop.h"
+#include "seq.h"
#include "shash.h"
#include "timeval.h"
#include "unixctl.h"
VLOG_DEFINE_THIS_MODULE(bond);
+static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER;
+static struct hmap all_bonds__ = HMAP_INITIALIZER(&all_bonds__);
+static struct hmap *const all_bonds OVS_GUARDED_BY(rwlock) = &all_bonds__;
+
/* Bit-mask for hashing a flow down to a bucket.
* There are (BOND_MASK + 1) buckets. */
#define BOND_MASK 0xff
/* A bond slave, that is, one of the links comprising a bond. */
struct bond_slave {
struct hmap_node hmap_node; /* In struct bond's slaves hmap. */
+ struct list list_node; /* In struct bond's enabled_slaves list. */
struct bond *bond; /* The bond that contains this slave. */
void *aux; /* Client-provided handle for this slave. */
/* Slaves. */
struct hmap slaves;
+ /* Enabled slaves.
+ *
+ * Any reader or writer of 'enabled_slaves' must hold 'mutex'.
+ * (To prevent the bond_slave from disappearing they must also hold
+ * 'rwlock'.) */
+ struct ovs_mutex mutex OVS_ACQ_AFTER(rwlock);
+ struct list enabled_slaves OVS_GUARDED; /* Contains struct bond_slaves. */
+
/* Bonding info. */
enum bond_mode balance; /* Balancing mode, one of BM_*. */
struct bond_slave *active_slave;
long long int next_fake_iface_update; /* LLONG_MAX if disabled. */
bool lacp_fallback_ab; /* Fallback to active-backup on LACP failure. */
- atomic_int ref_cnt;
+ struct ovs_refcount ref_cnt;
};
-static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER;
-static struct hmap all_bonds__ = HMAP_INITIALIZER(&all_bonds__);
-static struct hmap *const all_bonds OVS_GUARDED_BY(rwlock) = &all_bonds__;
-
static void bond_entry_reset(struct bond *) OVS_REQ_WRLOCK(rwlock);
static struct bond_slave *bond_slave_lookup(struct bond *, const void *slave_)
OVS_REQ_RDLOCK(rwlock);
const struct flow *,
uint16_t vlan)
OVS_REQ_RDLOCK(rwlock);
+static struct bond_slave *get_enabled_slave(struct bond *)
+ OVS_REQ_RDLOCK(rwlock);
static struct bond_slave *choose_output_slave(const struct bond *,
const struct flow *,
struct flow_wildcards *,
case BM_AB:
return "active-backup";
}
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
\f
bond = xzalloc(sizeof *bond);
hmap_init(&bond->slaves);
+ list_init(&bond->enabled_slaves);
+ ovs_mutex_init(&bond->mutex);
bond->next_fake_iface_update = LLONG_MAX;
- atomic_init(&bond->ref_cnt, 1);
+ ovs_refcount_init(&bond->ref_cnt);
bond_reconfigure(bond, s);
return bond;
struct bond *bond = CONST_CAST(struct bond *, bond_);
if (bond) {
- int orig;
- atomic_add(&bond->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
+ ovs_refcount_ref(&bond->ref_cnt);
}
return bond;
}
bond_unref(struct bond *bond)
{
struct bond_slave *slave, *next_slave;
- int orig;
-
- if (!bond) {
- return;
- }
- atomic_sub(&bond->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
- if (orig != 1) {
+ if (!bond || ovs_refcount_unref(&bond->ref_cnt) != 1) {
return;
}
}
hmap_destroy(&bond->slaves);
+ ovs_mutex_destroy(&bond->mutex);
free(bond->hash);
free(bond->name);
+ ovs_refcount_destroy(&bond->ref_cnt);
free(bond);
}
/* Enable slaves based on link status and LACP feedback. */
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
bond_link_status_update(slave);
- slave->change_seq = netdev_change_seq(slave->netdev);
+ slave->change_seq = seq_read(connectivity_seq_get());
}
if (!bond->active_slave || !bond->active_slave->enabled) {
bond_choose_active_slave(bond);
poll_timer_wait_until(slave->delay_expires);
}
- if (slave->change_seq != netdev_change_seq(slave->netdev)) {
- poll_immediate_wake();
- }
+ seq_wait(connectivity_seq_get(), slave->change_seq);
}
if (bond->next_fake_iface_update != LLONG_MAX) {
packet = ofpbuf_new(0);
compose_rarp(packet, eth_src);
if (vlan) {
- eth_push_vlan(packet, htons(vlan));
+ eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
}
*port_aux = slave->aux;
goto out;
}
- NOT_REACHED();
+ OVS_NOT_REACHED();
out:
ovs_rwlock_unlock(&rwlock);
return verdict;
if (enable != slave->enabled) {
slave->bond->bond_revalidate = true;
slave->enabled = enable;
+
+ ovs_mutex_lock(&slave->bond->mutex);
+ if (enable) {
+ list_insert(&slave->bond->enabled_slaves, &slave->list_node);
+ } else {
+ list_remove(&slave->list_node);
+ }
+ ovs_mutex_unlock(&slave->bond->mutex);
+
VLOG_INFO("interface %s: %s", slave->name,
slave->enabled ? "enabled" : "disabled");
}
return &bond->hash[bond_hash(bond, flow, vlan) & BOND_MASK];
}
+/* Selects and returns an enabled slave from the 'enabled_slaves' list
+ * in a round-robin fashion. If the 'enabled_slaves' list is empty,
+ * returns NULL. */
+static struct bond_slave *
+get_enabled_slave(struct bond *bond)
+{
+ struct list *node;
+
+ ovs_mutex_lock(&bond->mutex);
+ if (list_is_empty(&bond->enabled_slaves)) {
+ ovs_mutex_unlock(&bond->mutex);
+ return NULL;
+ }
+
+ node = list_pop_front(&bond->enabled_slaves);
+ list_push_back(&bond->enabled_slaves, node);
+ ovs_mutex_unlock(&bond->mutex);
+
+ return CONTAINER_OF(node, struct bond_slave, list_node);
+}
+
static struct bond_slave *
choose_output_slave(const struct bond *bond, const struct flow *flow,
struct flow_wildcards *wc, uint16_t vlan)
}
e = lookup_bond_entry(bond, flow, vlan);
if (!e->slave || !e->slave->enabled) {
- e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves),
- struct bond_slave, hmap_node);
- if (!e->slave->enabled) {
- e->slave = bond->active_slave;
- }
+ e->slave = get_enabled_slave(CONST_CAST(struct bond*, bond));
}
return e->slave;
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
}