-static struct ofhooks bridge_ofhooks = {
- bridge_normal_ofhook_cb,
- bridge_special_ofhook_cb,
- bridge_account_flow_ofhook_cb,
- bridge_account_checkpoint_ofhook_cb,
-};
-\f
-/* LACP functions. */
-
-static void
-lacp_process_packet(const struct ofpbuf *packet, struct iface *iface)
-{
- const struct lacp_pdu *pdu;
-
- if (!iface->port->lacp) {
- return;
- }
-
- pdu = parse_lacp_packet(packet);
- if (!pdu) {
- return;
- }
-
- 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);
- if (memcmp(&iface->lacp_actor, &pdu->partner, sizeof pdu->partner)) {
- iface->lacp_tx = 0;
- }
-
- if (memcmp(&iface->lacp_partner, &pdu->actor, sizeof pdu->actor)) {
- iface->port->lacp_need_update = true;
- iface->lacp_partner = pdu->actor;
- }
-}
-
-static void
-lacp_update_ifaces(struct port *port)
-{
- 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_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)) {
- VLOG_WARN_RL(&rl, "iface %s: Loopback detected. Interface is "
- "connected to its own bridge", iface->name);
- iface->lacp_status &= ~LACP_ATTACHED;
- continue;
- }
-
- if (iface->lacp_status & LACP_DEFAULTED) {
- continue;
- }
-
- iface_get_lacp_priority(iface, &pri);
-
- if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) {
- lead = iface;
- lead_pri = pri;
- }
- }
-
- if (!lead) {
- port->lacp &= ~LACP_NEGOTIATED;
- return;
- }
-
- port->lacp |= LACP_NEGOTIATED;
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- 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_status &= ~LACP_ATTACHED;
- }
- }
-}
-
-static bool
-lacp_iface_may_tx(const struct iface *iface)
-{
- return iface->port->lacp & LACP_ACTIVE
- || iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED);
-}
-
-static void
-lacp_run(struct bridge *br)
-{
- size_t i, j;
- struct ofpbuf packet;
-
- ofpbuf_init(&packet, ETH_HEADER_LEN + LACP_PDU_LEN);
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
-
- if (!port->lacp) {
- continue;
- }
-
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
-
- if (time_msec() > iface->lacp_rx) {
- if (iface->lacp_status & LACP_CURRENT) {
- iface_set_lacp_expired(iface);
- } else if (iface->lacp_status & LACP_EXPIRED) {
- iface_set_lacp_defaulted(iface);
- }
- }
- }
-
- if (port->lacp_need_update) {
- lacp_update_ifaces(port);
- }
-
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- uint8_t ea[ETH_ADDR_LEN];
- int error;
-
- if (time_msec() < iface->lacp_tx || !lacp_iface_may_tx(iface)) {
- continue;
- }
-
- error = netdev_get_etheraddr(iface->netdev, ea);
- if (!error) {
- iface->lacp_actor.state = iface_get_lacp_state(iface);
- compose_lacp_packet(&packet, &iface->lacp_actor,
- &iface->lacp_partner, ea);
- iface_send_packet(iface, &packet);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
- VLOG_ERR_RL(&rl, "iface %s: failed to obtain Ethernet address "
- "(%s)", iface->name, strerror(error));
- }
-
- iface->lacp_tx = time_msec() +
- (iface->lacp_partner.state & LACP_STATE_TIME
- ? LACP_FAST_TIME_TX
- : LACP_SLOW_TIME_TX);
- }
- }
- ofpbuf_uninit(&packet);
-}
-
-static void
-lacp_wait(struct bridge *br)
-{
- size_t i, j;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
-
- if (!port->lacp) {
- continue;
- }
-
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
-
- if (lacp_iface_may_tx(iface)) {
- poll_timer_wait_until(iface->lacp_tx);
- }
-
- if (iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED)) {
- poll_timer_wait_until(iface->lacp_rx);
- }
- }
- }
-}
-\f
-/* Bonding functions. */
-
-/* Statistics for a single interface on a bonded port, used for load-based
- * bond rebalancing. */
-struct slave_balance {
- struct iface *iface; /* The interface. */
- uint64_t tx_bytes; /* Sum of hashes[*]->tx_bytes. */
-
- /* All the "bond_entry"s that are assigned to this interface, in order of
- * increasing tx_bytes. */
- struct bond_entry **hashes;
- 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";
- static char *bm_tcp = "balance-tcp";
-
- switch (bm) {
- case BM_SLB: return bm_slb;
- case BM_AB: return bm_ab;
- case BM_TCP: return bm_tcp;
- }
-
- 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. */
-static int
-compare_bond_entries(const void *a_, const void *b_)
-{
- const struct bond_entry *const *ap = a_;
- const struct bond_entry *const *bp = b_;
- const struct bond_entry *a = *ap;
- const struct bond_entry *b = *bp;
- if (a->iface_idx != b->iface_idx) {
- return a->iface_idx > b->iface_idx ? 1 : -1;
- } else if (a->tx_bytes != b->tx_bytes) {
- return a->tx_bytes > b->tx_bytes ? 1 : -1;
- } else {
- return 0;
- }
-}
-
-/* Sorts slave_balances so that enabled ports come first, and otherwise in
- * *descending* order by number of bytes transmitted. */
-static int
-compare_slave_balance(const void *a_, const void *b_)
-{
- const struct slave_balance *a = a_;
- const struct slave_balance *b = b_;
- if (a->iface->enabled != b->iface->enabled) {
- return a->iface->enabled ? -1 : 1;
- } else if (a->tx_bytes != b->tx_bytes) {
- return a->tx_bytes > b->tx_bytes ? -1 : 1;
- } else {
- return 0;
- }
-}
-
-static void
-swap_bals(struct slave_balance *a, struct slave_balance *b)
-{
- struct slave_balance tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-/* Restores the 'n_bals' slave_balance structures in 'bals' to sorted order
- * given that 'p' (and only 'p') might be in the wrong location.
- *
- * This function invalidates 'p', since it might now be in a different memory
- * location. */
-static void
-resort_bals(struct slave_balance *p,
- struct slave_balance bals[], size_t n_bals)
-{
- if (n_bals > 1) {
- for (; p > bals && p->tx_bytes > p[-1].tx_bytes; p--) {
- swap_bals(p, p - 1);
- }
- for (; p < &bals[n_bals - 1] && p->tx_bytes < p[1].tx_bytes; p++) {
- swap_bals(p, p + 1);
- }
- }
-}
-
-static void
-log_bals(const struct slave_balance *bals, size_t n_bals, struct port *port)
-{
- if (VLOG_IS_DBG_ENABLED()) {
- struct ds ds = DS_EMPTY_INITIALIZER;
- const struct slave_balance *b;
-
- for (b = bals; b < bals + n_bals; b++) {
- size_t i;
-
- if (b > bals) {
- ds_put_char(&ds, ',');
- }
- ds_put_format(&ds, " %s %"PRIu64"kB",
- b->iface->name, b->tx_bytes / 1024);
-
- if (!b->iface->enabled) {
- ds_put_cstr(&ds, " (disabled)");
- }
- if (b->n_hashes > 0) {
- ds_put_cstr(&ds, " (");
- for (i = 0; i < b->n_hashes; i++) {
- const struct bond_entry *e = b->hashes[i];
- if (i > 0) {
- ds_put_cstr(&ds, " + ");
- }
- ds_put_format(&ds, "h%td: %"PRIu64"kB",
- e - port->bond_hash, e->tx_bytes / 1024);
- }
- ds_put_cstr(&ds, ")");
- }
- }
- VLOG_DBG("bond %s:%s", port->name, ds_cstr(&ds));
- ds_destroy(&ds);
- }
-}
-
-/* Shifts 'hash' from 'from' to 'to' within 'port'. */
-static void
-bond_shift_load(struct slave_balance *from, struct slave_balance *to,
- int hash_idx)
-{
- struct bond_entry *hash = from->hashes[hash_idx];
- struct port *port = from->iface->port;
- uint64_t delta = hash->tx_bytes;
-
- 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 "
- "%"PRIu64"kB load, respectively)",
- port->name, delta / 1024, hash - port->bond_hash,
- from->iface->name, to->iface->name,
- (from->tx_bytes - delta) / 1024,
- (to->tx_bytes + delta) / 1024);
-
- /* Delete element from from->hashes.
- *
- * We don't bother to add the element to to->hashes because not only would
- * it require more work, the only purpose it would be to allow that hash to
- * be migrated to another slave in this rebalancing run, and there is no
- * point in doing that. */
- if (hash_idx == 0) {
- from->hashes++;
- } else {
- memmove(from->hashes + hash_idx, from->hashes + hash_idx + 1,
- (from->n_hashes - (hash_idx + 1)) * sizeof *from->hashes);
- }
- from->n_hashes--;
-
- /* Shift load away from 'from' to 'to'. */
- from->tx_bytes -= delta;
- to->tx_bytes += delta;
-
- /* Arrange for flows to be revalidated. */
- ofproto_revalidate(port->bridge->ofproto, hash->iface_tag);
- hash->iface_idx = to->iface->port_ifidx;
- hash->iface_tag = tag_create_random();
-}
-
-static void
-bond_rebalance_port(struct port *port)
-{
- struct slave_balance *bals;
- size_t n_bals;
- struct bond_entry *hashes[BOND_MASK + 1];
- struct slave_balance *b, *from, *to;
- struct bond_entry *e;
- size_t i;
-
- assert(port->bond_mode != BM_AB);
-
- /* 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
- * loaded slave.
- *
- * The code is a bit tricky: to avoid dynamically allocating a 'hashes'
- * array for each slave_balance structure, we sort our local array of
- * hashes in order by slave, so that all of the hashes for a given slave
- * become contiguous in memory, and then we point each 'hashes' members of
- * a slave_balance structure to the start of a contiguous group. */
- n_bals = port->n_ifaces;
- bals = xmalloc(n_bals * sizeof *bals);
- for (b = bals; b < &bals[n_bals]; b++) {
- b->iface = port->ifaces[b - bals];
- b->tx_bytes = 0;
- b->hashes = NULL;
- b->n_hashes = 0;
- }
- for (i = 0; i <= BOND_MASK; i++) {
- hashes[i] = &port->bond_hash[i];
- }
- qsort(hashes, BOND_MASK + 1, sizeof *hashes, compare_bond_entries);
- for (i = 0; i <= BOND_MASK; i++) {
- e = hashes[i];
- if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
- b = &bals[e->iface_idx];
- b->tx_bytes += e->tx_bytes;
- if (!b->hashes) {
- b->hashes = &hashes[i];
- }
- b->n_hashes++;
- }
- }
- qsort(bals, n_bals, sizeof *bals, compare_slave_balance);
- log_bals(bals, n_bals, port);
-
- /* Discard slaves that aren't enabled (which were sorted to the back of the
- * array earlier). */
- while (!bals[n_bals - 1].iface->enabled) {
- n_bals--;
- if (!n_bals) {
- goto exit;
- }
- }
-
- /* Shift load from the most-loaded slaves to the least-loaded slaves. */
- to = &bals[n_bals - 1];
- for (from = bals; from < to; ) {
- uint64_t overload = from->tx_bytes - to->tx_bytes;
- if (overload < to->tx_bytes >> 5 || overload < 100000) {
- /* The extra load on 'from' (and all less-loaded slaves), compared
- * to that of 'to' (the least-loaded slave), is less than ~3%, or
- * it is less than ~1Mbps. No point in rebalancing. */
- break;
- } else if (from->n_hashes == 1) {
- /* 'from' only carries a single MAC hash, so we can't shift any
- * load away from it, even though we want to. */
- from++;
- } else {
- /* 'from' is carrying significantly more load than 'to', and that
- * load is split across at least two different hashes. Pick a hash
- * to migrate to 'to' (the least-loaded slave), given that doing so
- * must decrease the ratio of the load on the two slaves by at
- * least 0.1.
- *
- * The sort order we use means that we prefer to shift away the
- * smallest hashes instead of the biggest ones. There is little
- * reason behind this decision; we could use the opposite sort
- * order to shift away big hashes ahead of small ones. */
- bool order_swapped;
-
- for (i = 0; i < from->n_hashes; i++) {
- double old_ratio, new_ratio;
- uint64_t delta = from->hashes[i]->tx_bytes;
-
- if (delta == 0 || from->tx_bytes - delta == 0) {
- /* Pointless move. */
- continue;
- }
-
- order_swapped = from->tx_bytes - delta < to->tx_bytes + delta;
-
- if (to->tx_bytes == 0) {
- /* Nothing on the new slave, move it. */
- break;
- }
-
- old_ratio = (double)from->tx_bytes / to->tx_bytes;
- new_ratio = (double)(from->tx_bytes - delta) /
- (to->tx_bytes + delta);
-
- if (new_ratio == 0) {
- /* Should already be covered but check to prevent division
- * by zero. */
- continue;
- }
-
- if (new_ratio < 1) {
- new_ratio = 1 / new_ratio;
- }
-
- if (old_ratio - new_ratio > 0.1) {
- /* Would decrease the ratio, move it. */
- break;
- }
- }
- if (i < from->n_hashes) {
- bond_shift_load(from, to, i);
-
- /* If the result of the migration changed the relative order of
- * 'from' and 'to' swap them back to maintain invariants. */
- if (order_swapped) {
- swap_bals(from, to);
- }
-
- /* Re-sort 'bals'. Note that this may make 'from' and 'to'
- * point to different slave_balance structures. It is only
- * valid to do these two operations in a row at all because we
- * know that 'from' will not move past 'to' and vice versa. */
- resort_bals(from, bals, n_bals);
- resort_bals(to, bals, n_bals);
- } else {
- from++;
- }
- }
- }
-
- /* Implement exponentially weighted moving average. A weight of 1/2 causes
- * historical data to decay to <1% in 7 rebalancing runs. */
- for (e = &port->bond_hash[0]; e <= &port->bond_hash[BOND_MASK]; e++) {
- e->tx_bytes /= 2;
- }
-
-exit:
- free(bals);
-}
-
-static void
-bond_send_learning_packets(struct port *port)