From 62d1822af37a5ef414cb99ac3c171d7e650a124e Mon Sep 17 00:00:00 2001 From: t_jeang Date: Tue, 6 Jan 2009 12:06:05 +0000 Subject: [PATCH] imported patch bonding-balance-slb.patch --- drivers/net/bonding/bond_alb.c | 77 +++++++++++++++++++++++++++++--- drivers/net/bonding/bond_alb.h | 4 +- drivers/net/bonding/bond_main.c | 46 ++++++++++++++----- drivers/net/bonding/bond_sysfs.c | 2 +- drivers/net/bonding/bonding.h | 6 ++- include/linux/if_bonding.h | 1 + include/linux/if_bridge.h | 2 + net/bridge/br_if.c | 74 ++++++++++++++++++++++++++++++ 8 files changed, 190 insertions(+), 22 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 217a2eed..c6ecb4c7 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -297,7 +298,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; itx_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(&(BOND_ALB_INFO(bond).rx_hashtbl_lock)); @@ -1218,7 +1246,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; @@ -1227,7 +1255,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); @@ -1235,8 +1267,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; @@ -1279,7 +1315,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: if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) || (skb->nh.iph->daddr == ip_bcast) || @@ -1339,10 +1378,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); @@ -1365,6 +1414,7 @@ out: return 0; } +/* Route to a slave based solely on source Ethernet address. */ void bond_alb_monitor(struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); @@ -1417,6 +1467,12 @@ void bond_alb_monitor(struct bonding *bond) 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); @@ -1537,6 +1593,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; diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h index 28f2a2fd..52abc36b 100644 --- a/drivers/net/bonding/bond_alb.h +++ b/drivers/net/bonding/bond_alb.h @@ -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); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 19da415c..3f6cc714 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -116,7 +116,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); @@ -165,6 +165,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}, }; @@ -205,6 +206,8 @@ 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"; } @@ -267,7 +270,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); } @@ -1092,7 +1096,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 { @@ -1110,7 +1115,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); @@ -1415,7 +1421,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 */ @@ -1569,6 +1576,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; if ((!bond->curr_active_slave) && (new_slave->link != BOND_LINK_DOWN)) { @@ -1728,7 +1736,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), @@ -1849,7 +1858,8 @@ static int bond_release_all(struct net_device *bond_dev) bond_detach_slave(bond, slave); 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 */ @@ -2137,7 +2147,8 @@ void bond_mii_monitor(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)) { bond_alb_handle_link_change(bond, slave, BOND_LINK_DOWN); } @@ -2223,7 +2234,8 @@ void bond_mii_monitor(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)) { bond_alb_handle_link_change(bond, slave, BOND_LINK_UP); } @@ -3078,6 +3090,12 @@ static void bond_info_show_master(struct seq_file *seq) ad_info.partner_system[5]); } } + + 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) @@ -3517,13 +3535,14 @@ 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)) { struct timer_list *alb_timer = &(BOND_ALB_INFO(bond).alb_timer); /* 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; } @@ -3611,6 +3630,7 @@ static int bond_close(struct net_device *bond_dev) break; case BOND_MODE_TLB: case BOND_MODE_ALB: + case BOND_MODE_SLB: del_timer_sync(&(BOND_ALB_INFO(bond).alb_timer)); break; default: @@ -3619,7 +3639,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 */ @@ -4223,6 +4244,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; diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 8d1df373..6634d6ad 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -427,7 +427,7 @@ static ssize_t bonding_store_mode(struct class_device *cd, const char *buf, size 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; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 41aa78bf..b401426b 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -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 @@ -256,7 +257,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)) diff --git a/include/linux/if_bonding.h b/include/linux/if_bonding.h index 84598fa2..db6669f4 100644 --- a/include/linux/if_bonding.h +++ b/include/linux/if_bonding.h @@ -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 */ diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index cd7ca302..5773c60b 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -110,6 +110,8 @@ extern int (*br_should_route_hook)(struct sk_buff **pskb); 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 diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 1c76d910..0e3e4aef 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -295,6 +295,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; -- 2.39.5