ovs-parse-leaks: Fix typos in manpage.
[sliver-openvswitch.git] / lib / lacp.c
index 7a7ca6e..eaf01c3 100644 (file)
@@ -48,10 +48,11 @@ struct lacp {
     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 {
@@ -61,6 +62,7 @@ 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. */
@@ -180,15 +182,16 @@ lacp_configure(struct lacp *lacp, const struct lacp_settings *s)
 
     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
@@ -207,11 +210,23 @@ lacp_process_pdu(struct lacp *lacp, const void *slave_,
                  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;
 
@@ -258,9 +273,12 @@ lacp_slave_register(struct lacp *lacp, void *slave_,
         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;
 
@@ -329,7 +347,7 @@ lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_)
 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'. */
@@ -364,15 +382,21 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu)
 
         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);
         }
     }
 }
@@ -405,6 +429,13 @@ lacp_update_attached(struct lacp *lacp)
     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;
@@ -445,10 +476,6 @@ lacp_update_attached(struct lacp *lacp)
                 slave->attached = false;
             }
         }
-    } else if (lacp->strict) {
-        HMAP_FOR_EACH (slave, node, &lacp->slaves) {
-            slave->attached = false;
-        }
     }
 }
 
@@ -488,22 +515,32 @@ slave_set_defaulted(struct slave *slave)
 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;
     }
 
@@ -519,20 +556,25 @@ slave_get_actor(struct slave *slave, struct lacp_info *actor)
         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
@@ -674,8 +716,8 @@ lacp_unixctl_show(struct unixctl_conn *conn,
     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");
@@ -692,6 +734,21 @@ lacp_unixctl_show(struct unixctl_conn *conn,
     }
     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;