- /* 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 not cause 'to''s load to exceed 'from''s load.
- *
- * 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. */
- size_t i;
-
- for (i = 0; i < from->n_hashes; i++) {
- uint64_t delta = from->hashes[i]->tx_bytes;
- if (to->tx_bytes + delta < from->tx_bytes - delta) {
- break;
- }
- }
- if (i < from->n_hashes) {
- bond_shift_load(from, to, from->hashes[i]);
-
- /* 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;
- }
-}
-
-static void
-bond_send_learning_packets(struct port *port)
-{
- struct bridge *br = port->bridge;
- struct mac_entry *e;
- struct ofpbuf packet;
- int error, n_packets, n_errors;
-
- if (!port->n_ifaces || port->active_iface < 0 || !br->ml) {
- return;
- }
-
- ofpbuf_init(&packet, 128);
- error = n_packets = n_errors = 0;
- LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
- static const char s[] = "Open vSwitch Bond Failover";
- union ofp_action actions[2], *a;
- struct eth_header *eth;
- struct llc_snap_header *llc_snap;
- uint16_t dp_ifidx;
- tag_type tags = 0;
- flow_t flow;
- int retval;
-
- if (e->port == port->port_idx
- || !choose_output_iface(port, e->mac, &dp_ifidx, &tags)) {
- continue;
- }
-
- /* Compose packet to send. */
- ofpbuf_clear(&packet);
- eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN);
- llc_snap = ofpbuf_put_zeros(&packet, LLC_SNAP_HEADER_LEN);
- ofpbuf_put(&packet, s, sizeof s); /* Includes null byte. */
- ofpbuf_put(&packet, e->mac, ETH_ADDR_LEN);
-
- memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
- memcpy(eth->eth_src, e->mac, ETH_ADDR_LEN);
- eth->eth_type = htons(packet.size - ETH_HEADER_LEN);
-
- llc_snap->llc.llc_dsap = LLC_DSAP_SNAP;
- llc_snap->llc.llc_ssap = LLC_SSAP_SNAP;
- llc_snap->llc.llc_cntl = LLC_CNTL_SNAP;
- memcpy(llc_snap->snap.snap_org, "\x00\x23\x20", 3);
- llc_snap->snap.snap_type = htons(0xf177); /* Random number. */
-
- /* Compose actions. */
- memset(actions, 0, sizeof actions);
- a = actions;
- if (e->vlan) {
- a->vlan_vid.type = htons(OFPAT_SET_VLAN_VID);
- a->vlan_vid.len = htons(sizeof *a);
- a->vlan_vid.vlan_vid = htons(e->vlan);
- a++;
- }
- a->output.type = htons(OFPAT_OUTPUT);
- a->output.len = htons(sizeof *a);
- a->output.port = htons(odp_port_to_ofp_port(dp_ifidx));
- a++;
-
- /* Send packet. */
- n_packets++;
- flow_extract(&packet, ODPP_NONE, &flow);
- retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
- &packet);
- if (retval) {
- error = retval;
- n_errors++;
- }
- }
- ofpbuf_uninit(&packet);
-
- if (n_errors) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
- "packets, last error was: %s",
- port->name, n_errors, n_packets, strerror(error));