Commit 858f0eb3 authored by Shmulik Hen's avatar Shmulik Hen Committed by Jeff Garzik

[PATCH] bonding cleanup 2.6 - Code re-org

Code re-organization in bond_main.c according to context
(e.g. module initialization, bond initialization, device
entry points, monitoring, etc).
parent 8586007c
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
* - Send LACPDU as highest priority packet to further fix the above * - Send LACPDU as highest priority packet to further fix the above
* problem on very high Tx traffic load where packets may get dropped * problem on very high Tx traffic load where packets may get dropped
* by the slave. * by the slave.
*
* 2003/09/24 - Shmulik Hen <shmulik.hen at intel dot com>
* - Code cleanup and style changes
*/ */
//#define BONDING_DEBUG 1 //#define BONDING_DEBUG 1
...@@ -2416,6 +2419,8 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) ...@@ -2416,6 +2419,8 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
skb->dev = slave->dev; skb->dev = slave->dev;
skb->priority = 1; skb->priority = 1;
dev_queue_xmit(skb); dev_queue_xmit(skb);
goto out;
} }
} }
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
* 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com> * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
* - Renamed bond_3ad_link_status_changed() to * - Renamed bond_3ad_link_status_changed() to
* bond_3ad_handle_link_change() for compatibility with TLB. * bond_3ad_handle_link_change() for compatibility with TLB.
*
* 2003/09/24 - Shmulik Hen <shmulik.hen at intel dot com>
* - Code cleanup and style changes
*/ */
#ifndef __BOND_3AD_H__ #ifndef __BOND_3AD_H__
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
* 2003/08/06 - Amir Noam <amir.noam at intel dot com> * 2003/08/06 - Amir Noam <amir.noam at intel dot com>
* - Add support for setting bond's MAC address with special * - Add support for setting bond's MAC address with special
* handling required for ALB/TLB. * handling required for ALB/TLB.
*
* 2003/09/24 - Shmulik Hen <shmulik.hen at intel dot com>
* - Code cleanup and style changes
*/ */
//#define BONDING_DEBUG 1 //#define BONDING_DEBUG 1
...@@ -52,11 +55,11 @@ ...@@ -52,11 +55,11 @@
#define ALB_TIMER_TICKS_PER_SEC 10 /* should be a divisor of HZ */ #define ALB_TIMER_TICKS_PER_SEC 10 /* should be a divisor of HZ */
#define BOND_TLB_REBALANCE_INTERVAL 10 /* in seconds, periodic re-balancing #define BOND_TLB_REBALANCE_INTERVAL 10 /* In seconds, periodic re-balancing.
* used for division - never set * Used for division - never set
* to zero !!! * to zero !!!
*/ */
#define BOND_ALB_LP_INTERVAL 1 /* in seconds periodic send of #define BOND_ALB_LP_INTERVAL 1 /* In seconds, periodic send of
* learning packets to the switch * learning packets to the switch
*/ */
...@@ -68,7 +71,7 @@ ...@@ -68,7 +71,7 @@
#define TLB_HASH_TABLE_SIZE 256 /* The size of the clients hash table. #define TLB_HASH_TABLE_SIZE 256 /* The size of the clients hash table.
* Note that this value MUST NOT be smaller * Note that this value MUST NOT be smaller
* because the key hash table BYTE wide ! * because the key hash table is BYTE wide !
*/ */
...@@ -380,15 +383,18 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct ...@@ -380,15 +383,18 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct
static struct slave *rlb_next_rx_slave(struct bonding *bond) static struct slave *rlb_next_rx_slave(struct bonding *bond)
{ {
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
struct slave *rx_slave = NULL, *slave; struct slave *rx_slave, *slave, *start_at;
int i = 0; int i = 0;
slave = bond_info->next_rx_slave; if (bond_info->next_rx_slave) {
if (!slave) { start_at = bond_info->next_rx_slave;
slave = bond->first_slave; } else {
start_at = bond->first_slave;
} }
bond_for_each_slave(bond, slave, i) { rx_slave = NULL;
bond_for_each_slave_from(bond, slave, i, start_at) {
if (SLAVE_IS_OK(slave)) { if (SLAVE_IS_OK(slave)) {
if (!rx_slave) { if (!rx_slave) {
rx_slave = slave; rx_slave = slave;
...@@ -928,10 +934,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct ...@@ -928,10 +934,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct
*/ */
static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *slave) static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *slave)
{ {
struct slave *tmp_slave;
int perm_curr_diff; int perm_curr_diff;
int perm_bond_diff; int perm_bond_diff;
int i, found = 0;
perm_curr_diff = memcmp(slave->perm_hwaddr, perm_curr_diff = memcmp(slave->perm_hwaddr,
slave->dev->dev_addr, slave->dev->dev_addr,
...@@ -939,7 +943,11 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla ...@@ -939,7 +943,11 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
perm_bond_diff = memcmp(slave->perm_hwaddr, perm_bond_diff = memcmp(slave->perm_hwaddr,
bond->dev->dev_addr, bond->dev->dev_addr,
ETH_ALEN); ETH_ALEN);
if (perm_curr_diff && perm_bond_diff) { if (perm_curr_diff && perm_bond_diff) {
struct slave *tmp_slave;
int i, found = 0;
bond_for_each_slave(bond, tmp_slave, i) { bond_for_each_slave(bond, tmp_slave, i) {
if (!memcmp(slave->perm_hwaddr, if (!memcmp(slave->perm_hwaddr,
tmp_slave->dev->dev_addr, tmp_slave->dev->dev_addr,
...@@ -1017,8 +1025,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav ...@@ -1017,8 +1025,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
return 0; return 0;
} }
/* the slave's address is equal to the address of the bond /* The slave's address is equal to the address of the bond.
* search for a spare address in the bond for this slave. * Search for a spare address in the bond for this slave.
*/ */
free_mac_slave = NULL; free_mac_slave = NULL;
...@@ -1469,7 +1477,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char ...@@ -1469,7 +1477,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave) void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave)
{ {
struct slave *swap_slave; struct slave *swap_slave;
int i, found = 0; int i;
if (bond->curr_active_slave == new_slave) { if (bond->curr_active_slave == new_slave) {
return; return;
...@@ -1492,18 +1500,19 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave ...@@ -1492,18 +1500,19 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
* i.e. swap mac addresses of old curr_active_slave and new curr_active_slave * i.e. swap mac addresses of old curr_active_slave and new curr_active_slave
*/ */
if (!swap_slave) { if (!swap_slave) {
struct slave *tmp_slave;
/* find slave that is holding the bond's mac address */ /* find slave that is holding the bond's mac address */
bond_for_each_slave(bond, swap_slave, i) { bond_for_each_slave(bond, tmp_slave, i) {
if (!memcmp(swap_slave->dev->dev_addr, if (!memcmp(tmp_slave->dev->dev_addr,
bond->dev->dev_addr, ETH_ALEN)) { bond->dev->dev_addr, ETH_ALEN)) {
found = 1; swap_slave = tmp_slave;
break; break;
} }
} }
} }
/* curr_active_slave must be set before calling alb_swap_mac_addr */ /* curr_active_slave must be set before calling alb_swap_mac_addr */
if (found) { if (swap_slave) {
/* swap mac address */ /* swap mac address */
alb_swap_mac_addr(bond, swap_slave, new_slave); alb_swap_mac_addr(bond, swap_slave, new_slave);
} else { } else {
...@@ -1519,9 +1528,9 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) ...@@ -1519,9 +1528,9 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct sockaddr *sa = addr; struct sockaddr *sa = addr;
struct slave *swap_slave; struct slave *slave, *swap_slave;
int res; int res;
int i, found = 0; int i;
if (!is_valid_ether_addr(sa->sa_data)) { if (!is_valid_ether_addr(sa->sa_data)) {
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
...@@ -1542,14 +1551,16 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) ...@@ -1542,14 +1551,16 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
return 0; return 0;
} }
bond_for_each_slave(bond, swap_slave, i) { swap_slave = NULL;
if (!memcmp(swap_slave->dev->dev_addr, bond_dev->dev_addr, ETH_ALEN)) {
found = 1; bond_for_each_slave(bond, slave, i) {
if (!memcmp(slave->dev->dev_addr, bond_dev->dev_addr, ETH_ALEN)) {
swap_slave = slave;
break; break;
} }
} }
if (found) { if (swap_slave) {
alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave);
} else { } else {
alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr, alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr,
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
* 2003/08/06 - Amir Noam <amir.noam at intel dot com> * 2003/08/06 - Amir Noam <amir.noam at intel dot com>
* - Add support for setting bond's MAC address with special * - Add support for setting bond's MAC address with special
* handling required for ALB/TLB. * handling required for ALB/TLB.
*
* 2003/09/24 - Shmulik Hen <shmulik.hen at intel dot com>
* - Code cleanup and style changes
*/ */
#ifndef __BOND_ALB_H__ #ifndef __BOND_ALB_H__
......
...@@ -426,6 +426,32 @@ ...@@ -426,6 +426,32 @@
* - Convert /proc to seq_file interface. * - Convert /proc to seq_file interface.
* Change /proc/net/bondX/info to /proc/net/bonding/bondX. * Change /proc/net/bondX/info to /proc/net/bonding/bondX.
* Set version to 2.4.1. * Set version to 2.4.1.
*
* 2003/11/20 - Amir Noam <amir.noam at intel dot com>
* - Fix /proc creation/destruction.
*
* 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
* - Massive cleanup - Set version to 2.5.0
* Code changes:
* o Consolidate format of prints and debug prints.
* o Remove bonding_t/slave_t typedefs and consolidate all casts.
* o Remove dead code and unnecessary checks.
* o Consolidate starting/stopping timers.
* o Consolidate handling of primary module param throughout the code.
* o Removed multicast module param support - all settings are done
* according to mode.
* o Slave list iteration - bond is no longer part of the list,
* added cyclic list iteration macros.
* o Consolidate error handling in all xmit functions.
* Style changes:
* o Consolidate function naming and declarations.
* o Consolidate function params and local variables names.
* o Consolidate return values.
* o Consolidate curly braces.
* o Consolidate conditionals format.
* o Change struct member names and types.
* o Chomp trailing spaces, remove empty lines, fix indentations.
* o Re-organize code according to context.
*/ */
//#define BONDING_DEBUG 1 //#define BONDING_DEBUG 1
...@@ -454,7 +480,6 @@ ...@@ -454,7 +480,6 @@
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
...@@ -463,51 +488,71 @@ ...@@ -463,51 +488,71 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/if_bonding.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <net/arp.h> #include <net/arp.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/if_bonding.h>
#include "bonding.h" #include "bonding.h"
#include "bond_3ad.h" #include "bond_3ad.h"
#include "bond_alb.h" #include "bond_alb.h"
static const char *version = /*---------------------------- Module parameters ----------------------------*/
DRV_NAME ".c:v" DRV_VERSION " (" DRV_RELDATE ")\n";
/* monitor all links that often (in milliseconds). <=0 disables monitoring */ /* monitor all links that often (in milliseconds). <=0 disables monitoring */
#ifndef BOND_LINK_MON_INTERV
#define BOND_LINK_MON_INTERV 0 #define BOND_LINK_MON_INTERV 0
#endif
#ifndef BOND_LINK_ARP_INTERV
#define BOND_LINK_ARP_INTERV 0 #define BOND_LINK_ARP_INTERV 0
#endif
#ifndef MAX_ARP_IP_TARGETS
#define MAX_ARP_IP_TARGETS 16 #define MAX_ARP_IP_TARGETS 16
#endif
#define USES_PRIMARY(mode) \
(((mode) == BOND_MODE_ACTIVEBACKUP) || \
((mode) == BOND_MODE_TLB) || \
((mode) == BOND_MODE_ALB))
struct bond_parm_tbl {
char *modename;
int mode;
};
static int max_bonds = BOND_DEFAULT_MAX_BONDS;
static int miimon = BOND_LINK_MON_INTERV;
static int updelay = 0;
static int downdelay = 0;
static int use_carrier = 1;
static char *mode = NULL;
static char *primary = NULL;
static char *lacp_rate = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV; static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[MAX_ARP_IP_TARGETS] = { NULL, }; static char *arp_ip_target[MAX_ARP_IP_TARGETS] = { NULL, };
MODULE_PARM(max_bonds, "i");
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
MODULE_PARM(miimon, "i");
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
MODULE_PARM(updelay, "i");
MODULE_PARM_DESC(updelay, "Delay before considering link up, in milliseconds");
MODULE_PARM(downdelay, "i");
MODULE_PARM_DESC(downdelay, "Delay before considering link down, in milliseconds");
MODULE_PARM(use_carrier, "i");
MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; 0 for off, 1 for on (default)");
MODULE_PARM(mode, "s");
MODULE_PARM_DESC(mode, "Mode of operation : 0 for round robin, 1 for active-backup, 2 for xor");
MODULE_PARM(primary, "s");
MODULE_PARM_DESC(primary, "Primary network device to use");
MODULE_PARM(lacp_rate, "s");
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner (slow/fast)");
MODULE_PARM(arp_interval, "i");
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
MODULE_PARM(arp_ip_target, "1-" __MODULE_STRING(MAX_ARP_IP_TARGETS) "s");
MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
/*----------------------------- Global variables ----------------------------*/
static const char *version =
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
static LIST_HEAD(bond_dev_list);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *bond_proc_dir = NULL;
#endif
static u32 arp_target[MAX_ARP_IP_TARGETS] = { 0, } ; static u32 arp_target[MAX_ARP_IP_TARGETS] = { 0, } ;
static int arp_ip_count = 0; static int arp_ip_count = 0;
static u32 my_ip = 0; static u32 my_ip = 0;
static int bond_mode = BOND_MODE_ROUNDROBIN;
static char *primary= NULL; static int lacp_fast = 0;
static int app_abi_ver = 0; static int app_abi_ver = 0;
static int orig_app_abi_ver = -1; /* This is used to save the first ABI version static int orig_app_abi_ver = -1; /* This is used to save the first ABI version
* we receive from the application. Once set, * we receive from the application. Once set,
...@@ -517,14 +562,16 @@ static int orig_app_abi_ver = -1; /* This is used to save the first ABI version ...@@ -517,14 +562,16 @@ static int orig_app_abi_ver = -1; /* This is used to save the first ABI version
* another ABI version. * another ABI version.
*/ */
static int max_bonds = BOND_DEFAULT_MAX_BONDS; struct bond_parm_tbl {
static int miimon = BOND_LINK_MON_INTERV; char *modename;
static int use_carrier = 1; int mode;
static int bond_mode = BOND_MODE_ROUNDROBIN; };
static int updelay = 0;
static int downdelay = 0;
static char *mode = NULL; static struct bond_parm_tbl bond_lacp_tbl[] = {
{ "slow", AD_LACP_SLOW},
{ "fast", AD_LACP_FAST},
{ NULL, -1},
};
static struct bond_parm_tbl bond_mode_tbl[] = { static struct bond_parm_tbl bond_mode_tbl[] = {
{ "balance-rr", BOND_MODE_ROUNDROBIN}, { "balance-rr", BOND_MODE_ROUNDROBIN},
...@@ -537,75 +584,7 @@ static struct bond_parm_tbl bond_mode_tbl[] = { ...@@ -537,75 +584,7 @@ static struct bond_parm_tbl bond_mode_tbl[] = {
{ NULL, -1}, { NULL, -1},
}; };
static int lacp_fast = 0; /*---------------------------- General routines -----------------------------*/
static char *lacp_rate = NULL;
static struct bond_parm_tbl bond_lacp_tbl[] = {
{ "slow", AD_LACP_SLOW},
{ "fast", AD_LACP_FAST},
{ NULL, -1},
};
static LIST_HEAD(bond_dev_list);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *bond_proc_dir = NULL;
#endif
MODULE_PARM(max_bonds, "i");
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
MODULE_PARM(miimon, "i");
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
MODULE_PARM(use_carrier, "i");
MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; 0 for off, 1 for on (default)");
MODULE_PARM(mode, "s");
MODULE_PARM_DESC(mode, "Mode of operation : 0 for round robin, 1 for active-backup, 2 for xor");
MODULE_PARM(arp_interval, "i");
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
MODULE_PARM(arp_ip_target, "1-" __MODULE_STRING(MAX_ARP_IP_TARGETS) "s");
MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
MODULE_PARM(updelay, "i");
MODULE_PARM_DESC(updelay, "Delay before considering link up, in milliseconds");
MODULE_PARM(downdelay, "i");
MODULE_PARM_DESC(downdelay, "Delay before considering link down, in milliseconds");
MODULE_PARM(primary, "s");
MODULE_PARM_DESC(primary, "Primary network device to use");
MODULE_PARM(lacp_rate, "s");
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner (slow/fast)");
static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev);
static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev);
static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev);
static struct net_device_stats *bond_get_stats(struct net_device *bond_dev);
static void bond_mii_monitor(struct net_device *bond_dev);
static void bond_loadbalance_arp_mon(struct net_device *bond_dev);
static void bond_activebackup_arp_mon(struct net_device *bond_dev);
static void bond_mc_list_destroy(struct bonding *bond);
static void bond_mc_add(struct bonding *bond, void *addr, int alen);
static void bond_mc_delete(struct bonding *bond, void *addr, int alen);
static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond, int gpf_flag);
static inline int bond_is_dmi_same(struct dev_mc_list *dmi1, struct dev_mc_list *dmi2);
static void bond_set_promiscuity(struct bonding *bond, int inc);
static void bond_set_allmulti(struct bonding *bond, int inc);
static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi, struct dev_mc_list *mc_list);
static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct slave *old_active);
static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
static int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
static int bond_release_all(struct net_device *bond_dev);
static int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev);
static void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
static void bond_select_active_slave(struct bonding *bond);
static struct slave *bond_find_best_slave(struct bonding *bond);
static void bond_arp_send_all(struct slave *slave)
{
int i;
for (i = 0; (i<MAX_ARP_IP_TARGETS) && arp_target[i]; i++) {
arp_send(ARPOP_REQUEST, ETH_P_ARP, arp_target[i], slave->dev,
my_ip, NULL, slave->dev->dev_addr,
NULL);
}
}
static const char *bond_mode_name(void) static const char *bond_mode_name(void)
{ {
...@@ -629,84 +608,7 @@ static const char *bond_mode_name(void) ...@@ -629,84 +608,7 @@ static const char *bond_mode_name(void)
} }
} }
void bond_set_slave_inactive_flags(struct slave *slave) /*------------------------------- Link status -------------------------------*/
{
slave->state = BOND_STATE_BACKUP;
slave->dev->flags |= IFF_NOARP;
}
void bond_set_slave_active_flags(struct slave *slave)
{
slave->state = BOND_STATE_ACTIVE;
slave->dev->flags &= ~IFF_NOARP;
}
/*
* This function detaches the slave from the list.
* WARNING: no check is made to verify if the slave effectively
* belongs to <bond>.
* Nothing is freed on return, structures are just unchained.
* If any slave pointer in bond was pointing to <slave>,
* it should be changed by the calling function.
*
* bond->lock held for writing by caller.
*/
static void bond_detach_slave(struct bonding *bond, struct slave *slave)
{
if (slave->next) {
slave->next->prev = slave->prev;
}
if (slave->prev) {
slave->prev->next = slave->next;
}
if (bond->first_slave == slave) { /* slave is the first slave */
if (bond->slave_cnt > 1) { /* there are more slave */
bond->first_slave = slave->next;
} else {
bond->first_slave = NULL; /* slave was the last one */
}
}
slave->next = NULL;
slave->prev = NULL;
bond->slave_cnt--;
}
/*
* This function attaches the slave to the end of list.
*
* bond->lock held for writing by caller.
*/
static void bond_attach_slave(struct bonding *bond, struct slave *new_slave)
{
if (bond->first_slave == NULL) { /* attaching the first slave */
new_slave->next = new_slave;
new_slave->prev = new_slave;
bond->first_slave = new_slave;
} else {
new_slave->next = bond->first_slave;
new_slave->prev = bond->first_slave->prev;
new_slave->next->prev = new_slave;
new_slave->prev->next = new_slave;
}
bond->slave_cnt++;
}
/*
* Less bad way to call ioctl from within the kernel; this needs to be
* done some other way to get the call out of interrupt context.
* Needs "ioctl" variable to be supplied by calling context.
*/
#define IOCTL(dev, arg, cmd) ({ \
int res = 0; \
mm_segment_t fs = get_fs(); \
set_fs(get_ds()); \
res = ioctl(dev, arg, cmd); \
set_fs(fs); \
res; })
/* /*
* Get link speed and duplex from the slave's base driver * Get link speed and duplex from the slave's base driver
...@@ -835,174 +737,68 @@ static int bond_check_dev_link(struct net_device *slave_dev, int reporting) ...@@ -835,174 +737,68 @@ static int bond_check_dev_link(struct net_device *slave_dev, int reporting)
return (reporting ? -1 : BMSR_LSTATUS); return (reporting ? -1 : BMSR_LSTATUS);
} }
/* register to receive lacpdus on a bond */ /*----------------------------- Multicast list ------------------------------*/
static void bond_register_lacpdu(struct bonding *bond)
{
struct packet_type *pk_type = &(BOND_AD_INFO(bond).ad_pkt_type);
/* initialize packet type */
pk_type->type = PKT_TYPE_LACPDU;
pk_type->dev = bond->dev;
pk_type->func = bond_3ad_lacpdu_recv;
dev_add_pack(pk_type);
}
/* unregister to receive lacpdus on a bond */ /*
static void bond_unregister_lacpdu(struct bonding *bond) * Returns 0 if dmi1 and dmi2 are the same, non-0 otherwise
*/
static inline int bond_is_dmi_same(struct dev_mc_list *dmi1, struct dev_mc_list *dmi2)
{ {
dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type)); return memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0 &&
dmi1->dmi_addrlen == dmi2->dmi_addrlen;
} }
static int bond_open(struct net_device *bond_dev) /*
* returns dmi entry if found, NULL otherwise
*/
static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi, struct dev_mc_list *mc_list)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct dev_mc_list *idmi;
struct timer_list *timer = &bond->mii_timer;
struct timer_list *arp_timer = &bond->arp_timer;
bond->kill_timers = 0; for (idmi = mc_list; idmi; idmi = idmi->next) {
if (bond_is_dmi_same(dmi, idmi)) {
return idmi;
}
}
if ((bond_mode == BOND_MODE_TLB) || return NULL;
(bond_mode == BOND_MODE_ALB)) { }
struct timer_list *alb_timer = &(BOND_ALB_INFO(bond).alb_timer);
/* bond_alb_initialize must be called before the timer /*
* is started. * Push the promiscuity flag down to appropriate slaves
*/ */
if (bond_alb_initialize(bond, (bond_mode == BOND_MODE_ALB))) { static void bond_set_promiscuity(struct bonding *bond, int inc)
/* something went wrong - fail the open operation */ {
return -1; if (USES_PRIMARY(bond_mode)) {
/* write lock already acquired */
if (bond->curr_active_slave) {
dev_set_promiscuity(bond->curr_active_slave->dev, inc);
} }
} else {
init_timer(alb_timer); struct slave *slave;
alb_timer->expires = jiffies + 1; int i;
alb_timer->data = (unsigned long)bond; bond_for_each_slave(bond, slave, i) {
alb_timer->function = (void *)&bond_alb_monitor; dev_set_promiscuity(slave->dev, inc);
add_timer(alb_timer);
} }
if (miimon) { /* link check interval, in milliseconds. */
init_timer(timer);
timer->expires = jiffies + 1;
timer->data = (unsigned long)bond_dev;
timer->function = (void *)&bond_mii_monitor;
add_timer(timer);
} }
}
if (arp_interval) { /* arp interval, in milliseconds. */
init_timer(arp_timer);
arp_timer->expires = jiffies + 1;
arp_timer->data = (unsigned long)bond_dev;
if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
arp_timer->function = (void *)&bond_activebackup_arp_mon;
} else {
arp_timer->function = (void *)&bond_loadbalance_arp_mon;
}
add_timer(arp_timer);
}
if (bond_mode == BOND_MODE_8023AD) {
struct timer_list *ad_timer = &(BOND_AD_INFO(bond).ad_timer);
init_timer(ad_timer);
ad_timer->expires = jiffies + 1;
ad_timer->data = (unsigned long)bond;
ad_timer->function = (void *)&bond_3ad_state_machine_handler;
add_timer(ad_timer);
/* register to receive LACPDUs */
bond_register_lacpdu(bond);
}
return 0;
}
static int bond_close(struct net_device *bond_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
write_lock_bh(&bond->lock);
bond_mc_list_destroy(bond);
if (bond_mode == BOND_MODE_8023AD) {
/* Unregister the receive of LACPDUs */
bond_unregister_lacpdu(bond);
}
/* signal timers not to re-arm */
bond->kill_timers = 1;
write_unlock_bh(&bond->lock);
/* del_timer_sync must run without holding the bond->lock
* because a running timer might be trying to hold it too
*/
if (miimon) { /* link check interval, in milliseconds. */
del_timer_sync(&bond->mii_timer);
}
if (arp_interval) { /* arp interval, in milliseconds. */
del_timer_sync(&bond->arp_timer);
}
switch (bond_mode) {
case BOND_MODE_8023AD:
del_timer_sync(&(BOND_AD_INFO(bond).ad_timer));
break;
case BOND_MODE_TLB:
case BOND_MODE_ALB:
del_timer_sync(&(BOND_ALB_INFO(bond).alb_timer));
break;
default:
break;
}
/* Release the bonded slaves */
bond_release_all(bond_dev);
if ((bond_mode == BOND_MODE_TLB) ||
(bond_mode == BOND_MODE_ALB)) {
/* Must be called only after all
* slaves have been released
*/
bond_alb_deinitialize(bond);
}
return 0;
}
/* /*
* flush all members of flush->mc_list from device dev->mc_list * Push the allmulti flag down to all slaves
*/ */
static void bond_mc_list_flush(struct net_device *slave_dev, struct net_device *bond_dev) static void bond_set_allmulti(struct bonding *bond, int inc)
{ {
struct dev_mc_list *dmi; if (USES_PRIMARY(bond_mode)) {
/* write lock already acquired */
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { if (bond->curr_active_slave) {
dev_mc_delete(slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); dev_set_allmulti(bond->curr_active_slave->dev, inc);
} }
} else {
if (bond_mode == BOND_MODE_8023AD) { struct slave *slave;
/* del lacpdu mc addr from mc list */ int i;
u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; bond_for_each_slave(bond, slave, i) {
dev_set_allmulti(slave->dev, inc);
dev_mc_delete(slave_dev, lacpdu_multicast, ETH_ALEN, 0);
} }
}
/*
* Totally destroys the mc_list in bond
*/
static void bond_mc_list_destroy(struct bonding *bond)
{
struct dev_mc_list *dmi;
dmi = bond->mc_list;
while (dmi) {
bond->mc_list = dmi->next;
kfree(dmi);
dmi = bond->mc_list;
} }
} }
...@@ -1046,6 +842,21 @@ static void bond_mc_delete(struct bonding *bond, void *addr, int alen) ...@@ -1046,6 +842,21 @@ static void bond_mc_delete(struct bonding *bond, void *addr, int alen)
} }
} }
/*
* Totally destroys the mc_list in bond
*/
static void bond_mc_list_destroy(struct bonding *bond)
{
struct dev_mc_list *dmi;
dmi = bond->mc_list;
while (dmi) {
bond->mc_list = dmi->next;
kfree(dmi);
dmi = bond->mc_list;
}
}
/* /*
* Copy all the Multicast addresses from src to the bonding device dst * Copy all the Multicast addresses from src to the bonding device dst
*/ */
...@@ -1073,118 +884,26 @@ static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond, ...@@ -1073,118 +884,26 @@ static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond,
} }
/* /*
* Returns 0 if dmi1 and dmi2 are the same, non-0 otherwise * flush all members of flush->mc_list from device dev->mc_list
*/
static inline int bond_is_dmi_same(struct dev_mc_list *dmi1, struct dev_mc_list *dmi2)
{
return memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0 &&
dmi1->dmi_addrlen == dmi2->dmi_addrlen;
}
/*
* Push the promiscuity flag down to appropriate slaves
*/
static void bond_set_promiscuity(struct bonding *bond, int inc)
{
if (USES_PRIMARY(bond_mode)) {
/* write lock already acquired */
if (bond->curr_active_slave) {
dev_set_promiscuity(bond->curr_active_slave->dev, inc);
}
} else {
struct slave *slave;
int i;
bond_for_each_slave(bond, slave, i) {
dev_set_promiscuity(slave->dev, inc);
}
}
}
/*
* Push the allmulti flag down to all slaves
*/
static void bond_set_allmulti(struct bonding *bond, int inc)
{
if (USES_PRIMARY(bond_mode)) {
/* write lock already acquired */
if (bond->curr_active_slave) {
dev_set_allmulti(bond->curr_active_slave->dev, inc);
}
} else {
struct slave *slave;
int i;
bond_for_each_slave(bond, slave, i) {
dev_set_allmulti(slave->dev, inc);
}
}
}
/*
* returns dmi entry if found, NULL otherwise
*/ */
static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi, struct dev_mc_list *mc_list) static void bond_mc_list_flush(struct net_device *bond_dev, struct net_device *slave_dev)
{
struct dev_mc_list *idmi;
for (idmi = mc_list; idmi; idmi = idmi->next) {
if (bond_is_dmi_same(dmi, idmi)) {
return idmi;
}
}
return NULL;
}
static void bond_set_multicast_list(struct net_device *bond_dev)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv;
struct dev_mc_list *dmi; struct dev_mc_list *dmi;
write_lock_bh(&bond->lock);
/*
* Do promisc before checking multicast_mode
*/
if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC)) {
bond_set_promiscuity(bond, 1);
}
if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC)) {
bond_set_promiscuity(bond, -1);
}
/* set allmulti flag to slaves */
if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI)) {
bond_set_allmulti(bond, 1);
}
if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI)) {
bond_set_allmulti(bond, -1);
}
bond->flags = bond_dev->flags;
/* looking for addresses to add to slaves' mc list */
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
if (!bond_mc_list_find_dmi(dmi, bond->mc_list)) { dev_mc_delete(slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
bond_mc_add(bond, dmi->dmi_addr, dmi->dmi_addrlen);
}
}
/* looking for addresses to delete from slaves' list */
for (dmi = bond->mc_list; dmi; dmi = dmi->next) {
if (!bond_mc_list_find_dmi(dmi, bond_dev->mc_list)) {
bond_mc_delete(bond, dmi->dmi_addr, dmi->dmi_addrlen);
}
} }
/* save master's multicast list */ if (bond_mode == BOND_MODE_8023AD) {
bond_mc_list_destroy(bond); /* del lacpdu mc addr from mc list */
bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC); u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
write_unlock_bh(&bond->lock); dev_mc_delete(slave_dev, lacpdu_multicast, ETH_ALEN, 0);
}
} }
/*--------------------------- Active slave change ---------------------------*/
/* /*
* Update the mc list and multicast-related flags for the new and * Update the mc list and multicast-related flags for the new and
* old active slaves (if any) according to the multicast mode, and * old active slaves (if any) according to the multicast mode, and
...@@ -1230,15 +949,236 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct ...@@ -1230,15 +949,236 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct
} }
} }
/* enslave device <slave> to bond device <master> */ /**
static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * find_best_interface - select the best available slave to be the active one
* @bond: our bonding struct
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static struct slave *bond_find_best_slave(struct bonding *bond)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct slave *new_active, *old_active;
struct slave *new_slave = NULL; struct slave *bestslave = NULL;
struct dev_mc_list *dmi; int mintime;
int link_reporting; int i;
struct sockaddr addr;
int res = 0; new_active = old_active = bond->curr_active_slave;
if (!new_active) { /* there were no active slaves left */
if (bond->slave_cnt > 0) { /* found one slave */
new_active = bond->first_slave;
} else {
return NULL; /* still no slave, return NULL */
}
}
mintime = updelay;
/* first try the primary link; if arping, a link must tx/rx traffic
* before it can be considered the curr_active_slave - also, we would skip
* slaves between the curr_active_slave and primary_slave that may be up
* and able to arp
*/
if ((bond->primary_slave) &&
(!arp_interval) &&
(IS_UP(bond->primary_slave->dev))) {
new_active = bond->primary_slave;
}
/* remember where to stop iterating over the slaves */
old_active = new_active;
bond_for_each_slave_from(bond, new_active, i, old_active) {
if (IS_UP(new_active->dev)) {
if (new_active->link == BOND_LINK_UP) {
return new_active;
} else if (new_active->link == BOND_LINK_BACK) {
/* link up, but waiting for stabilization */
if (new_active->delay < mintime) {
mintime = new_active->delay;
bestslave = new_active;
}
}
}
}
return bestslave;
}
/**
* change_active_interface - change the active slave into the specified one
* @bond: our bonding struct
* @new: the new slave to make the active one
*
* Set the new slave to the bond's settings and unset them on the old
* curr_active_slave.
* Setting include flags, mc-list, promiscuity, allmulti, etc.
*
* If @new's link state is %BOND_LINK_BACK we'll set it to %BOND_LINK_UP,
* because it is apparently the best available slave we have, even though its
* updelay hasn't timed out yet.
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
{
struct slave *old_active = bond->curr_active_slave;
if (old_active == new_active) {
return;
}
if (new_active) {
if (new_active->link == BOND_LINK_BACK) {
if (USES_PRIMARY(bond_mode)) {
printk(KERN_INFO DRV_NAME
": %s: making interface %s the new "
"active one %d ms earlier.\n",
bond->dev->name, new_active->dev->name,
(updelay - new_active->delay) * miimon);
}
new_active->delay = 0;
new_active->link = BOND_LINK_UP;
new_active->jiffies = jiffies;
if (bond_mode == BOND_MODE_8023AD) {
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
}
if ((bond_mode == BOND_MODE_TLB) ||
(bond_mode == BOND_MODE_ALB)) {
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
}
} else {
if (USES_PRIMARY(bond_mode)) {
printk(KERN_INFO DRV_NAME
": %s: making interface %s the new "
"active one.\n",
bond->dev->name, new_active->dev->name);
}
}
}
if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
if (old_active) {
bond_set_slave_inactive_flags(old_active);
}
if (new_active) {
bond_set_slave_active_flags(new_active);
}
}
if (USES_PRIMARY(bond_mode)) {
bond_mc_swap(bond, new_active, old_active);
}
if ((bond_mode == BOND_MODE_TLB) ||
(bond_mode == BOND_MODE_ALB)) {
bond_alb_handle_active_change(bond, new_active);
} else {
bond->curr_active_slave = new_active;
}
}
/**
* bond_select_active_slave - select a new active slave, if needed
* @bond: our bonding struct
*
* This functions shoud be called when one of the following occurs:
* - The old curr_active_slave has been released or lost its link.
* - The primary_slave has got its link back.
* - A slave has got its link back and there's no old curr_active_slave.
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static void bond_select_active_slave(struct bonding *bond)
{
struct slave *best_slave;
best_slave = bond_find_best_slave(bond);
if (best_slave != bond->curr_active_slave) {
bond_change_active_slave(bond, best_slave);
}
}
/*--------------------------- slave list handling ---------------------------*/
/*
* This function attaches the slave to the end of list.
*
* bond->lock held for writing by caller.
*/
static void bond_attach_slave(struct bonding *bond, struct slave *new_slave)
{
if (bond->first_slave == NULL) { /* attaching the first slave */
new_slave->next = new_slave;
new_slave->prev = new_slave;
bond->first_slave = new_slave;
} else {
new_slave->next = bond->first_slave;
new_slave->prev = bond->first_slave->prev;
new_slave->next->prev = new_slave;
new_slave->prev->next = new_slave;
}
bond->slave_cnt++;
}
/*
* This function detaches the slave from the list.
* WARNING: no check is made to verify if the slave effectively
* belongs to <bond>.
* Nothing is freed on return, structures are just unchained.
* If any slave pointer in bond was pointing to <slave>,
* it should be changed by the calling function.
*
* bond->lock held for writing by caller.
*/
static void bond_detach_slave(struct bonding *bond, struct slave *slave)
{
if (slave->next) {
slave->next->prev = slave->prev;
}
if (slave->prev) {
slave->prev->next = slave->next;
}
if (bond->first_slave == slave) { /* slave is the first slave */
if (bond->slave_cnt > 1) { /* there are more slave */
bond->first_slave = slave->next;
} else {
bond->first_slave = NULL; /* slave was the last one */
}
}
slave->next = NULL;
slave->prev = NULL;
bond->slave_cnt--;
}
/*---------------------------------- IOCTL ----------------------------------*/
static int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev)
{
dprintk("bond_dev=%p\n", bond_dev);
dprintk("slave_dev=%p\n", slave_dev);
dprintk("slave_dev->addr_len=%d\n", slave_dev->addr_len);
memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
return 0;
}
/* enslave device <slave> to bond device <master> */
static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *new_slave = NULL;
struct dev_mc_list *dmi;
struct sockaddr addr;
int link_reporting;
int res = 0;
if (slave_dev->do_ioctl == NULL) { if (slave_dev->do_ioctl == NULL) {
printk(KERN_WARNING DRV_NAME printk(KERN_WARNING DRV_NAME
...@@ -1604,235 +1544,29 @@ static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_de ...@@ -1604,235 +1544,29 @@ static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_de
} }
/* /*
* This function changes the active slave to slave <slave_dev>. * Try to release the slave device <slave> from the bond device <master>
* It returns -EINVAL in the following cases. * It is legal to access curr_active_slave without a lock because all the function
* - <slave_dev> is not found in the list. * is write-locked.
* - There is not active slave now. *
* - <slave_dev> is already active. * The rules for slave state should be:
* - The link state of <slave_dev> is not BOND_LINK_UP. * for Active/Backup:
* - <slave_dev> is not running. * Active stays on all backups go down
* In these cases, this fuction does nothing. * for Bonded connections:
* In the other cases, currnt_slave pointer is changed and 0 is returned. * The first up interface should be left on and all others downed.
*/ */
static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev) static int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *old_active = NULL; struct slave *slave;
struct slave *new_active = NULL; struct sockaddr addr;
int res = 0; int mac_addr_differ;
/* Verify that master_dev is indeed the master of slave_dev */ /* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_SLAVE) || if (!(slave_dev->flags & IFF_SLAVE) ||
(slave_dev->master != bond_dev)) { (slave_dev->master != bond_dev)) {
printk(KERN_ERR DRV_NAME
return -EINVAL; ": Error: %s: cannot release %s.\n",
} bond_dev->name, slave_dev->name);
write_lock_bh(&bond->lock);
old_active = bond->curr_active_slave;
new_active = bond_get_slave_by_dev(bond, slave_dev);
/*
* Changing to the current active: do nothing; return success.
*/
if (new_active && (new_active == old_active)) {
write_unlock_bh(&bond->lock);
return 0;
}
if ((new_active) &&
(old_active) &&
(new_active->link == BOND_LINK_UP) &&
IS_UP(new_active->dev)) {
bond_change_active_slave(bond, new_active);
} else {
res = -EINVAL;
}
write_unlock_bh(&bond->lock);
return res;
}
/**
* find_best_interface - select the best available slave to be the active one
* @bond: our bonding struct
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static struct slave *bond_find_best_slave(struct bonding *bond)
{
struct slave *new_active, *old_active;
struct slave *bestslave = NULL;
int mintime;
int i;
new_active = old_active = bond->curr_active_slave;
if (!new_active) { /* there were no active slaves left */
if (bond->slave_cnt > 0) { /* found one slave */
new_active = bond->first_slave;
} else {
return NULL; /* still no slave, return NULL */
}
}
mintime = updelay;
/* first try the primary link; if arping, a link must tx/rx traffic
* before it can be considered the curr_active_slave - also, we would skip
* slaves between the curr_active_slave and primary_slave that may be up
* and able to arp
*/
if ((bond->primary_slave) &&
(!arp_interval) &&
(IS_UP(bond->primary_slave->dev))) {
new_active = bond->primary_slave;
}
/* remember where to stop iterating over the slaves */
old_active = new_active;
bond_for_each_slave_from(bond, new_active, i, old_active) {
if (IS_UP(new_active->dev)) {
if (new_active->link == BOND_LINK_UP) {
return new_active;
} else if (new_active->link == BOND_LINK_BACK) {
/* link up, but waiting for stabilization */
if (new_active->delay < mintime) {
mintime = new_active->delay;
bestslave = new_active;
}
}
}
}
return bestslave;
}
/**
* change_active_interface - change the active slave into the specified one
* @bond: our bonding struct
* @new: the new slave to make the active one
*
* Set the new slave to the bond's settings and unset them on the old
* curr_active_slave.
* Setting include flags, mc-list, promiscuity, allmulti, etc.
*
* If @new's link state is %BOND_LINK_BACK we'll set it to %BOND_LINK_UP,
* because it is apparently the best available slave we have, even though its
* updelay hasn't timed out yet.
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
{
struct slave *old_active = bond->curr_active_slave;
if (old_active == new_active) {
return;
}
if (new_active) {
if (new_active->link == BOND_LINK_BACK) {
if (USES_PRIMARY(bond_mode)) {
printk(KERN_INFO DRV_NAME
": %s: making interface %s the new "
"active one %d ms earlier.\n",
bond->dev->name, new_active->dev->name,
(updelay - new_active->delay) * miimon);
}
new_active->delay = 0;
new_active->link = BOND_LINK_UP;
new_active->jiffies = jiffies;
if (bond_mode == BOND_MODE_8023AD) {
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
}
if ((bond_mode == BOND_MODE_TLB) ||
(bond_mode == BOND_MODE_ALB)) {
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
}
} else {
if (USES_PRIMARY(bond_mode)) {
printk(KERN_INFO DRV_NAME
": %s: making interface %s the new "
"active one.\n",
bond->dev->name, new_active->dev->name);
}
}
}
if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
if (old_active) {
bond_set_slave_inactive_flags(old_active);
}
if (new_active) {
bond_set_slave_active_flags(new_active);
}
}
if (USES_PRIMARY(bond_mode)) {
bond_mc_swap(bond, new_active, old_active);
}
if ((bond_mode == BOND_MODE_TLB) ||
(bond_mode == BOND_MODE_ALB)) {
bond_alb_handle_active_change(bond, new_active);
} else {
bond->curr_active_slave = new_active;
}
}
/**
* reselect_active_interface - select a new active slave, if needed
* @bond: our bonding struct
*
* This functions shoud be called when one of the following occurs:
* - The old curr_active_slave has been released or lost its link.
* - The primary_slave has got its link back.
* - A slave has got its link back and there's no old curr_active_slave.
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
static void bond_select_active_slave(struct bonding *bond)
{
struct slave *best_slave;
best_slave = bond_find_best_slave(bond);
if (best_slave != bond->curr_active_slave) {
bond_change_active_slave(bond, best_slave);
}
}
/*
* Try to release the slave device <slave> from the bond device <master>
* It is legal to access curr_active_slave without a lock because all the function
* is write-locked.
*
* The rules for slave state should be:
* for Active/Backup:
* Active stays on all backups go down
* for Bonded connections:
* The first up interface should be left on and all others downed.
*/
static int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave;
struct sockaddr addr;
int mac_addr_differ;
/* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_SLAVE) ||
(slave_dev->master != bond_dev)) {
printk(KERN_ERR DRV_NAME
": Error: %s: cannot release %s.\n",
bond_dev->name, slave_dev->name);
return -EINVAL; return -EINVAL;
} }
...@@ -1897,7 +1631,7 @@ static int bond_release(struct net_device *bond_dev, struct net_device *slave_de ...@@ -1897,7 +1631,7 @@ static int bond_release(struct net_device *bond_dev, struct net_device *slave_de
bond_select_active_slave(bond); bond_select_active_slave(bond);
} }
if (bond->curr_active_slave == NULL) { if (!bond->curr_active_slave) {
printk(KERN_INFO DRV_NAME printk(KERN_INFO DRV_NAME
": %s: now running without any active " ": %s: now running without any active "
"interface !\n", "interface !\n",
...@@ -1931,7 +1665,7 @@ static int bond_release(struct net_device *bond_dev, struct net_device *slave_de ...@@ -1931,7 +1665,7 @@ static int bond_release(struct net_device *bond_dev, struct net_device *slave_de
} }
/* flush master's mc_list from slave */ /* flush master's mc_list from slave */
bond_mc_list_flush (slave_dev, bond_dev); bond_mc_list_flush(bond_dev, slave_dev);
} }
netdev_set_master(slave_dev, NULL); netdev_set_master(slave_dev, NULL);
...@@ -2028,7 +1762,7 @@ static int bond_release_all(struct net_device *bond_dev) ...@@ -2028,7 +1762,7 @@ static int bond_release_all(struct net_device *bond_dev)
} }
/* flush master's mc_list from slave */ /* flush master's mc_list from slave */
bond_mc_list_flush(slave_dev, bond_dev); bond_mc_list_flush(bond_dev, slave_dev);
} }
netdev_set_master(slave_dev, NULL); netdev_set_master(slave_dev, NULL);
...@@ -2072,39 +1806,189 @@ static int bond_release_all(struct net_device *bond_dev) ...@@ -2072,39 +1806,189 @@ static int bond_release_all(struct net_device *bond_dev)
return 0; return 0;
} }
/* this function is called regularly to monitor each slave's link. */ /*
static void bond_mii_monitor(struct net_device *bond_dev) * This function changes the active slave to slave <slave_dev>.
* It returns -EINVAL in the following cases.
* - <slave_dev> is not found in the list.
* - There is not active slave now.
* - <slave_dev> is already active.
* - The link state of <slave_dev> is not BOND_LINK_UP.
* - <slave_dev> is not running.
* In these cases, this fuction does nothing.
* In the other cases, currnt_slave pointer is changed and 0 is returned.
*/
static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *oldcurrent; struct slave *old_active = NULL;
int slave_died = 0; struct slave *new_active = NULL;
int do_failover = 0; int res = 0;
int delta_in_ticks = miimon * HZ / 1000;
int i;
read_lock(&bond->lock);
if (bond->kill_timers) { /* Verify that master_dev is indeed the master of slave_dev */
goto out; if (!(slave_dev->flags & IFF_SLAVE) ||
(slave_dev->master != bond_dev)) {
return -EINVAL;
} }
if (bond->slave_cnt == 0) { write_lock_bh(&bond->lock);
goto re_arm;
}
/* we will try to read the link status of each of our slaves, and old_active = bond->curr_active_slave;
* set their IFF_RUNNING flag appropriately. For each slave not new_active = bond_get_slave_by_dev(bond, slave_dev);
* supporting MII status, we won't do anything so that a user-space
* program could monitor the link itself if needed.
*/
read_lock(&bond->curr_slave_lock); /*
oldcurrent = bond->curr_active_slave; * Changing to the current active: do nothing; return success.
read_unlock(&bond->curr_slave_lock); */
if (new_active && (new_active == old_active)) {
write_unlock_bh(&bond->lock);
return 0;
}
bond_for_each_slave(bond, slave, i) { if ((new_active) &&
struct net_device *slave_dev = slave->dev; (old_active) &&
int link_state; (new_active->link == BOND_LINK_UP) &&
IS_UP(new_active->dev)) {
bond_change_active_slave(bond, new_active);
} else {
res = -EINVAL;
}
write_unlock_bh(&bond->lock);
return res;
}
static int bond_ethtool_ioctl(struct net_device *bond_dev, struct ifreq *ifr)
{
struct ethtool_drvinfo info;
void *addr = ifr->ifr_data;
uint32_t cmd;
if (get_user(cmd, (uint32_t *)addr)) {
return -EFAULT;
}
switch (cmd) {
case ETHTOOL_GDRVINFO:
if (copy_from_user(&info, addr, sizeof(info))) {
return -EFAULT;
}
if (strcmp(info.driver, "ifenslave") == 0) {
int new_abi_ver;
char *endptr;
new_abi_ver = simple_strtoul(info.fw_version,
&endptr, 0);
if (*endptr) {
printk(KERN_ERR DRV_NAME
": Error: got invalid ABI "
"version from application\n");
return -EINVAL;
}
if (orig_app_abi_ver == -1) {
orig_app_abi_ver = new_abi_ver;
}
app_abi_ver = new_abi_ver;
}
strncpy(info.driver, DRV_NAME, 32);
strncpy(info.version, DRV_VERSION, 32);
snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION);
if (copy_to_user(addr, &info, sizeof(info))) {
return -EFAULT;
}
return 0;
default:
return -EOPNOTSUPP;
}
}
static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
info->bond_mode = bond_mode;
info->miimon = miimon;
read_lock_bh(&bond->lock);
info->num_slaves = bond->slave_cnt;
read_unlock_bh(&bond->lock);
return 0;
}
static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave;
int i, found = 0;
if (info->slave_id < 0) {
return -ENODEV;
}
read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, i) {
if (i == (int)info->slave_id) {
found = 1;
break;
}
}
read_unlock_bh(&bond->lock);
if (found) {
strcpy(info->slave_name, slave->dev->name);
info->link = slave->link;
info->state = slave->state;
info->link_failure_count = slave->link_failure_count;
} else {
return -ENODEV;
}
return 0;
}
/*-------------------------------- Monitoring -------------------------------*/
/* this function is called regularly to monitor each slave's link. */
static void bond_mii_monitor(struct net_device *bond_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *oldcurrent;
int do_failover = 0;
int delta_in_ticks = miimon * HZ / 1000;
int i;
read_lock(&bond->lock);
if (bond->kill_timers) {
goto out;
}
if (bond->slave_cnt == 0) {
goto re_arm;
}
/* we will try to read the link status of each of our slaves, and
* set their IFF_RUNNING flag appropriately. For each slave not
* supporting MII status, we won't do anything so that a user-space
* program could monitor the link itself if needed.
*/
read_lock(&bond->curr_slave_lock);
oldcurrent = bond->curr_active_slave;
read_unlock(&bond->curr_slave_lock);
bond_for_each_slave(bond, slave, i) {
struct net_device *slave_dev = slave->dev;
int link_state;
u16 old_speed = slave->speed; u16 old_speed = slave->speed;
u8 old_duplex = slave->duplex; u8 old_duplex = slave->duplex;
...@@ -2160,7 +2044,7 @@ static void bond_mii_monitor(struct net_device *bond_dev) ...@@ -2160,7 +2044,7 @@ static void bond_mii_monitor(struct net_device *bond_dev)
printk(KERN_INFO DRV_NAME printk(KERN_INFO DRV_NAME
": %s: link status definitely " ": %s: link status definitely "
"down for interface %s, " "down for interface %s, "
"disabling it", "disabling it\n",
bond_dev->name, bond_dev->name,
slave_dev->name); slave_dev->name);
...@@ -2177,8 +2061,6 @@ static void bond_mii_monitor(struct net_device *bond_dev) ...@@ -2177,8 +2061,6 @@ static void bond_mii_monitor(struct net_device *bond_dev)
if (slave == oldcurrent) { if (slave == oldcurrent) {
do_failover = 1; do_failover = 1;
} }
slave_died = 1;
} else { } else {
slave->delay--; slave->delay--;
} }
...@@ -2296,6 +2178,7 @@ static void bond_mii_monitor(struct net_device *bond_dev) ...@@ -2296,6 +2178,7 @@ static void bond_mii_monitor(struct net_device *bond_dev)
write_lock(&bond->curr_slave_lock); write_lock(&bond->curr_slave_lock);
bond_select_active_slave(bond); bond_select_active_slave(bond);
if (oldcurrent && !bond->curr_active_slave) { if (oldcurrent && !bond->curr_active_slave) {
printk(KERN_INFO DRV_NAME printk(KERN_INFO DRV_NAME
": %s: now running without any active " ": %s: now running without any active "
...@@ -2312,6 +2195,17 @@ static void bond_mii_monitor(struct net_device *bond_dev) ...@@ -2312,6 +2195,17 @@ static void bond_mii_monitor(struct net_device *bond_dev)
read_unlock(&bond->lock); read_unlock(&bond->lock);
} }
static void bond_arp_send_all(struct slave *slave)
{
int i;
for (i = 0; (i<MAX_ARP_IP_TARGETS) && arp_target[i]; i++) {
arp_send(ARPOP_REQUEST, ETH_P_ARP, arp_target[i], slave->dev,
my_ip, NULL, slave->dev->dev_addr,
NULL);
}
}
/* /*
* this function is called regularly to monitor each slave's link * this function is called regularly to monitor each slave's link
* ensuring that traffic is being sent and received when arp monitoring * ensuring that traffic is being sent and received when arp monitoring
...@@ -2578,7 +2472,7 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev) ...@@ -2578,7 +2472,7 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev)
printk(KERN_INFO DRV_NAME printk(KERN_INFO DRV_NAME
": %s: link status down for active interface " ": %s: link status down for active interface "
"%s, disabling it", "%s, disabling it\n",
bond_dev->name, bond_dev->name,
slave->dev->name); slave->dev->name);
...@@ -2629,8 +2523,7 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev) ...@@ -2629,8 +2523,7 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev)
* for becoming the curr_active_slave * for becoming the curr_active_slave
*/ */
if (!slave) { if (!slave) {
if (!bond->current_arp_slave) {
if (bond->current_arp_slave == NULL) {
bond->current_arp_slave = bond->first_slave; bond->current_arp_slave = bond->first_slave;
} }
...@@ -2679,473 +2572,531 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev) ...@@ -2679,473 +2572,531 @@ static void bond_activebackup_arp_mon(struct net_device *bond_dev)
read_unlock(&bond->lock); read_unlock(&bond->lock);
} }
static int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev) /*------------------------------ proc/seq_file-------------------------------*/
{
dprintk("bond_dev=%p\n", bond_dev);
dprintk("slave_dev=%p\n", slave_dev);
dprintk("slave_dev->addr_len=%d\n", slave_dev->addr_len);
memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
return 0;
}
static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
info->bond_mode = bond_mode;
info->miimon = miimon;
read_lock_bh(&bond->lock); #ifdef CONFIG_PROC_FS
info->num_slaves = bond->slave_cnt;
read_unlock_bh(&bond->lock);
return 0; #define SEQ_START_TOKEN ((void *)1)
}
static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info) static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = seq->private;
loff_t off = 0;
struct slave *slave; struct slave *slave;
int i, found = 0; int i;
if (info->slave_id < 0) {
return -ENODEV;
}
/* make sure the bond won't be taken away */
read_lock(&dev_base_lock);
read_lock_bh(&bond->lock); read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, i) { if (*pos == 0) {
if (i == (int)info->slave_id) { return SEQ_START_TOKEN;
found = 1;
break;
}
} }
read_unlock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) {
if (++off == *pos) {
if (found) { return slave;
strcpy(info->slave_name, slave->dev->name); }
info->link = slave->link;
info->state = slave->state;
info->link_failure_count = slave->link_failure_count;
} else {
return -ENODEV;
} }
return 0; return NULL;
} }
static int bond_ethtool_ioctl(struct net_device *bond_dev, struct ifreq *ifr) static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
void *addr = ifr->ifr_data; struct bonding *bond = seq->private;
uint32_t cmd; struct slave *slave = v;
struct ethtool_drvinfo info;
char *endptr;
if (get_user(cmd, (uint32_t *)addr)) { ++*pos;
return -EFAULT; if (v == SEQ_START_TOKEN) {
return bond->first_slave;
} }
switch (cmd) { slave = slave->next;
case ETHTOOL_GDRVINFO:
if (copy_from_user(&info, addr, sizeof(info))) {
return -EFAULT;
}
if (strcmp(info.driver, "ifenslave") == 0) { return (slave == bond->first_slave) ? NULL : slave;
int new_abi_ver; }
new_abi_ver = simple_strtoul(info.fw_version, static void bond_info_seq_stop(struct seq_file *seq, void *v)
&endptr, 0); {
if (*endptr) { struct bonding *bond = seq->private;
printk(KERN_ERR DRV_NAME
": Error: got invalid ABI "
"version from application\n");
return -EINVAL; read_unlock_bh(&bond->lock);
} read_unlock(&dev_base_lock);
}
if (orig_app_abi_ver == -1) { static void bond_info_show_master(struct seq_file *seq, struct bonding *bond)
orig_app_abi_ver = new_abi_ver; {
} struct slave *curr;
app_abi_ver = new_abi_ver; read_lock(&bond->curr_slave_lock);
} curr = bond->curr_active_slave;
read_unlock(&bond->curr_slave_lock);
strncpy(info.driver, DRV_NAME, 32); seq_printf(seq, "Bonding Mode: %s\n", bond_mode_name());
strncpy(info.version, DRV_VERSION, 32);
snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION);
if (copy_to_user(addr, &info, sizeof(info))) { if (USES_PRIMARY(bond_mode)) {
return -EFAULT; if (curr) {
seq_printf(seq,
"Currently Active Slave: %s\n",
curr->dev->name);
}
} }
return 0; seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down");
default: seq_printf(seq, "MII Polling Interval (ms): %d\n", miimon);
return -EOPNOTSUPP; seq_printf(seq, "Up Delay (ms): %d\n", updelay * miimon);
seq_printf(seq, "Down Delay (ms): %d\n", downdelay * miimon);
if (bond_mode == BOND_MODE_8023AD) {
struct ad_info ad_info;
seq_puts(seq, "\n802.3ad info\n");
if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
seq_printf(seq, "bond %s has no active aggregator\n",
bond->dev->name);
} else {
seq_printf(seq, "Active Aggregator Info:\n");
seq_printf(seq, "\tAggregator ID: %d\n",
ad_info.aggregator_id);
seq_printf(seq, "\tNumber of ports: %d\n",
ad_info.ports);
seq_printf(seq, "\tActor Key: %d\n",
ad_info.actor_key);
seq_printf(seq, "\tPartner Key: %d\n",
ad_info.partner_key);
seq_printf(seq, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
ad_info.partner_system[0],
ad_info.partner_system[1],
ad_info.partner_system[2],
ad_info.partner_system[3],
ad_info.partner_system[4],
ad_info.partner_system[5]);
}
} }
} }
static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd) static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave)
{ {
struct net_device *slave_dev = NULL; seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
struct ifbond *u_binfo = NULL, k_binfo; seq_printf(seq, "MII Status: %s\n",
struct ifslave *u_sinfo = NULL, k_sinfo; (slave->link == BOND_LINK_UP) ? "up" : "down");
struct mii_ioctl_data *mii = NULL; seq_printf(seq, "Link Failure Count: %d\n",
int prev_abi_ver = orig_app_abi_ver; slave->link_failure_count);
int res = 0;
dprintk("bond_ioctl: master=%s, cmd=%d\n", if (app_abi_ver >= 1) {
bond_dev->name, cmd); seq_printf(seq,
"Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
slave->perm_hwaddr[0],
slave->perm_hwaddr[1],
slave->perm_hwaddr[2],
slave->perm_hwaddr[3],
slave->perm_hwaddr[4],
slave->perm_hwaddr[5]);
}
switch (cmd) { if (bond_mode == BOND_MODE_8023AD) {
case SIOCETHTOOL: const struct aggregator *agg
return bond_ethtool_ioctl(bond_dev, ifr); = SLAVE_AD_INFO(slave).port.aggregator;
case SIOCGMIIPHY:
mii = (struct mii_ioctl_data *)&ifr->ifr_data; if (agg) {
if (!mii) { seq_printf(seq, "Aggregator ID: %d\n",
return -EINVAL; agg->aggregator_identifier);
} else {
seq_puts(seq, "Aggregator ID: N/A\n");
} }
mii->phy_id = 0;
/* Fall Through */
case SIOCGMIIREG:
/*
* We do this again just in case we were called by SIOCGMIIREG
* instead of SIOCGMIIPHY.
*/
mii = (struct mii_ioctl_data *)&ifr->ifr_data;
if (!mii) {
return -EINVAL;
} }
}
if (mii->reg_num == 1) { static int bond_info_seq_show(struct seq_file *seq, void *v)
struct bonding *bond = (struct bonding *)bond_dev->priv; {
mii->val_out = 0; if (v == SEQ_START_TOKEN) {
read_lock_bh(&bond->lock); seq_printf(seq, "%s\n", version);
read_lock(&bond->curr_slave_lock); bond_info_show_master(seq, seq->private);
if (bond->curr_active_slave) { } else {
mii->val_out = BMSR_LSTATUS; bond_info_show_slave(seq, v);
}
read_unlock(&bond->curr_slave_lock);
read_unlock_bh(&bond->lock);
} }
return 0; return 0;
case BOND_INFO_QUERY_OLD: }
case SIOCBONDINFOQUERY:
u_binfo = (struct ifbond *)ifr->ifr_data;
if (copy_from_user(&k_binfo, u_binfo, sizeof(ifbond))) { static struct seq_operations bond_info_seq_ops = {
return -EFAULT; .start = bond_info_seq_start,
} .next = bond_info_seq_next,
.stop = bond_info_seq_stop,
.show = bond_info_seq_show,
};
res = bond_info_query(bond_dev, &k_binfo); static int bond_info_open(struct inode *inode, struct file *file)
if (res == 0) { {
if (copy_to_user(u_binfo, &k_binfo, sizeof(ifbond))) { struct seq_file *seq;
return -EFAULT; struct proc_dir_entry *proc;
} int res;
res = seq_open(file, &bond_info_seq_ops);
if (!res) {
/* recover the pointer buried in proc_dir_entry data */
seq = file->private_data;
proc = PDE(inode);
seq->private = proc->data;
} }
return res; return res;
case BOND_SLAVE_INFO_QUERY_OLD: }
case SIOCBONDSLAVEINFOQUERY:
u_sinfo = (struct ifslave *)ifr->ifr_data;
if (copy_from_user(&k_sinfo, u_sinfo, sizeof(ifslave))) { static struct file_operations bond_info_fops = {
return -EFAULT; .owner = THIS_MODULE,
} .open = bond_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
res = bond_slave_info_query(bond_dev, &k_sinfo); static int bond_create_proc_entry(struct bonding *bond)
if (res == 0) { {
if (copy_to_user(u_sinfo, &k_sinfo, sizeof(ifslave))) { struct net_device *bond_dev = bond->dev;
return -EFAULT;
if (bond_proc_dir) {
bond->proc_entry = create_proc_entry(bond_dev->name,
S_IRUGO,
bond_proc_dir);
if (bond->proc_entry == NULL) {
printk(KERN_WARNING DRV_NAME
": Warning: Cannot create /proc/net/%s/%s\n",
DRV_NAME, bond_dev->name);
} else {
bond->proc_entry->data = bond;
bond->proc_entry->proc_fops = &bond_info_fops;
bond->proc_entry->owner = THIS_MODULE;
memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ);
} }
} }
return res; return 0;
default: }
/* Go on */
break;
}
if (!capable(CAP_NET_ADMIN)) { static void bond_remove_proc_entry(struct bonding *bond)
return -EPERM; {
if (bond_proc_dir && bond->proc_entry) {
remove_proc_entry(bond->proc_file_name, bond_proc_dir);
memset(bond->proc_file_name, 0, IFNAMSIZ);
bond->proc_entry = NULL;
} }
}
if (orig_app_abi_ver == -1) { /* Create the bonding directory under /proc/net, if doesn't exist yet.
/* no orig_app_abi_ver was provided yet, so we'll use the * Caller must hold rtnl_lock.
* current one from now on, even if it's 0
*/ */
orig_app_abi_ver = app_abi_ver; static void bond_create_proc_dir(void)
{
int len = strlen(DRV_NAME);
} else if (orig_app_abi_ver != app_abi_ver) { for (bond_proc_dir = proc_net->subdir; bond_proc_dir;
printk(KERN_ERR DRV_NAME bond_proc_dir = bond_proc_dir->next) {
": Error: already using ifenslave ABI version %d; to " if ((bond_proc_dir->namelen == len) &&
"upgrade ifenslave to version %d, you must first " !memcmp(bond_proc_dir->name, DRV_NAME, len)) {
"reload bonding.\n", break;
orig_app_abi_ver, app_abi_ver); }
return -EINVAL;
} }
slave_dev = dev_get_by_name(ifr->ifr_slave); if (!bond_proc_dir) {
bond_proc_dir = proc_mkdir(DRV_NAME, proc_net);
if (bond_proc_dir) {
bond_proc_dir->owner = THIS_MODULE;
} else {
printk(KERN_WARNING DRV_NAME
": Warning: cannot create /proc/net/%s\n",
DRV_NAME);
}
}
}
dprintk("slave_dev=%p: \n", slave_dev); /* Destroy the bonding directory under /proc/net, if empty.
* Caller must hold rtnl_lock.
*/
static void bond_destroy_proc_dir(void)
{
struct proc_dir_entry *de;
if (!slave_dev) { if (!bond_proc_dir) {
res = -ENODEV; return;
} else {
dprintk("slave_dev->name=%s: \n", slave_dev->name);
switch (cmd) {
case BOND_ENSLAVE_OLD:
case SIOCBONDENSLAVE:
res = bond_enslave(bond_dev, slave_dev);
break;
case BOND_RELEASE_OLD:
case SIOCBONDRELEASE:
res = bond_release(bond_dev, slave_dev);
break;
case BOND_SETHWADDR_OLD:
case SIOCBONDSETHWADDR:
res = bond_sethwaddr(bond_dev, slave_dev);
break;
case BOND_CHANGE_ACTIVE_OLD:
case SIOCBONDCHANGEACTIVE:
if (USES_PRIMARY(bond_mode)) {
res = bond_ioctl_change_active(bond_dev, slave_dev);
} else {
res = -EINVAL;
} }
/* verify that the /proc dir is empty */
for (de = bond_proc_dir->subdir; de; de = de->next) {
/* ignore . and .. */
if (*(de->name) != '.') {
break; break;
default: }
res = -EOPNOTSUPP;
} }
dev_put(slave_dev); if (de) {
if (bond_proc_dir->owner == THIS_MODULE) {
bond_proc_dir->owner = NULL;
} }
} else {
remove_proc_entry(DRV_NAME, proc_net);
bond_proc_dir = NULL;
}
}
#endif /* CONFIG_PROC_FS */
if (res < 0) { /*-------------------------- netdev event handling --------------------------*/
/* The ioctl failed, so there's no point in changing the
* orig_app_abi_ver. We'll restore it's value just in case /*
* we've changed it earlier in this function. * Change device name
*/ */
orig_app_abi_ver = prev_abi_ver; static int bond_event_changename(struct bonding *bond)
{
#ifdef CONFIG_PROC_FS
bond_remove_proc_entry(bond);
bond_create_proc_entry(bond);
#endif
return NOTIFY_DONE;
}
static int bond_master_netdev_event(unsigned long event, struct net_device *bond_dev)
{
struct bonding *event_bond = (struct bonding *)bond_dev->priv;
switch (event) {
case NETDEV_CHANGENAME:
return bond_event_changename(event_bond);
case NETDEV_UNREGISTER:
/*
* TODO: remove a bond from the list?
*/
break;
default:
break;
} }
return res; return NOTIFY_DONE;
} }
#ifdef CONFIG_NET_FASTROUTE static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev)
static int bond_accept_fastpath(struct net_device *bond_dev, struct dst_entry *dst)
{ {
return -1; struct net_device *bond_dev = slave_dev->master;
switch (event) {
case NETDEV_UNREGISTER:
if (bond_dev) {
bond_release(bond_dev, slave_dev);
}
break;
case NETDEV_CHANGE:
/*
* TODO: is this what we get if somebody
* sets up a hierarchical bond, then rmmod's
* one of the slave bonding devices?
*/
break;
case NETDEV_DOWN:
/*
* ... Or is it this?
*/
break;
case NETDEV_CHANGEMTU:
/*
* TODO: Should slaves be allowed to
* independently alter their MTU? For
* an active-backup bond, slaves need
* not be the same type of device, so
* MTUs may vary. For other modes,
* slaves arguably should have the
* same MTUs. To do this, we'd need to
* take over the slave's change_mtu
* function for the duration of their
* servitude.
*/
break;
case NETDEV_CHANGENAME:
/*
* TODO: handle changing the primary's name
*/
break;
default:
break;
}
return NOTIFY_DONE;
} }
#endif
/* /*
* in broadcast mode, we send everything to all usable interfaces. * bond_netdev_event: handle netdev notifier chain events.
*
* This function receives events for the netdev chain. The caller (an
* ioctl handler calling notifier_call_chain) holds the necessary
* locks for us to safely manipulate the slave devices (RTNL lock,
* dev_probe_lock).
*/ */
static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct net_device *event_dev = (struct net_device *)ptr;
struct slave *slave, *start_at;
struct net_device *tx_dev = NULL;
int i;
read_lock(&bond->lock); dprintk("event_dev: %s, event: %lx\n",
(event_dev ? event_dev->name : "None"),
event);
if (!BOND_IS_OK(bond)) { if (event_dev->flags & IFF_MASTER) {
goto free_out; dprintk("IFF_MASTER\n");
return bond_master_netdev_event(event, event_dev);
} }
read_lock(&bond->curr_slave_lock); if (event_dev->flags & IFF_SLAVE) {
start_at = bond->curr_active_slave; dprintk("IFF_SLAVE\n");
read_unlock(&bond->curr_slave_lock); return bond_slave_netdev_event(event, event_dev);
if (!start_at) {
goto free_out;
} }
bond_for_each_slave_from(bond, slave, i, start_at) { return NOTIFY_DONE;
if (IS_UP(slave->dev) && }
(slave->link == BOND_LINK_UP) &&
(slave->state == BOND_STATE_ACTIVE)) {
if (tx_dev) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
printk(KERN_ERR DRV_NAME
": Error: bond_xmit_broadcast(): "
"skb_clone() failed\n");
continue;
}
skb2->dev = tx_dev; static struct notifier_block bond_netdev_notifier = {
skb2->priority = 1; .notifier_call = bond_netdev_event,
dev_queue_xmit(skb2); };
}
tx_dev = slave->dev; /*-------------------------- Packet type handling ---------------------------*/
}
} /* register to receive lacpdus on a bond */
static void bond_register_lacpdu(struct bonding *bond)
{
struct packet_type *pk_type = &(BOND_AD_INFO(bond).ad_pkt_type);
if (tx_dev) { /* initialize packet type */
skb->dev = tx_dev; pk_type->type = PKT_TYPE_LACPDU;
skb->priority = 1; pk_type->dev = bond->dev;
dev_queue_xmit(skb); pk_type->func = bond_3ad_lacpdu_recv;
} else {
goto free_out;
}
out: dev_add_pack(pk_type);
/* frame sent to all suitable interfaces */ }
read_unlock(&bond->lock);
return 0;
free_out: /* unregister to receive lacpdus on a bond */
/* no suitable interface, frame not sent */ static void bond_unregister_lacpdu(struct bonding *bond)
dev_kfree_skb(skb); {
goto out; dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
} }
static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev) /*-------------------------- Device entry points ----------------------------*/
static int bond_open(struct net_device *bond_dev)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *start_at; struct timer_list *mii_timer = &bond->mii_timer;
int i; struct timer_list *arp_timer = &bond->arp_timer;
read_lock(&bond->lock); bond->kill_timers = 0;
if (!BOND_IS_OK(bond)) { if ((bond_mode == BOND_MODE_TLB) ||
goto free_out; (bond_mode == BOND_MODE_ALB)) {
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_mode == BOND_MODE_ALB))) {
/* something went wrong - fail the open operation */
return -1;
} }
read_lock(&bond->curr_slave_lock); init_timer(alb_timer);
slave = start_at = bond->curr_active_slave; alb_timer->expires = jiffies + 1;
read_unlock(&bond->curr_slave_lock); alb_timer->data = (unsigned long)bond;
alb_timer->function = (void *)&bond_alb_monitor;
add_timer(alb_timer);
}
if (!slave) { if (miimon) { /* link check interval, in milliseconds. */
goto free_out; init_timer(mii_timer);
mii_timer->expires = jiffies + 1;
mii_timer->data = (unsigned long)bond_dev;
mii_timer->function = (void *)&bond_mii_monitor;
add_timer(mii_timer);
} }
bond_for_each_slave_from(bond, slave, i, start_at) { if (arp_interval) { /* arp interval, in milliseconds. */
if (IS_UP(slave->dev) && init_timer(arp_timer);
(slave->link == BOND_LINK_UP) && arp_timer->expires = jiffies + 1;
(slave->state == BOND_STATE_ACTIVE)) { arp_timer->data = (unsigned long)bond_dev;
skb->dev = slave->dev; if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
skb->priority = 1; arp_timer->function = (void *)&bond_activebackup_arp_mon;
dev_queue_xmit(skb); } else {
arp_timer->function = (void *)&bond_loadbalance_arp_mon;
}
add_timer(arp_timer);
}
write_lock(&bond->curr_slave_lock); if (bond_mode == BOND_MODE_8023AD) {
bond->curr_active_slave = slave->next; struct timer_list *ad_timer = &(BOND_AD_INFO(bond).ad_timer);
write_unlock(&bond->curr_slave_lock); init_timer(ad_timer);
ad_timer->expires = jiffies + 1;
ad_timer->data = (unsigned long)bond;
ad_timer->function = (void *)&bond_3ad_state_machine_handler;
add_timer(ad_timer);
goto out; /* register to receive LACPDUs */
} bond_register_lacpdu(bond);
} }
out:
read_unlock(&bond->lock);
return 0; return 0;
free_out:
/* no suitable interface, frame not sent */
dev_kfree_skb(skb);
goto out;
} }
/* static int bond_close(struct net_device *bond_dev)
* in XOR mode, we determine the output device by performing xor on
* the source and destination hw adresses. If this device is not
* enabled, find the next slave following this xor slave.
*/
static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct ethhdr *data = (struct ethhdr *)skb->data;
struct slave *slave, *start_at;
int slave_no;
int i;
read_lock(&bond->lock);
if (!BOND_IS_OK(bond)) {
goto free_out;
}
slave_no = (data->h_dest[5]^bond_dev->dev_addr[5]) % bond->slave_cnt;
bond_for_each_slave(bond, slave, i) {
slave_no--;
if (slave_no < 0) {
break;
}
}
start_at = slave; write_lock_bh(&bond->lock);
bond_for_each_slave_from(bond, slave, i, start_at) { bond_mc_list_destroy(bond);
if (IS_UP(slave->dev) &&
(slave->link == BOND_LINK_UP) &&
(slave->state == BOND_STATE_ACTIVE)) {
skb->dev = slave->dev;
skb->priority = 1;
dev_queue_xmit(skb);
goto out; if (bond_mode == BOND_MODE_8023AD) {
} /* Unregister the receive of LACPDUs */
bond_unregister_lacpdu(bond);
} }
out: /* signal timers not to re-arm */
read_unlock(&bond->lock); bond->kill_timers = 1;
return 0;
free_out: write_unlock_bh(&bond->lock);
/* no suitable interface, frame not sent */
dev_kfree_skb(skb);
goto out;
}
/* /* del_timer_sync must run without holding the bond->lock
* in active-backup mode, we know that bond->curr_active_slave is always valid if * because a running timer might be trying to hold it too
* the bond has a usable interface.
*/ */
static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
/* if we are sending arp packets, try to at least if (miimon) { /* link check interval, in milliseconds. */
identify our own ip address */ del_timer_sync(&bond->mii_timer);
if (arp_interval && !my_ip &&
(skb->protocol == __constant_htons(ETH_P_ARP))) {
char *the_ip = (((char *)skb->data)) +
sizeof(struct ethhdr) +
sizeof(struct arphdr) +
ETH_ALEN;
memcpy(&my_ip, the_ip, 4);
} }
read_lock(&bond->lock); if (arp_interval) { /* arp interval, in milliseconds. */
read_lock(&bond->curr_slave_lock); del_timer_sync(&bond->arp_timer);
}
if (!BOND_IS_OK(bond)) { switch (bond_mode) {
goto free_out; case BOND_MODE_8023AD:
del_timer_sync(&(BOND_AD_INFO(bond).ad_timer));
break;
case BOND_MODE_TLB:
case BOND_MODE_ALB:
del_timer_sync(&(BOND_ALB_INFO(bond).alb_timer));
break;
default:
break;
} }
if (bond->curr_active_slave) { /* one usable interface */ /* Release the bonded slaves */
skb->dev = bond->curr_active_slave->dev; bond_release_all(bond_dev);
skb->priority = 1;
dev_queue_xmit(skb); if ((bond_mode == BOND_MODE_TLB) ||
goto out; (bond_mode == BOND_MODE_ALB)) {
} else { /* Must be called only after all
goto free_out; * slaves have been released
*/
bond_alb_deinitialize(bond);
} }
out:
read_unlock(&bond->curr_slave_lock);
read_unlock(&bond->lock);
return 0;
free_out: return 0;
/* no suitable interface, frame not sent */
dev_kfree_skb(skb);
goto out;
} }
static struct net_device_stats *bond_get_stats(struct net_device *bond_dev) static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
...@@ -3183,285 +3134,290 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev) ...@@ -3183,285 +3134,290 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
stats->rx_missed_errors += sstats->rx_missed_errors; stats->rx_missed_errors += sstats->rx_missed_errors;
stats->tx_aborted_errors += sstats->tx_aborted_errors; stats->tx_aborted_errors += sstats->tx_aborted_errors;
stats->tx_carrier_errors += sstats->tx_carrier_errors; stats->tx_carrier_errors += sstats->tx_carrier_errors;
stats->tx_fifo_errors += sstats->tx_fifo_errors; stats->tx_fifo_errors += sstats->tx_fifo_errors;
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
stats->tx_window_errors += sstats->tx_window_errors; stats->tx_window_errors += sstats->tx_window_errors;
}
read_unlock_bh(&bond->lock);
return stats;
}
#ifdef CONFIG_PROC_FS
#define SEQ_START_TOKEN ((void *)1)
static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
{
struct bonding *bond = seq->private;
loff_t off = 0;
struct slave *slave;
int i;
/* make sure the bond won't be taken away */
read_lock(&dev_base_lock);
read_lock_bh(&bond->lock);
if (*pos == 0) {
return SEQ_START_TOKEN;
}
bond_for_each_slave(bond, slave, i) {
if (++off == *pos) {
return slave;
}
}
return NULL;
}
static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct bonding *bond = seq->private;
struct slave *slave = v;
++*pos;
if (v == SEQ_START_TOKEN) {
return bond->first_slave;
} }
slave = slave->next; read_unlock_bh(&bond->lock);
return (slave == bond->first_slave) ? NULL : slave; return stats;
} }
static void bond_info_seq_stop(struct seq_file *seq, void *v) static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
{ {
struct bonding *bond = seq->private; struct net_device *slave_dev = NULL;
struct ifbond *u_binfo = NULL, k_binfo;
struct ifslave *u_sinfo = NULL, k_sinfo;
struct mii_ioctl_data *mii = NULL;
int prev_abi_ver = orig_app_abi_ver;
int res = 0;
read_unlock_bh(&bond->lock); dprintk("bond_ioctl: master=%s, cmd=%d\n",
read_unlock(&dev_base_lock); bond_dev->name, cmd);
}
static void bond_info_show_master(struct seq_file *seq, struct bonding *bond) switch (cmd) {
{ case SIOCETHTOOL:
struct slave *curr; return bond_ethtool_ioctl(bond_dev, ifr);
case SIOCGMIIPHY:
mii = (struct mii_ioctl_data *)&ifr->ifr_data;
if (!mii) {
return -EINVAL;
}
mii->phy_id = 0;
/* Fall Through */
case SIOCGMIIREG:
/*
* We do this again just in case we were called by SIOCGMIIREG
* instead of SIOCGMIIPHY.
*/
mii = (struct mii_ioctl_data *)&ifr->ifr_data;
if (!mii) {
return -EINVAL;
}
if (mii->reg_num == 1) {
struct bonding *bond = (struct bonding *)bond_dev->priv;
mii->val_out = 0;
read_lock_bh(&bond->lock);
read_lock(&bond->curr_slave_lock); read_lock(&bond->curr_slave_lock);
curr = bond->curr_active_slave; if (bond->curr_active_slave) {
mii->val_out = BMSR_LSTATUS;
}
read_unlock(&bond->curr_slave_lock); read_unlock(&bond->curr_slave_lock);
read_unlock_bh(&bond->lock);
}
seq_printf(seq, "Bonding Mode: %s\n", bond_mode_name()); return 0;
case BOND_INFO_QUERY_OLD:
case SIOCBONDINFOQUERY:
u_binfo = (struct ifbond *)ifr->ifr_data;
if (USES_PRIMARY(bond_mode)) { if (copy_from_user(&k_binfo, u_binfo, sizeof(ifbond))) {
if (curr) { return -EFAULT;
seq_printf(seq,
"Currently Active Slave: %s\n",
curr->dev->name);
}
} }
seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down"); res = bond_info_query(bond_dev, &k_binfo);
seq_printf(seq, "MII Polling Interval (ms): %d\n", miimon); if (res == 0) {
seq_printf(seq, "Up Delay (ms): %d\n", updelay * miimon); if (copy_to_user(u_binfo, &k_binfo, sizeof(ifbond))) {
seq_printf(seq, "Down Delay (ms): %d\n", downdelay * miimon); return -EFAULT;
}
}
if (bond_mode == BOND_MODE_8023AD) { return res;
struct ad_info ad_info; case BOND_SLAVE_INFO_QUERY_OLD:
case SIOCBONDSLAVEINFOQUERY:
u_sinfo = (struct ifslave *)ifr->ifr_data;
seq_puts(seq, "\n802.3ad info\n"); if (copy_from_user(&k_sinfo, u_sinfo, sizeof(ifslave))) {
return -EFAULT;
}
if (bond_3ad_get_active_agg_info(bond, &ad_info)) { res = bond_slave_info_query(bond_dev, &k_sinfo);
seq_printf(seq, "bond %s has no active aggregator\n", if (res == 0) {
bond->dev->name); if (copy_to_user(u_sinfo, &k_sinfo, sizeof(ifslave))) {
} else { return -EFAULT;
seq_printf(seq, "Active Aggregator Info:\n"); }
}
seq_printf(seq, "\tAggregator ID: %d\n", return res;
ad_info.aggregator_id); default:
seq_printf(seq, "\tNumber of ports: %d\n", /* Go on */
ad_info.ports); break;
seq_printf(seq, "\tActor Key: %d\n",
ad_info.actor_key);
seq_printf(seq, "\tPartner Key: %d\n",
ad_info.partner_key);
seq_printf(seq, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
ad_info.partner_system[0],
ad_info.partner_system[1],
ad_info.partner_system[2],
ad_info.partner_system[3],
ad_info.partner_system[4],
ad_info.partner_system[5]);
} }
if (!capable(CAP_NET_ADMIN)) {
return -EPERM;
} }
}
static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave) if (orig_app_abi_ver == -1) {
{ /* no orig_app_abi_ver was provided yet, so we'll use the
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name); * current one from now on, even if it's 0
seq_printf(seq, "MII Status: %s\n", */
(slave->link == BOND_LINK_UP) ? "up" : "down"); orig_app_abi_ver = app_abi_ver;
seq_printf(seq, "Link Failure Count: %d\n",
slave->link_failure_count);
if (app_abi_ver >= 1) { } else if (orig_app_abi_ver != app_abi_ver) {
seq_printf(seq, printk(KERN_ERR DRV_NAME
"Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n", ": Error: already using ifenslave ABI version %d; to "
slave->perm_hwaddr[0], "upgrade ifenslave to version %d, you must first "
slave->perm_hwaddr[1], "reload bonding.\n",
slave->perm_hwaddr[2], orig_app_abi_ver, app_abi_ver);
slave->perm_hwaddr[3], return -EINVAL;
slave->perm_hwaddr[4],
slave->perm_hwaddr[5]);
} }
if (bond_mode == BOND_MODE_8023AD) { slave_dev = dev_get_by_name(ifr->ifr_slave);
const struct aggregator *agg
= SLAVE_AD_INFO(slave).port.aggregator;
if (agg) { dprintk("slave_dev=%p: \n", slave_dev);
seq_printf(seq, "Aggregator ID: %d\n",
agg->aggregator_identifier); if (!slave_dev) {
res = -ENODEV;
} else { } else {
seq_puts(seq, "Aggregator ID: N/A\n"); dprintk("slave_dev->name=%s: \n", slave_dev->name);
switch (cmd) {
case BOND_ENSLAVE_OLD:
case SIOCBONDENSLAVE:
res = bond_enslave(bond_dev, slave_dev);
break;
case BOND_RELEASE_OLD:
case SIOCBONDRELEASE:
res = bond_release(bond_dev, slave_dev);
break;
case BOND_SETHWADDR_OLD:
case SIOCBONDSETHWADDR:
res = bond_sethwaddr(bond_dev, slave_dev);
break;
case BOND_CHANGE_ACTIVE_OLD:
case SIOCBONDCHANGEACTIVE:
if (USES_PRIMARY(bond_mode)) {
res = bond_ioctl_change_active(bond_dev, slave_dev);
} else {
res = -EINVAL;
} }
break;
default:
res = -EOPNOTSUPP;
} }
}
static int bond_info_seq_show(struct seq_file *seq, void *v) dev_put(slave_dev);
{
if (v == SEQ_START_TOKEN) {
seq_printf(seq, "%s\n", version);
bond_info_show_master(seq, seq->private);
} else {
bond_info_show_slave(seq, v);
} }
return 0; if (res < 0) {
} /* The ioctl failed, so there's no point in changing the
* orig_app_abi_ver. We'll restore it's value just in case
static struct seq_operations bond_info_seq_ops = { * we've changed it earlier in this function.
.start = bond_info_seq_start, */
.next = bond_info_seq_next, orig_app_abi_ver = prev_abi_ver;
.stop = bond_info_seq_stop,
.show = bond_info_seq_show,
};
static int bond_info_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
struct proc_dir_entry *proc;
int res;
res = seq_open(file, &bond_info_seq_ops);
if (!res) {
/* recover the pointer buried in proc_dir_entry data */
seq = file->private_data;
proc = PDE(inode);
seq->private = proc->data;
} }
return res; return res;
} }
static struct file_operations bond_info_fops = { static void bond_set_multicast_list(struct net_device *bond_dev)
.owner = THIS_MODULE,
.open = bond_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int bond_create_proc_entry(struct bonding *bond)
{ {
struct net_device *bond_dev = bond->dev; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct dev_mc_list *dmi;
if (bond_proc_dir) { write_lock_bh(&bond->lock);
bond->proc_entry = create_proc_entry(bond_dev->name,
S_IRUGO, /*
bond_proc_dir); * Do promisc before checking multicast_mode
if (bond->proc_entry == NULL) { */
printk(KERN_WARNING DRV_NAME if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC)) {
": Warning: Cannot create /proc/net/bonding/%s\n", bond_set_promiscuity(bond, 1);
bond_dev->name);
} else {
bond->proc_entry->data = bond;
bond->proc_entry->proc_fops = &bond_info_fops;
bond->proc_entry->owner = THIS_MODULE;
memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ);
} }
if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC)) {
bond_set_promiscuity(bond, -1);
} }
return 0; /* set allmulti flag to slaves */
} if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI)) {
bond_set_allmulti(bond, 1);
}
static void bond_remove_proc_entry(struct bonding *bond) if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI)) {
{ bond_set_allmulti(bond, -1);
if (bond_proc_dir && bond->proc_entry) {
remove_proc_entry(bond->proc_file_name, bond_proc_dir);
memset(bond->proc_file_name, 0, IFNAMSIZ);
bond->proc_entry = NULL;
} }
}
/* Create the bonding directory under /proc/net, if doesn't exist yet. bond->flags = bond_dev->flags;
* Caller must hold rtnl_lock.
*/
static void bond_create_proc_dir(void)
{
int len = strlen(DRV_NAME);
for (bond_proc_dir = proc_net->subdir; bond_proc_dir; /* looking for addresses to add to slaves' mc list */
bond_proc_dir = bond_proc_dir->next) { for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
if ((bond_proc_dir->namelen == len) && if (!bond_mc_list_find_dmi(dmi, bond->mc_list)) {
!memcmp(bond_proc_dir->name, DRV_NAME, len)) { bond_mc_add(bond, dmi->dmi_addr, dmi->dmi_addrlen);
break;
} }
} }
if (!bond_proc_dir) { /* looking for addresses to delete from slaves' list */
bond_proc_dir = proc_mkdir(DRV_NAME, proc_net); for (dmi = bond->mc_list; dmi; dmi = dmi->next) {
if (bond_proc_dir) { if (!bond_mc_list_find_dmi(dmi, bond_dev->mc_list)) {
bond_proc_dir->owner = THIS_MODULE; bond_mc_delete(bond, dmi->dmi_addr, dmi->dmi_addrlen);
} else {
printk(KERN_WARNING DRV_NAME
": Warning: cannot create /proc/net/%s\n",
DRV_NAME);
} }
} }
/* save master's multicast list */
bond_mc_list_destroy(bond);
bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC);
write_unlock_bh(&bond->lock);
} }
/* Destroy the bonding directory under /proc/net, if empty. /*
* Caller must hold rtnl_lock. * Change the MTU of all of a master's slaves to match the master
*/ */
static void bond_destroy_proc_dir(void) static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
{ {
struct proc_dir_entry *de; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *stop_at;
int res = 0;
int i;
if (!bond_proc_dir) { dprintk("bond=%p, name=%s, new_mtu=%d\n", bond,
return; (bond_dev ? bond_dev->name : "None"), new_mtu);
/* Can't hold bond->lock with bh disabled here since
* some base drivers panic. On the other hand we can't
* hold bond->lock without bh disabled because we'll
* deadlock. The only solution is to rely on the fact
* that we're under rtnl_lock here, and the slaves
* list won't change. This doesn't solve the problem
* of setting the slave's MTU while it is
* transmitting, but the assumption is that the base
* driver can handle that.
*
* TODO: figure out a way to safely iterate the slaves
* list, but without holding a lock around the actual
* call to the base driver.
*/
bond_for_each_slave(bond, slave, i) {
dprintk("s %p s->p %p c_m %p\n", slave,
slave->prev, slave->dev->change_mtu);
if (slave->dev->change_mtu) {
res = slave->dev->change_mtu(slave->dev, new_mtu);
} else {
slave->dev->mtu = new_mtu;
res = 0;
} }
/* verify that the /proc dir is empty */ if (res) {
for (de = bond_proc_dir->subdir; de; de = de->next) { /* If we failed to set the slave's mtu to the new value
/* ignore . and .. */ * we must abort the operation even in ACTIVE_BACKUP
if (*(de->name) != '.') { * mode, because if we allow the backup slaves to have
break; * different mtu values than the active slave we'll
* need to change their mtu when doing a failover. That
* means changing their mtu from timer context, which
* is probably not a good idea.
*/
dprintk("err %d %s\n", res, slave->dev->name);
goto unwind;
} }
} }
if (de) { bond_dev->mtu = new_mtu;
if (bond_proc_dir->owner == THIS_MODULE) {
bond_proc_dir->owner = NULL; return 0;
unwind:
/* unwind from head to the slave that failed */
stop_at = slave;
bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) {
int tmp_res;
if (slave->dev->change_mtu) {
tmp_res = slave->dev->change_mtu(slave->dev, bond_dev->mtu);
if (tmp_res) {
dprintk("unwind err %d dev %s\n", tmp_res,
slave->dev->name);
} }
} else { } else {
remove_proc_entry(DRV_NAME, proc_net); slave->dev->mtu = bond_dev->mtu;
bond_proc_dir = NULL;
} }
}
return res;
} }
#endif /* CONFIG_PROC_FS */
/* /*
* Change HW address * Change HW address
...@@ -3529,7 +3485,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) ...@@ -3529,7 +3485,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
memcpy(tmp_sa.sa_data, bond_dev->dev_addr, bond_dev->addr_len); memcpy(tmp_sa.sa_data, bond_dev->dev_addr, bond_dev->addr_len);
tmp_sa.sa_family = bond_dev->type; tmp_sa.sa_family = bond_dev->type;
/* unwind from the first slave that failed to head */ /* unwind from head to the slave that failed */
stop_at = slave; stop_at = slave;
bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) { bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) {
int tmp_res; int tmp_res;
...@@ -3544,227 +3500,221 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) ...@@ -3544,227 +3500,221 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
return res; return res;
} }
/* static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)
* Change the MTU of all of a master's slaves to match the master
*/
static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
{ {
struct bonding *bond = (struct bonding *)bond_dev->priv; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *stop_at; struct slave *slave, *start_at;
int res = 0;
int i; int i;
dprintk("bond=%p, name=%s, new_mtu=%d\n", bond, read_lock(&bond->lock);
(bond_dev ? bond_dev->name : "None"), new_mtu);
/* Can't hold bond->lock with bh disabled here since if (!BOND_IS_OK(bond)) {
* some base drivers panic. On the other hand we can't goto free_out;
* hold bond->lock without bh disabled because we'll }
* deadlock. The only solution is to rely on the fact
* that we're under rtnl_lock here, and the slaves
* list won't change. This doesn't solve the problem
* of setting the slave's MTU while it is
* transmitting, but the assumption is that the base
* driver can handle that.
*
* TODO: figure out a way to safely iterate the slaves
* list, but without holding a lock around the actual
* call to the base driver.
*/
bond_for_each_slave(bond, slave, i) { read_lock(&bond->curr_slave_lock);
dprintk("s %p s->p %p c_m %p\n", slave, slave = start_at = bond->curr_active_slave;
slave->prev, slave->dev->change_mtu); read_unlock(&bond->curr_slave_lock);
if (slave->dev->change_mtu) {
res = slave->dev->change_mtu(slave->dev, new_mtu); if (!slave) {
} else { goto free_out;
slave->dev->mtu = new_mtu;
res = 0;
} }
if (res) { bond_for_each_slave_from(bond, slave, i, start_at) {
/* If we failed to set the slave's mtu to the new value if (IS_UP(slave->dev) &&
* we must abort the operation even in ACTIVE_BACKUP (slave->link == BOND_LINK_UP) &&
* mode, because if we allow the backup slaves to have (slave->state == BOND_STATE_ACTIVE)) {
* different mtu values than the active slave we'll skb->dev = slave->dev;
* need to change their mtu when doing a failover. That skb->priority = 1;
* means changing their mtu from timer context, which dev_queue_xmit(skb);
* is probably not a good idea.
*/ write_lock(&bond->curr_slave_lock);
dprintk("err %d %s\n", res, slave->dev->name); bond->curr_active_slave = slave->next;
goto unwind; write_unlock(&bond->curr_slave_lock);
goto out;
} }
} }
bond_dev->mtu = new_mtu; out:
read_unlock(&bond->lock);
return 0; return 0;
unwind: free_out:
/* unwind from the first slave that failed to head */ /* no suitable interface, frame not sent */
stop_at = slave; dev_kfree_skb(skb);
bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) { goto out;
int tmp_res; }
if (slave->dev->change_mtu) { /*
tmp_res = slave->dev->change_mtu(slave->dev, bond_dev->mtu); * in active-backup mode, we know that bond->curr_active_slave is always valid if
if (tmp_res) { * the bond has a usable interface.
dprintk("unwind err %d dev %s\n", tmp_res, */
slave->dev->name); static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
/* if we are sending arp packets, try to at least
identify our own ip address */
if (arp_interval && !my_ip &&
(skb->protocol == __constant_htons(ETH_P_ARP))) {
char *the_ip = (char *)skb->data +
sizeof(struct ethhdr) +
sizeof(struct arphdr) +
ETH_ALEN;
memcpy(&my_ip, the_ip, 4);
} }
} else {
slave->dev->mtu = bond_dev->mtu; read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
if (!BOND_IS_OK(bond)) {
goto free_out;
} }
if (bond->curr_active_slave) { /* one usable interface */
skb->dev = bond->curr_active_slave->dev;
skb->priority = 1;
dev_queue_xmit(skb);
goto out;
} else {
goto free_out;
} }
out:
read_unlock(&bond->curr_slave_lock);
read_unlock(&bond->lock);
return 0;
return res; free_out:
/* no suitable interface, frame not sent */
dev_kfree_skb(skb);
goto out;
} }
/* /*
* Change device name * in XOR mode, we determine the output device by performing xor on
* the source and destination hw adresses. If this device is not
* enabled, find the next slave following this xor slave.
*/ */
static int bond_event_changename(struct bonding *bond) static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
{ {
#ifdef CONFIG_PROC_FS struct bonding *bond = (struct bonding *)bond_dev->priv;
bond_remove_proc_entry(bond); struct ethhdr *data = (struct ethhdr *)skb->data;
bond_create_proc_entry(bond); struct slave *slave, *start_at;
#endif int slave_no;
int i;
return NOTIFY_DONE; read_lock(&bond->lock);
}
static int bond_master_netdev_event(unsigned long event, struct net_device *bond_dev) if (!BOND_IS_OK(bond)) {
{ goto free_out;
struct bonding *event_bond = (struct bonding *)bond_dev->priv; }
switch (event) { slave_no = (data->h_dest[5]^bond_dev->dev_addr[5]) % bond->slave_cnt;
case NETDEV_CHANGENAME:
return bond_event_changename(event_bond); bond_for_each_slave(bond, slave, i) {
case NETDEV_UNREGISTER: slave_no--;
/* if (slave_no < 0) {
* TODO: remove a bond from the list?
*/
break;
default:
break; break;
} }
}
return NOTIFY_DONE; start_at = slave;
}
static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev) bond_for_each_slave_from(bond, slave, i, start_at) {
{ if (IS_UP(slave->dev) &&
struct net_device *bond_dev = slave_dev->master; (slave->link == BOND_LINK_UP) &&
(slave->state == BOND_STATE_ACTIVE)) {
skb->dev = slave->dev;
skb->priority = 1;
dev_queue_xmit(skb);
switch (event) { goto out;
case NETDEV_UNREGISTER:
if (bond_dev) {
bond_release(bond_dev, slave_dev);
} }
break;
case NETDEV_CHANGE:
/*
* TODO: is this what we get if somebody
* sets up a hierarchical bond, then rmmod's
* one of the slave bonding devices?
*/
break;
case NETDEV_DOWN:
/*
* ... Or is it this?
*/
break;
case NETDEV_CHANGEMTU:
/*
* TODO: Should slaves be allowed to
* independently alter their MTU? For
* an active-backup bond, slaves need
* not be the same type of device, so
* MTUs may vary. For other modes,
* slaves arguably should have the
* same MTUs. To do this, we'd need to
* take over the slave's change_mtu
* function for the duration of their
* servitude.
*/
break;
case NETDEV_CHANGENAME:
/*
* TODO: handle changing the primary's name
*/
break;
default:
break;
} }
return NOTIFY_DONE; out:
read_unlock(&bond->lock);
return 0;
free_out:
/* no suitable interface, frame not sent */
dev_kfree_skb(skb);
goto out;
} }
/* /*
* bond_netdev_event: handle netdev notifier chain events. * in broadcast mode, we send everything to all usable interfaces.
*
* This function receives events for the netdev chain. The caller (an
* ioctl handler calling notifier_call_chain) holds the necessary
* locks for us to safely manipulate the slave devices (RTNL lock,
* dev_probe_lock).
*/ */
static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
{ {
struct net_device *event_dev = (struct net_device *)ptr; struct bonding *bond = (struct bonding *)bond_dev->priv;
struct slave *slave, *start_at;
struct net_device *tx_dev = NULL;
int i;
dprintk("event_dev: %s, event: %lx\n", read_lock(&bond->lock);
(event_dev ? event_dev->name : "None"),
event);
if (event_dev->flags & IFF_MASTER) { if (!BOND_IS_OK(bond)) {
dprintk("IFF_MASTER\n"); goto free_out;
return bond_master_netdev_event(event, event_dev);
} }
if (event_dev->flags & IFF_SLAVE) { read_lock(&bond->curr_slave_lock);
dprintk("IFF_SLAVE\n"); start_at = bond->curr_active_slave;
return bond_slave_netdev_event(event, event_dev); read_unlock(&bond->curr_slave_lock);
if (!start_at) {
goto free_out;
} }
return NOTIFY_DONE; bond_for_each_slave_from(bond, slave, i, start_at) {
} if (IS_UP(slave->dev) &&
(slave->link == BOND_LINK_UP) &&
(slave->state == BOND_STATE_ACTIVE)) {
if (tx_dev) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
printk(KERN_ERR DRV_NAME
": Error: bond_xmit_broadcast(): "
"skb_clone() failed\n");
continue;
}
static struct notifier_block bond_netdev_notifier = { skb2->dev = tx_dev;
.notifier_call = bond_netdev_event, skb2->priority = 1;
}; dev_queue_xmit(skb2);
}
tx_dev = slave->dev;
}
}
/* De-initialize device specific data. if (tx_dev) {
* Caller must hold rtnl_lock. skb->dev = tx_dev;
*/ skb->priority = 1;
static inline void bond_deinit(struct net_device *bond_dev) dev_queue_xmit(skb);
{ } else {
struct bonding *bond = (struct bonding *)bond_dev->priv; goto free_out;
}
list_del(&bond->bond_list); out:
/* frame sent to all suitable interfaces */
read_unlock(&bond->lock);
return 0;
#ifdef CONFIG_PROC_FS free_out:
bond_remove_proc_entry(bond); /* no suitable interface, frame not sent */
#endif dev_kfree_skb(skb);
goto out;
} }
/* Unregister and free all bond devices. #ifdef CONFIG_NET_FASTROUTE
* Caller must hold rtnl_lock. static int bond_accept_fastpath(struct net_device *bond_dev, struct dst_entry *dst)
*/
static void bond_free_all(void)
{ {
struct bonding *bond, *nxt; return -1;
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
struct net_device *bond_dev = bond->dev;
unregister_netdevice(bond_dev);
bond_deinit(bond_dev);
}
#ifdef CONFIG_PROC_FS
bond_destroy_proc_dir();
#endif
} }
#endif
/*------------------------- Device initialization ---------------------------*/
/* /*
* Does not allocate but creates a /proc entry. * Does not allocate but creates a /proc entry.
...@@ -3788,16 +3738,22 @@ static int __init bond_init(struct net_device *bond_dev) ...@@ -3788,16 +3738,22 @@ static int __init bond_init(struct net_device *bond_dev)
bond->primary_slave = NULL; bond->primary_slave = NULL;
bond->dev = bond_dev; bond->dev = bond_dev;
/* Initialize the device structure. */ /* Initialize the device entry points */
bond_dev->open = bond_open;
bond_dev->stop = bond_close;
bond_dev->get_stats = bond_get_stats;
bond_dev->do_ioctl = bond_do_ioctl;
bond_dev->set_multicast_list = bond_set_multicast_list;
bond_dev->change_mtu = bond_change_mtu;
bond_dev->set_mac_address = bond_set_mac_address; bond_dev->set_mac_address = bond_set_mac_address;
switch (bond_mode) { switch (bond_mode) {
case BOND_MODE_ACTIVEBACKUP:
bond_dev->hard_start_xmit = bond_xmit_activebackup;
break;
case BOND_MODE_ROUNDROBIN: case BOND_MODE_ROUNDROBIN:
bond_dev->hard_start_xmit = bond_xmit_roundrobin; bond_dev->hard_start_xmit = bond_xmit_roundrobin;
break; break;
case BOND_MODE_ACTIVEBACKUP:
bond_dev->hard_start_xmit = bond_xmit_activebackup;
break;
case BOND_MODE_XOR: case BOND_MODE_XOR:
bond_dev->hard_start_xmit = bond_xmit_xor; bond_dev->hard_start_xmit = bond_xmit_xor;
break; break;
...@@ -3805,12 +3761,12 @@ static int __init bond_init(struct net_device *bond_dev) ...@@ -3805,12 +3761,12 @@ static int __init bond_init(struct net_device *bond_dev)
bond_dev->hard_start_xmit = bond_xmit_broadcast; bond_dev->hard_start_xmit = bond_xmit_broadcast;
break; break;
case BOND_MODE_8023AD: case BOND_MODE_8023AD:
bond_dev->hard_start_xmit = bond_3ad_xmit_xor; bond_dev->hard_start_xmit = bond_3ad_xmit_xor; /* extern */
break; break;
case BOND_MODE_TLB: case BOND_MODE_TLB:
case BOND_MODE_ALB: case BOND_MODE_ALB:
bond_dev->hard_start_xmit = bond_alb_xmit; bond_dev->hard_start_xmit = bond_alb_xmit; /* extern */
bond_dev->set_mac_address = bond_alb_set_mac_address; bond_dev->set_mac_address = bond_alb_set_mac_address; /* extern */
break; break;
default: default:
printk(KERN_ERR DRV_NAME printk(KERN_ERR DRV_NAME
...@@ -3819,19 +3775,15 @@ static int __init bond_init(struct net_device *bond_dev) ...@@ -3819,19 +3775,15 @@ static int __init bond_init(struct net_device *bond_dev)
return -EINVAL; return -EINVAL;
} }
bond_dev->get_stats = bond_get_stats; dev->destructor = free_netdev;
bond_dev->open = bond_open;
bond_dev->stop = bond_close;
bond_dev->set_multicast_list = bond_set_multicast_list;
bond_dev->do_ioctl = bond_do_ioctl;
bond_dev->change_mtu = bond_change_mtu;
bond_dev->destructor = free_netdev;
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
#ifdef CONFIG_NET_FASTROUTE #ifdef CONFIG_NET_FASTROUTE
bond_dev->accept_fastpath = bond_accept_fastpath; bond_dev->accept_fastpath = bond_accept_fastpath;
#endif #endif
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
printk(KERN_INFO DRV_NAME ": %s registered with", bond_dev->name); printk(KERN_INFO DRV_NAME ": %s registered with", bond_dev->name);
if (miimon) { if (miimon) {
printk(" MII link monitoring set to %d ms", miimon); printk(" MII link monitoring set to %d ms", miimon);
...@@ -3863,6 +3815,41 @@ static int __init bond_init(struct net_device *bond_dev) ...@@ -3863,6 +3815,41 @@ static int __init bond_init(struct net_device *bond_dev)
return 0; return 0;
} }
/* De-initialize device specific data.
* Caller must hold rtnl_lock.
*/
static inline void bond_deinit(struct net_device *bond_dev)
{
struct bonding *bond = (struct bonding *)bond_dev->priv;
list_del(&bond->bond_list);
#ifdef CONFIG_PROC_FS
bond_remove_proc_entry(bond);
#endif
}
/* Unregister and free all bond devices.
* Caller must hold rtnl_lock.
*/
static void bond_free_all(void)
{
struct bonding *bond, *nxt;
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
struct net_device *bond_dev = bond->dev;
unregister_netdevice(bond_dev);
bond_deinit(bond_dev);
}
#ifdef CONFIG_PROC_FS
bond_destroy_proc_dir();
#endif
}
/*------------------------- Module initialization ---------------------------*/
/* /*
* Convert string input module parms. Accept either the * Convert string input module parms. Accept either the
* number of the mode or its string name. * number of the mode or its string name.
...@@ -4044,7 +4031,7 @@ static int bond_check_params(void) ...@@ -4044,7 +4031,7 @@ static int bond_check_params(void)
arp_interval = BOND_LINK_ARP_INTERV; arp_interval = BOND_LINK_ARP_INTERV;
} }
for (arp_ip_count=0 ; for (arp_ip_count = 0;
(arp_ip_count < MAX_ARP_IP_TARGETS) && arp_ip_target[arp_ip_count]; (arp_ip_count < MAX_ARP_IP_TARGETS) && arp_ip_target[arp_ip_count];
arp_ip_count++) { arp_ip_count++) {
/* not complete check, but should be good enough to /* not complete check, but should be good enough to
...@@ -4176,6 +4163,8 @@ module_init(bonding_init); ...@@ -4176,6 +4163,8 @@ module_init(bonding_init);
module_exit(bonding_exit); module_exit(bonding_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION); MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others");
MODULE_SUPPORTED_DEVICE("most ethernet devices");
/* /*
* Local variables: * Local variables:
......
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
* *
* 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com> * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
* - Added support for Transmit load balancing mode. * - Added support for Transmit load balancing mode.
*
* 2003/09/24 - Shmulik Hen <shmulik.hen at intel dot com>
* - Code cleanup and style changes
*/ */
#ifndef _LINUX_BONDING_H #ifndef _LINUX_BONDING_H
...@@ -29,11 +32,12 @@ ...@@ -29,11 +32,12 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/if_bonding.h>
#include "bond_3ad.h" #include "bond_3ad.h"
#include "bond_alb.h" #include "bond_alb.h"
#define DRV_VERSION "2.4.1" #define DRV_VERSION "2.5.0"
#define DRV_RELDATE "September 15, 2003" #define DRV_RELDATE "December 1, 2003"
#define DRV_NAME "bonding" #define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" #define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
...@@ -70,6 +74,65 @@ ...@@ -70,6 +74,65 @@
((slave)->state == BOND_STATE_ACTIVE)) ((slave)->state == BOND_STATE_ACTIVE))
#define USES_PRIMARY(mode) \
(((mode) == BOND_MODE_ACTIVEBACKUP) || \
((mode) == BOND_MODE_TLB) || \
((mode) == BOND_MODE_ALB))
/*
* Less bad way to call ioctl from within the kernel; this needs to be
* done some other way to get the call out of interrupt context.
* Needs "ioctl" variable to be supplied by calling context.
*/
#define IOCTL(dev, arg, cmd) ({ \
int res = 0; \
mm_segment_t fs = get_fs(); \
set_fs(get_ds()); \
res = ioctl(dev, arg, cmd); \
set_fs(fs); \
res; })
/**
* bond_for_each_slave_from - iterate the slaves list from a starting point
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for max number of moves
* @start: starting point.
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave_from(bond, pos, cnt, start) \
for (cnt = 0, pos = start; \
cnt < (bond)->slave_cnt; \
cnt++, pos = (pos)->next)
/**
* bond_for_each_slave_from_to - iterate the slaves list from start point to stop point
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for number max of moves
* @start: start point.
* @stop: stop point.
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave_from_to(bond, pos, cnt, start, stop) \
for (cnt = 0, pos = start; \
((cnt < (bond)->slave_cnt) && (pos != (stop)->next)); \
cnt++, pos = (pos)->next)
/**
* bond_for_each_slave - iterate the slaves list from head
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for max number of moves
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave(bond, pos, cnt) \
bond_for_each_slave_from(bond, pos, cnt, (bond)->first_slave)
struct slave { struct slave {
struct net_device *dev; /* first - usefull for panic debug */ struct net_device *dev; /* first - usefull for panic debug */
struct slave *next; struct slave *next;
...@@ -120,46 +183,6 @@ struct bonding { ...@@ -120,46 +183,6 @@ struct bonding {
struct alb_bond_info alb_info; struct alb_bond_info alb_info;
}; };
/**
* bond_for_each_slave_from - iterate the slaves list from a starting point
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for max number of moves
* @start: starting point.
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave_from(bond, pos, cnt, start) \
for (cnt = 0, pos = start; \
cnt < (bond)->slave_cnt; \
cnt++, pos = (pos)->next)
/**
* bond_for_each_slave_from_to - iterate the slaves list from start point to stop point
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for number max of moves
* @start: start point.
* @stop: stop point.
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave_from_to(bond, pos, cnt, start, stop) \
for (cnt = 0, pos = start; \
((cnt < (bond)->slave_cnt) && (pos != (stop)->next)); \
cnt++, pos = (pos)->next)
/**
* bond_for_each_slave - iterate the slaves list from head
* @bond: the bond holding this list.
* @pos: current slave.
* @cnt: counter for max number of moves
*
* Caller must hold bond->lock
*/
#define bond_for_each_slave(bond, pos, cnt) \
bond_for_each_slave_from(bond, pos, cnt, (bond)->first_slave)
/** /**
* Returns NULL if the net_device does not belong to any of the bond's slaves * Returns NULL if the net_device does not belong to any of the bond's slaves
* *
...@@ -188,9 +211,17 @@ extern inline struct bonding *bond_get_bond_by_slave(struct slave *slave) ...@@ -188,9 +211,17 @@ extern inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
return (struct bonding *)slave->dev->master->priv; return (struct bonding *)slave->dev->master->priv;
} }
/* Forward declarations */ extern inline void bond_set_slave_inactive_flags(struct slave *slave)
void bond_set_slave_active_flags(struct slave *slave); {
void bond_set_slave_inactive_flags(struct slave *slave); slave->state = BOND_STATE_BACKUP;
slave->dev->flags |= IFF_NOARP;
}
extern inline void bond_set_slave_active_flags(struct slave *slave)
{
slave->state = BOND_STATE_ACTIVE;
slave->dev->flags &= ~IFF_NOARP;
}
#endif /* _LINUX_BONDING_H */ #endif /* _LINUX_BONDING_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment