struct hmap slaves; /* Slaves this LACP object controls. */
struct slave *key_slave; /* Slave whose ID will be the aggregation key. */
- bool fast; /* Fast or Slow LACP time. */
- bool strict; /* True if in strict mode. */
+ enum lacp_time lacp_time; /* Fast, Slow or Custom LACP time. */
+ long long int custom_time; /* LACP_TIME_CUSTOM transmission rate. */
bool negotiated; /* True if LACP negotiations were successful. */
bool update; /* True if lacp_update() needs to be called. */
+ bool heartbeat; /* LACP heartbeat mode. */
};
struct slave {
struct lacp *lacp; /* LACP object containing this slave. */
uint16_t port_id; /* Port ID. */
uint16_t port_priority; /* Port Priority. */
+ uint16_t key; /* Aggregation Key. 0 if default. */
char *name; /* Name of this slave. */
enum slave_status status; /* Slave status. */
if (!eth_addr_equals(lacp->sys_id, s->id)
|| lacp->sys_priority != s->priority
- || lacp->strict != s->strict) {
+ || lacp->heartbeat != s->heartbeat) {
memcpy(lacp->sys_id, s->id, ETH_ADDR_LEN);
lacp->sys_priority = s->priority;
- lacp->strict = s->strict;
+ lacp->heartbeat = s->heartbeat;
lacp->update = true;
}
lacp->active = s->active;
- lacp->fast = s->fast;
+ lacp->lacp_time = s->lacp_time;
+ lacp->custom_time = MAX(TIME_UPDATE_INTERVAL, s->custom_time);
}
/* Returns true if 'lacp' is configured in active mode, false if 'lacp' is
const struct lacp_pdu *pdu)
{
struct slave *slave = slave_lookup(lacp, slave_);
+ long long int tx_rate;
+
+ switch (lacp->lacp_time) {
+ case LACP_TIME_FAST:
+ tx_rate = LACP_FAST_TIME_TX;
+ break;
+ case LACP_TIME_SLOW:
+ tx_rate = LACP_SLOW_TIME_TX;
+ break;
+ case LACP_TIME_CUSTOM:
+ tx_rate = lacp->custom_time;
+ break;
+ default: NOT_REACHED();
+ }
slave->status = LACP_CURRENT;
- timer_set_duration(&slave->rx, (lacp->fast
- ? LACP_FAST_TIME_RX
- : LACP_SLOW_TIME_RX));
+ timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate);
slave->ntt_actor = pdu->partner;
slave->name = xstrdup(s->name);
}
- if (slave->port_id != s->id || slave->port_priority != s->priority) {
+ if (slave->port_id != s->id
+ || slave->port_priority != s->priority
+ || slave->key != s->key) {
slave->port_id = s->id;
slave->port_priority = s->priority;
+ slave->key = s->key;
lacp->update = true;
bool
lacp_slave_is_current(const struct lacp *lacp, const void *slave_)
{
- return slave_lookup(lacp, slave_)->status == LACP_CURRENT;
+ return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED;
}
/* This function should be called periodically to update 'lacp'. */
if (timer_expired(&slave->tx)
|| !info_tx_equal(&actor, &slave->ntt_actor)) {
+ long long int duration;
slave->ntt_actor = actor;
compose_lacp_pdu(&actor, &slave->partner, &pdu);
send_pdu(slave->aux, &pdu);
- timer_set_duration(&slave->tx,
- (slave->partner.state & LACP_STATE_TIME
- ? LACP_FAST_TIME_TX
- : LACP_SLOW_TIME_TX));
+ if (lacp->lacp_time == LACP_TIME_CUSTOM) {
+ duration = lacp->custom_time;
+ } else {
+ duration = (slave->partner.state & LACP_STATE_TIME
+ ? LACP_FAST_TIME_TX
+ : LACP_SLOW_TIME_TX);
+ }
+
+ timer_set_duration(&slave->tx, duration);
}
}
}
struct lacp_info lead_pri;
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+ if (lacp->heartbeat) {
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ slave->attached = slave->status != LACP_DEFAULTED;
+ }
+ return;
+ }
+
lacp->update = false;
lead = NULL;
slave->attached = false;
}
}
- } else if (lacp->strict) {
- HMAP_FOR_EACH (slave, node, &lacp->slaves) {
- slave->attached = false;
- }
}
}
static void
slave_set_expired(struct slave *slave)
{
+ struct lacp *lacp = slave->lacp;
+
slave->status = LACP_EXPIRED;
slave->partner.state |= LACP_STATE_TIME;
slave->partner.state &= ~LACP_STATE_SYNC;
- timer_set_duration(&slave->rx, LACP_FAST_TIME_RX);
+
+ /* The spec says we should wait LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX.
+ * This doesn't make sense when using custom times which can be much
+ * smaller than LACP_FAST_TIME. */
+ timer_set_duration(&slave->rx, (lacp->lacp_time == LACP_TIME_CUSTOM
+ ? lacp->custom_time
+ : LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX));
}
static void
slave_get_actor(struct slave *slave, struct lacp_info *actor)
{
+ struct lacp *lacp = slave->lacp;
+ uint16_t key;
uint8_t state = 0;
- if (slave->lacp->active) {
+ if (lacp->active) {
state |= LACP_STATE_ACT;
}
- if (slave->lacp->fast) {
+ if (lacp->lacp_time != LACP_TIME_SLOW) {
state |= LACP_STATE_TIME;
}
state |= LACP_STATE_EXP;
}
- if (hmap_count(&slave->lacp->slaves) > 1) {
+ if (lacp->heartbeat || hmap_count(&lacp->slaves) > 1) {
state |= LACP_STATE_AGG;
}
- if (slave->attached || !slave->lacp->negotiated) {
+ if (slave->attached || !lacp->negotiated) {
state |= LACP_STATE_COL | LACP_STATE_DIST;
}
+ key = lacp->key_slave->key;
+ if (!key) {
+ key = lacp->key_slave->port_id;
+ }
+
actor->state = state;
- actor->key = htons(slave->lacp->key_slave->port_id);
+ actor->key = htons(key);
actor->port_priority = htons(slave->port_priority);
actor->port_id = htons(slave->port_id);
- actor->sys_priority = htons(slave->lacp->sys_priority);
- memcpy(&actor->sys_id, slave->lacp->sys_id, ETH_ADDR_LEN);
+ actor->sys_priority = htons(lacp->sys_priority);
+ memcpy(&actor->sys_id, lacp->sys_id, ETH_ADDR_LEN);
}
/* Given 'slave', populates 'priority' with data representing its LACP link
ds_put_format(&ds, "lacp: %s\n", lacp->name);
ds_put_format(&ds, "\tstatus: %s", lacp->active ? "active" : "passive");
- if (lacp->strict) {
- ds_put_cstr(&ds, " strict");
+ if (lacp->heartbeat) {
+ ds_put_cstr(&ds, " heartbeat");
}
if (lacp->negotiated) {
ds_put_cstr(&ds, " negotiated");
}
ds_put_cstr(&ds, "\n");
+ ds_put_cstr(&ds, "\tlacp_time: ");
+ switch (lacp->lacp_time) {
+ case LACP_TIME_FAST:
+ ds_put_cstr(&ds, "fast\n");
+ break;
+ case LACP_TIME_SLOW:
+ ds_put_cstr(&ds, "slow\n");
+ break;
+ case LACP_TIME_CUSTOM:
+ ds_put_format(&ds, "custom (%lld)\n", lacp->custom_time);
+ break;
+ default:
+ ds_put_cstr(&ds, "unknown\n");
+ }
+
HMAP_FOR_EACH (slave, node, &lacp->slaves) {
char *status;
struct lacp_info actor;