]> xenbits.xensource.com Git - people/ssmith/nc2-2.6.27.bak/.git/commitdiff
patch bonding-balance-slb.patch
authorSteven Smith <ssmith@weybridge.uk.xensource.com>
Thu, 28 May 2009 10:54:19 +0000 (11:54 +0100)
committerSteven Smith <ssmith@weybridge.uk.xensource.com>
Thu, 28 May 2009 10:54:19 +0000 (11:54 +0100)
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_alb.h
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
include/linux/if_bonding.h
include/linux/if_bridge.h
net/bridge/br_if.c

index 4489e586be1d9c10dd5fc1c6f3d1119cfcd1b26b..32b52d537ab7e0f442a7e30041bec3068b4a7791 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/if_bonding.h>
+#include <linux/if_bridge.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
 #include <net/ipx.h>
@@ -305,7 +306,34 @@ static struct slave *tlb_choose_channel(struct bonding *bond, u32 hash_index, u3
        return assigned_slave;
 }
 
+/*********************** slb specific functions ***************************/
+
+void bond_info_show_slb(struct seq_file *seq)
+{
+       struct bonding *bond = seq->private;
+       struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct tlb_client_info *hash_table, *slot;
+       int i;
+
+       _lock_tx_hashtbl(bond);
+
+       hash_table = bond_info->tx_hashtbl;
+       if (hash_table == NULL)
+               goto out;
+
+       seq_puts(seq, "\nSource load balancing info:\n");
+       for (i=0; i<TLB_HASH_TABLE_SIZE; i++) {
+               slot = &hash_table[i];
+               if (slot == NULL || slot->tx_slave == NULL)
+                       continue;
+               seq_printf(seq, " [%03d] = %s\n", i, slot->tx_slave->dev->name);
+       }
+ out:
+       _unlock_tx_hashtbl(bond);
+}
+
 /*********************** rlb specific functions ***************************/
+
 static inline void _lock_rx_hashtbl(struct bonding *bond)
 {
        spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
@@ -1246,7 +1274,7 @@ unwind:
 
 /************************ exported alb funcions ************************/
 
-int bond_alb_initialize(struct bonding *bond, int rlb_enabled)
+int bond_alb_initialize(struct bonding *bond, int mode)
 {
        int res;
 
@@ -1255,7 +1283,11 @@ int bond_alb_initialize(struct bonding *bond, int rlb_enabled)
                return res;
        }
 
-       if (rlb_enabled) {
+       bond->alb_info.rlb_enabled = 0;
+       bond->alb_info.slb_enabled = 0;
+
+       switch (mode) {
+       case BOND_MODE_ALB:
                bond->alb_info.rlb_enabled = 1;
                /* initialize rlb */
                res = rlb_initialize(bond);
@@ -1263,8 +1295,12 @@ int bond_alb_initialize(struct bonding *bond, int rlb_enabled)
                        tlb_deinitialize(bond);
                        return res;
                }
-       } else {
-               bond->alb_info.rlb_enabled = 0;
+               break;
+       case BOND_MODE_SLB:
+               bond->alb_info.slb_enabled = 1;
+               break;
+       case BOND_MODE_TLB:
+               break;
        }
 
        return 0;
@@ -1307,7 +1343,10 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                goto out;
        }
 
-       switch (ntohs(skb->protocol)) {
+       if (bond_info->slb_enabled) {
+               hash_start = (char*)&(eth_data->h_source);
+               hash_size = ETH_ALEN;
+       } else switch (ntohs(skb->protocol)) {
        case ETH_P_IP: {
                const struct iphdr *iph = ip_hdr(skb);
 
@@ -1369,10 +1408,20 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                /* unbalanced or unassigned, send through primary */
                tx_slave = bond->curr_active_slave;
                bond_info->unbalanced_load += skb->len;
+               printk(KERN_ERR "No slave for %02x:%02x:%02x:%02x:%02x:%02x.\n",
+                      eth_data->h_source[0], eth_data->h_source[1],
+                      eth_data->h_source[2], eth_data->h_source[3],
+                      eth_data->h_source[4], eth_data->h_source[5]);
+               if (tx_slave) {
+                       printk(KERN_ERR "Sending via primary %s (hash_index %x, length %x)\n",
+                              tx_slave->dev->name, hash_index, hash_size);
+               } else {
+                       printk(KERN_ERR "No primary interface found\n");
+               }
        }
 
        if (tx_slave && SLAVE_IS_OK(tx_slave)) {
-               if (tx_slave != bond->curr_active_slave) {
+               if (!bond->alb_info.slb_enabled && tx_slave != bond->curr_active_slave) {
                        memcpy(eth_data->h_source,
                               tx_slave->dev->dev_addr,
                               ETH_ALEN);
@@ -1395,6 +1444,7 @@ out:
        return 0;
 }
 
+/* Route to a slave based solely on source Ethernet address. */
 void bond_alb_monitor(struct work_struct *work)
 {
        struct bonding *bond = container_of(work, struct bonding,
@@ -1449,6 +1499,12 @@ void bond_alb_monitor(struct work_struct *work)
                                                BOND_TLB_REBALANCE_INTERVAL;
                                bond_info->unbalanced_load = 0;
                        }
+                       /*
+                        * No need for ARP in the SLB case since the
+                        * RX path remains valid, although we may
+                        * shortly be choosing a different TX path
+                        * which will cause RX to change too.
+                        */
                }
 
                read_unlock(&bond->curr_slave_lock);
@@ -1575,6 +1631,15 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
                if (bond->alb_info.rlb_enabled) {
                        rlb_clear_slave(bond, slave);
                }
+               if (bond->alb_info.slb_enabled) {
+                       /*
+                        * The receive path for any MAC addresses
+                        * which were hashed to this slave has now
+                        * gone away. Send a gratuitous packet which
+                        * will cause the switch to update its tables.
+                        */
+                       br_send_gratuitous_switch_learning_packet(bond->dev);
+               }
        } else if (link == BOND_LINK_UP) {
                /* order a rebalance ASAP */
                bond_info->tx_rebalance_counter = BOND_TLB_REBALANCE_TICKS;
index 50968f8196cfa10e40cb5ba7367a6e611dda6d60..2bbb3df1e467381399c24fe55eba71e576df93c9 100644 (file)
@@ -90,6 +90,8 @@ struct alb_bond_info {
        u32                     unbalanced_load;
        int                     tx_rebalance_counter;
        int                     lp_counter;
+       /* -------- slb parameters -------- */
+       int slb_enabled;
        /* -------- rlb parameters -------- */
        int rlb_enabled;
        struct packet_type      rlb_pkt_type;
@@ -118,7 +120,7 @@ struct alb_bond_info {
        struct vlan_entry       *current_alb_vlan;
 };
 
-int bond_alb_initialize(struct bonding *bond, int rlb_enabled);
+int bond_alb_initialize(struct bonding *bond, int mode);
 void bond_alb_deinitialize(struct bonding *bond);
 int bond_alb_init_slave(struct bonding *bond, struct slave *slave);
 void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave);
index 1b9c4dc6f0cf6ed7975a526c2bf251fe3cd97180..3f3a7c1d91d95dab12e7808143afc6f820c5eb26 100644 (file)
@@ -121,7 +121,7 @@ module_param(mode, charp, 0);
 MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, "
                       "1 for active-backup, 2 for balance-xor, "
                       "3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, "
-                      "6 for balance-alb");
+                      "6 for balance-alb, 7 for balance-slb");
 module_param(primary, charp, 0);
 MODULE_PARM_DESC(primary, "Primary network device to use");
 module_param(lacp_rate, charp, 0);
@@ -172,6 +172,7 @@ struct bond_parm_tbl bond_mode_tbl[] = {
 {      "802.3ad",              BOND_MODE_8023AD},
 {      "balance-tlb",          BOND_MODE_TLB},
 {      "balance-alb",          BOND_MODE_ALB},
+{      "balance-slb",          BOND_MODE_SLB},
 {      NULL,                   -1},
 };
 
@@ -221,6 +222,8 @@ static const char *bond_mode_name(int mode)
                return "transmit load balancing";
        case BOND_MODE_ALB:
                return "adaptive load balancing";
+       case BOND_MODE_SLB:
+               return "source load balancing";
        default:
                return "unknown";
        }
@@ -283,7 +286,8 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
                        list_del(&vlan->vlan_list);
 
                        if ((bond->params.mode == BOND_MODE_TLB) ||
-                           (bond->params.mode == BOND_MODE_ALB)) {
+                           (bond->params.mode == BOND_MODE_ALB) ||
+                           (bond->params.mode == BOND_MODE_SLB)) {
                                bond_alb_clear_vlan(bond, vlan_id);
                        }
 
@@ -1165,7 +1169,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                        }
 
                        if ((bond->params.mode == BOND_MODE_TLB) ||
-                           (bond->params.mode == BOND_MODE_ALB)) {
+                           (bond->params.mode == BOND_MODE_ALB) ||
+                           (bond->params.mode == BOND_MODE_SLB)) {
                                bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
                        }
                } else {
@@ -1183,7 +1188,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
        }
 
        if ((bond->params.mode == BOND_MODE_TLB) ||
-           (bond->params.mode == BOND_MODE_ALB)) {
+           (bond->params.mode == BOND_MODE_ALB) ||
+           (bond->params.mode == BOND_MODE_SLB)) {
                bond_alb_handle_active_change(bond, new_active);
                if (old_active)
                        bond_set_slave_inactive_flags(old_active);
@@ -1537,7 +1543,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        slave_dev->priv_flags |= IFF_BONDING;
 
        if ((bond->params.mode == BOND_MODE_TLB) ||
-           (bond->params.mode == BOND_MODE_ALB)) {
+           (bond->params.mode == BOND_MODE_ALB) ||
+           (bond->params.mode == BOND_MODE_SLB)) {
                /* bond_alb_init_slave() must be called before all other stages since
                 * it might fail and we do not want to have to undo everything
                 */
@@ -1703,6 +1710,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                break;
        case BOND_MODE_TLB:
        case BOND_MODE_ALB:
+       case BOND_MODE_SLB:
                new_slave->state = BOND_STATE_ACTIVE;
                bond_set_slave_inactive_flags(new_slave);
                break;
@@ -1855,7 +1863,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 
        if ((bond->params.mode == BOND_MODE_TLB) ||
-           (bond->params.mode == BOND_MODE_ALB)) {
+           (bond->params.mode == BOND_MODE_ALB) ||
+           (bond->params.mode == BOND_MODE_SLB)) {
                /* Must be called only after the slave has been
                 * detached from the list and the curr_active_slave
                 * has been cleared (if our_slave == old_current),
@@ -2031,7 +2040,8 @@ static int bond_release_all(struct net_device *bond_dev)
                write_unlock_bh(&bond->lock);
 
                if ((bond->params.mode == BOND_MODE_TLB) ||
-                   (bond->params.mode == BOND_MODE_ALB)) {
+                   (bond->params.mode == BOND_MODE_ALB) ||
+                   (bond->params.mode == BOND_MODE_SLB)) {
                        /* must be called only after the slave
                         * has been detached from the list
                         */
@@ -2359,7 +2369,8 @@ static void bond_miimon_commit(struct bonding *bond)
                                bond_3ad_handle_link_change(slave, BOND_LINK_UP);
 
                        if ((bond->params.mode == BOND_MODE_TLB) ||
-                           (bond->params.mode == BOND_MODE_ALB))
+                           (bond->params.mode == BOND_MODE_ALB) ||
+                           (bond->params.mode == BOND_MODE_SLB))
                                bond_alb_handle_link_change(bond, slave,
                                                            BOND_LINK_UP);
 
@@ -2389,7 +2400,8 @@ static void bond_miimon_commit(struct bonding *bond)
                                                            BOND_LINK_DOWN);
 
                        if (bond->params.mode == BOND_MODE_TLB ||
-                           bond->params.mode == BOND_MODE_ALB)
+                           bond->params.mode == BOND_MODE_ALB ||
+                           bond->params.mode == BOND_MODE_SLB)
                                bond_alb_handle_link_change(bond, slave,
                                                            BOND_LINK_DOWN);
 
@@ -3306,6 +3318,12 @@ static void bond_info_show_master(struct seq_file *seq)
                                   print_mac(mac, ad_info.partner_system));
                }
        }
+
+       if (bond->params.mode == BOND_MODE_SLB)
+       {
+               extern void bond_info_show_slb(struct seq_file *seq);
+               bond_info_show_slb(seq);
+       }
 }
 
 static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave)
@@ -3760,11 +3778,12 @@ static int bond_open(struct net_device *bond_dev)
        bond->kill_timers = 0;
 
        if ((bond->params.mode == BOND_MODE_TLB) ||
-           (bond->params.mode == BOND_MODE_ALB)) {
+           (bond->params.mode == BOND_MODE_ALB) ||
+           (bond->params.mode == BOND_MODE_SLB)) {
                /* bond_alb_initialize must be called before the timer
                 * is started.
                 */
-               if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) {
+               if (bond_alb_initialize(bond, bond->params.mode)) {
                        /* something went wrong - fail the open operation */
                        return -1;
                }
@@ -3844,7 +3863,8 @@ static int bond_close(struct net_device *bond_dev)
 
 
        if ((bond->params.mode == BOND_MODE_TLB) ||
-           (bond->params.mode == BOND_MODE_ALB)) {
+           (bond->params.mode == BOND_MODE_ALB) ||
+           (bond->params.mode == BOND_MODE_SLB)) {
                /* Must be called only after all
                 * slaves have been released
                 */
@@ -4472,6 +4492,7 @@ void bond_set_mode_ops(struct bonding *bond, int mode)
        case BOND_MODE_ALB:
                bond_set_master_alb_flags(bond);
                /* FALLTHRU */
+       case BOND_MODE_SLB:
        case BOND_MODE_TLB:
                bond_dev->hard_start_xmit = bond_alb_xmit;
                bond_dev->set_mac_address = bond_alb_set_mac_address;
index 1156137f9b000f80ee5152c1facc78e846690ffc..7074fd5af5481c3a81c54482f710cd10808aed03 100644 (file)
@@ -423,7 +423,7 @@ static ssize_t bonding_store_mode(struct device *d,
                if (bond->params.mode == BOND_MODE_8023AD)
                        bond_unset_master_3ad_flags(bond);
 
-               if (bond->params.mode == BOND_MODE_ALB)
+               if (bond->params.mode == BOND_MODE_ALB || bond->params.mode == BOND_MODE_SLB)
                        bond_unset_master_alb_flags(bond);
 
                bond->params.mode = new_value;
index fb730ec0396f59b1879b5908d909e3f85a273633..e0e0797bb8bd6ea66078b16f67e4149c6dcb8873 100644 (file)
@@ -65,7 +65,8 @@
 #define USES_PRIMARY(mode)                             \
                (((mode) == BOND_MODE_ACTIVEBACKUP) ||  \
                 ((mode) == BOND_MODE_TLB)          ||  \
-                ((mode) == BOND_MODE_ALB))
+                ((mode) == BOND_MODE_ALB)          ||  \
+                ((mode) == BOND_MODE_SLB))
 
 /*
  * Less bad way to call ioctl from within the kernel; this needs to be
@@ -277,7 +278,8 @@ static inline void bond_set_slave_inactive_flags(struct slave *slave)
 {
        struct bonding *bond = slave->dev->master->priv;
        if (bond->params.mode != BOND_MODE_TLB &&
-           bond->params.mode != BOND_MODE_ALB)
+           bond->params.mode != BOND_MODE_ALB &&
+           bond->params.mode != BOND_MODE_SLB)
                slave->state = BOND_STATE_BACKUP;
        slave->dev->priv_flags |= IFF_SLAVE_INACTIVE;
        if (slave_do_arp_validate(bond, slave))
index 65c2d247068b298b769e999d99f563c14312299e..1243b59e511c84a47cf3728353efea7735e93c56 100644 (file)
@@ -70,6 +70,7 @@
 #define BOND_MODE_8023AD        4
 #define BOND_MODE_TLB           5
 #define BOND_MODE_ALB          6 /* TLB + RLB (receive load balancing) */
+#define BOND_MODE_SLB          7 /* Source load balancing. */
 
 /* each slave's link has 4 states */
 #define BOND_LINK_UP    0           /* link is up and running */
index cf3296da6bbc249502e94acd565be401803cc36b..b89ca756f0d7cb51fe986c41e1bbb9273fdf5d79 100644 (file)
@@ -111,6 +111,8 @@ extern int (*br_should_route_hook)(struct sk_buff *skb);
 
 extern struct net_device *br_locate_physical_device(struct net_device *dev);
 
+extern void br_send_gratuitous_switch_learning_packet(struct net_device *dev);
+
 #endif
 
 #endif
index 63633c5a0401143233fa4a8ca1bca29b533d3467..154b1e14f6b05252fd274d19806add8a4822ef28 100644 (file)
@@ -281,6 +281,80 @@ struct net_device *br_locate_physical_device(struct net_device *dev)
 }
 EXPORT_SYMBOL(br_locate_physical_device);
 
+static struct sk_buff *create_switch_learning_packet(struct net_device *dev, unsigned char *src_hw)
+{
+       struct sk_buff *skb;
+       unsigned char *data;
+
+       /*
+        * Xen OUI is 00-16-3E therefore multicast address is 01-16-3E.
+        * Use the first of these addresses as our destination address with protocol type 0.
+        * Include the physical interface's MAC address as the payload.
+        */
+       unsigned char dest_hw[ETH_ALEN] = {0x01, 0x16, 0x3e, 0x00, 0x00, 0x00};
+
+       skb = alloc_skb(ETH_ALEN + LL_RESERVED_SPACE(dev), GFP_ATOMIC);
+       if (skb == NULL)
+               return NULL;
+
+       skb_reserve(skb, LL_RESERVED_SPACE(dev));
+       skb->nh.raw = skb->data;
+       data = (unsigned char *) skb_put(skb, ETH_ALEN);
+
+       skb->dev = dev;
+       skb->protocol = 0;
+
+       if (dev->hard_header &&
+           dev->hard_header(skb,dev,0,&dest_hw,src_hw,skb->len) < 0)
+               goto out;
+
+       memcpy(data, dev->dev_addr, ETH_ALEN);
+
+       return skb;
+
+out:
+       kfree_skb(skb);
+       return NULL;
+}
+
+void br_send_gratuitous_switch_learning_packet(struct net_device *dev)
+{
+       struct net_bridge *br;
+       struct net_device *phys;
+       int i;
+
+       if (!dev->br_port)
+               return;
+       if (!dev->br_port->br)
+               panic("bridge port not on a bridge");
+
+       br = dev->br_port->br;
+       phys = br_locate_physical_device(br->dev);
+
+       spin_lock_bh(&br->hash_lock);
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               struct net_bridge_fdb_entry *f;
+               struct hlist_node *h, *n;
+
+               hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) {
+                       if (f->dst != phys->br_port &&
+                           f->dst->dev->addr_len == ETH_ALEN &&
+                           memcmp(&f->dst->dev->dev_addr[0], &f->addr.addr[0], ETH_ALEN) != 0) {
+                               struct sk_buff *skb;
+                               skb = create_switch_learning_packet(dev, f->addr.addr);
+
+                               if (skb == NULL)
+                                       goto out;
+
+                               dev_queue_xmit(skb);
+                       }
+               }
+       }
+out:
+       spin_unlock_bh(&br->hash_lock);
+}
+EXPORT_SYMBOL(br_send_gratuitous_switch_learning_packet);
+
 int br_add_bridge(const char *name)
 {
        struct net_device *dev;