2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* originally based on the dummy device.
|
|
|
|
*
|
|
|
|
* Copyright 1999, Thomas Davis, tadavis@lbl.gov.
|
|
|
|
* Licensed under the GPL. Based on dummy.c, and eql.c devices.
|
|
|
|
*
|
|
|
|
* bonding.c: an Ethernet Bonding driver
|
|
|
|
*
|
|
|
|
* This is useful to talk to a Cisco EtherChannel compatible equipment:
|
|
|
|
* Cisco 5500
|
|
|
|
* Sun Trunking (Solaris)
|
|
|
|
* Alteon AceDirector Trunks
|
|
|
|
* Linux Bonding
|
|
|
|
* and probably many L2 switches ...
|
|
|
|
*
|
|
|
|
* How it works:
|
|
|
|
* ifconfig bond0 ipaddress netmask up
|
|
|
|
* will setup a network device, with an ip address. No mac address
|
|
|
|
* will be assigned at this time. The hw mac address will come from
|
|
|
|
* the first slave bonded to the channel. All slaves will then use
|
|
|
|
* this hw mac address.
|
|
|
|
*
|
|
|
|
* ifconfig bond0 down
|
|
|
|
* will release all slaves, marking them as down.
|
|
|
|
*
|
|
|
|
* ifenslave bond0 eth0
|
|
|
|
* will attach eth0 to bond0 as a slave. eth0 hw mac address will either
|
|
|
|
* a: be used as initial mac address
|
|
|
|
* b: if a hw mac address already is there, eth0's hw mac address
|
|
|
|
* will then be set from bond0.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/ptrace.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/in.h>
|
2005-06-26 21:54:11 +00:00
|
|
|
#include <net/ip.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/ip.h>
|
2005-06-26 21:54:11 +00:00
|
|
|
#include <linux/tcp.h>
|
|
|
|
#include <linux/udp.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/ctype.h>
|
|
|
|
#include <linux/inet.h>
|
|
|
|
#include <linux/bitops.h>
|
2009-06-12 19:02:48 +00:00
|
|
|
#include <linux/io.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/dma.h>
|
2009-06-12 19:02:48 +00:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/inetdevice.h>
|
bonding: Improve IGMP join processing
In active-backup mode, the current bonding code duplicates IGMP
traffic to all slaves, so that switches are up to date in case of a
failover from an active to a backup interface. If bonding then fails
back to the original active interface, it is likely that the "active
slave" switch's IGMP forwarding for the port will be out of date until
some event occurs to refresh the switch (e.g., a membership query).
This patch alters the behavior of bonding to no longer flood
IGMP to all ports, and to issue IGMP JOINs to the newly active port at
the time of a failover. This insures that switches are kept up to date
for all cases.
"GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally
reported this problem, and included a patch. His original patch was
modified by Jay Vosburgh to additionally remove the existing IGMP flood
behavior, use RCU, streamline code paths, fix trailing white space, and
adjust for style.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-03-01 01:03:37 +00:00
|
|
|
#include <linux/igmp.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <net/sock.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <net/arp.h>
|
|
|
|
#include <linux/mii.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/if_bonding.h>
|
2007-12-07 07:40:33 +00:00
|
|
|
#include <linux/jiffies.h>
|
2005-06-26 21:52:20 +00:00
|
|
|
#include <net/route.h>
|
2007-09-12 10:01:34 +00:00
|
|
|
#include <net/net_namespace.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "bonding.h"
|
|
|
|
#include "bond_3ad.h"
|
|
|
|
#include "bond_alb.h"
|
|
|
|
|
|
|
|
/*---------------------------- Module parameters ----------------------------*/
|
|
|
|
|
|
|
|
/* monitor all links that often (in milliseconds). <=0 disables monitoring */
|
|
|
|
#define BOND_LINK_MON_INTERV 0
|
|
|
|
#define BOND_LINK_ARP_INTERV 0
|
|
|
|
|
|
|
|
static int max_bonds = BOND_DEFAULT_MAX_BONDS;
|
2008-05-18 04:10:12 +00:00
|
|
|
static int num_grat_arp = 1;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
static int num_unsol_na = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
static int miimon = BOND_LINK_MON_INTERV;
|
2009-06-12 19:02:48 +00:00
|
|
|
static int updelay;
|
|
|
|
static int downdelay;
|
2005-04-16 22:20:36 +00:00
|
|
|
static int use_carrier = 1;
|
2009-06-12 19:02:48 +00:00
|
|
|
static char *mode;
|
|
|
|
static char *primary;
|
|
|
|
static char *lacp_rate;
|
|
|
|
static char *ad_select;
|
|
|
|
static char *xmit_hash_policy;
|
2005-04-16 22:20:36 +00:00
|
|
|
static int arp_interval = BOND_LINK_ARP_INTERV;
|
2009-06-12 19:02:48 +00:00
|
|
|
static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
|
|
|
|
static char *arp_validate;
|
|
|
|
static char *fail_over_mac;
|
2009-06-12 19:02:44 +00:00
|
|
|
static struct bond_params bonding_defaults;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
module_param(max_bonds, int, 0);
|
|
|
|
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
|
2008-05-18 04:10:12 +00:00
|
|
|
module_param(num_grat_arp, int, 0644);
|
|
|
|
MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
module_param(num_unsol_na, int, 0644);
|
|
|
|
MODULE_PARM_DESC(num_unsol_na, "Number of unsolicited IPv6 Neighbor Advertisements packets to send on failover event");
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(miimon, int, 0);
|
|
|
|
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
|
|
|
|
module_param(updelay, int, 0);
|
|
|
|
MODULE_PARM_DESC(updelay, "Delay before considering link up, in milliseconds");
|
|
|
|
module_param(downdelay, int, 0);
|
2005-11-09 18:35:03 +00:00
|
|
|
MODULE_PARM_DESC(downdelay, "Delay before considering link down, "
|
|
|
|
"in milliseconds");
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(use_carrier, int, 0);
|
2005-11-09 18:35:03 +00:00
|
|
|
MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; "
|
|
|
|
"0 for off, 1 for on (default)");
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(mode, charp, 0);
|
2005-11-09 18:35:03 +00:00
|
|
|
MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, "
|
|
|
|
"1 for active-backup, 2 for balance-xor, "
|
|
|
|
"3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, "
|
|
|
|
"6 for balance-alb");
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(primary, charp, 0);
|
|
|
|
MODULE_PARM_DESC(primary, "Primary network device to use");
|
|
|
|
module_param(lacp_rate, charp, 0);
|
2005-11-09 18:35:03 +00:00
|
|
|
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner "
|
|
|
|
"(slow/fast)");
|
2008-11-05 01:51:16 +00:00
|
|
|
module_param(ad_select, charp, 0);
|
|
|
|
MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic: stable (0, default), bandwidth (1), count (2)");
|
2005-06-26 21:54:11 +00:00
|
|
|
module_param(xmit_hash_policy, charp, 0);
|
2005-11-09 18:35:03 +00:00
|
|
|
MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method: 0 for layer 2 (default)"
|
|
|
|
", 1 for layer 3+4");
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(arp_interval, int, 0);
|
|
|
|
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
|
|
|
|
module_param_array(arp_ip_target, charp, NULL, 0);
|
|
|
|
MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
|
2006-09-23 04:54:53 +00:00
|
|
|
module_param(arp_validate, charp, 0);
|
|
|
|
MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all");
|
2008-05-18 04:10:14 +00:00
|
|
|
module_param(fail_over_mac, charp, 0);
|
|
|
|
MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the same MAC. none (default), active or follow");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*----------------------------- Global variables ----------------------------*/
|
|
|
|
|
2006-03-04 02:33:57 +00:00
|
|
|
static const char * const version =
|
2005-04-16 22:20:36 +00:00
|
|
|
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
|
|
|
|
|
2005-11-09 18:35:44 +00:00
|
|
|
LIST_HEAD(bond_dev_list);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2009-06-12 19:02:48 +00:00
|
|
|
static struct proc_dir_entry *bond_proc_dir;
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
static __be32 arp_target[BOND_MAX_ARP_TARGETS];
|
|
|
|
static int arp_ip_count;
|
2005-04-16 22:20:36 +00:00
|
|
|
static int bond_mode = BOND_MODE_ROUNDROBIN;
|
2009-06-12 19:02:48 +00:00
|
|
|
static int xmit_hashtype = BOND_XMIT_POLICY_LAYER2;
|
|
|
|
static int lacp_fast;
|
2005-09-26 23:11:50 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 07:10:38 +00:00
|
|
|
const struct bond_parm_tbl bond_lacp_tbl[] = {
|
2005-04-16 22:20:36 +00:00
|
|
|
{ "slow", AD_LACP_SLOW},
|
|
|
|
{ "fast", AD_LACP_FAST},
|
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2008-12-10 07:10:38 +00:00
|
|
|
const struct bond_parm_tbl bond_mode_tbl[] = {
|
2005-04-16 22:20:36 +00:00
|
|
|
{ "balance-rr", BOND_MODE_ROUNDROBIN},
|
|
|
|
{ "active-backup", BOND_MODE_ACTIVEBACKUP},
|
|
|
|
{ "balance-xor", BOND_MODE_XOR},
|
|
|
|
{ "broadcast", BOND_MODE_BROADCAST},
|
|
|
|
{ "802.3ad", BOND_MODE_8023AD},
|
|
|
|
{ "balance-tlb", BOND_MODE_TLB},
|
|
|
|
{ "balance-alb", BOND_MODE_ALB},
|
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2008-12-10 07:10:38 +00:00
|
|
|
const struct bond_parm_tbl xmit_hashtype_tbl[] = {
|
2005-06-26 21:54:11 +00:00
|
|
|
{ "layer2", BOND_XMIT_POLICY_LAYER2},
|
|
|
|
{ "layer3+4", BOND_XMIT_POLICY_LAYER34},
|
2007-12-07 07:40:34 +00:00
|
|
|
{ "layer2+3", BOND_XMIT_POLICY_LAYER23},
|
2005-06-26 21:54:11 +00:00
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2008-12-10 07:10:38 +00:00
|
|
|
const struct bond_parm_tbl arp_validate_tbl[] = {
|
2006-09-23 04:54:53 +00:00
|
|
|
{ "none", BOND_ARP_VALIDATE_NONE},
|
|
|
|
{ "active", BOND_ARP_VALIDATE_ACTIVE},
|
|
|
|
{ "backup", BOND_ARP_VALIDATE_BACKUP},
|
|
|
|
{ "all", BOND_ARP_VALIDATE_ALL},
|
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2008-12-10 07:10:38 +00:00
|
|
|
const struct bond_parm_tbl fail_over_mac_tbl[] = {
|
2008-05-18 04:10:14 +00:00
|
|
|
{ "none", BOND_FOM_NONE},
|
|
|
|
{ "active", BOND_FOM_ACTIVE},
|
|
|
|
{ "follow", BOND_FOM_FOLLOW},
|
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2008-11-05 01:51:16 +00:00
|
|
|
struct bond_parm_tbl ad_select_tbl[] = {
|
|
|
|
{ "stable", BOND_AD_STABLE},
|
|
|
|
{ "bandwidth", BOND_AD_BANDWIDTH},
|
|
|
|
{ "count", BOND_AD_COUNT},
|
|
|
|
{ NULL, -1},
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*-------------------------- Forward declarations ---------------------------*/
|
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
static void bond_send_gratuitous_arp(struct bonding *bond);
|
2007-10-24 16:23:17 +00:00
|
|
|
static void bond_deinit(struct net_device *bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*---------------------------- General routines -----------------------------*/
|
|
|
|
|
2007-07-09 18:51:12 +00:00
|
|
|
static const char *bond_mode_name(int mode)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-12-10 07:08:09 +00:00
|
|
|
static const char *names[] = {
|
|
|
|
[BOND_MODE_ROUNDROBIN] = "load balancing (round-robin)",
|
|
|
|
[BOND_MODE_ACTIVEBACKUP] = "fault-tolerance (active-backup)",
|
|
|
|
[BOND_MODE_XOR] = "load balancing (xor)",
|
|
|
|
[BOND_MODE_BROADCAST] = "fault-tolerance (broadcast)",
|
2009-06-12 19:02:48 +00:00
|
|
|
[BOND_MODE_8023AD] = "IEEE 802.3ad Dynamic link aggregation",
|
2008-12-10 07:08:09 +00:00
|
|
|
[BOND_MODE_TLB] = "transmit load balancing",
|
|
|
|
[BOND_MODE_ALB] = "adaptive load balancing",
|
|
|
|
};
|
|
|
|
|
|
|
|
if (mode < 0 || mode > BOND_MODE_ALB)
|
2005-04-16 22:20:36 +00:00
|
|
|
return "unknown";
|
2008-12-10 07:08:09 +00:00
|
|
|
|
|
|
|
return names[mode];
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------- VLAN -----------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_add_vlan - add a new vlan id on bond
|
|
|
|
* @bond: bond that got the notification
|
|
|
|
* @vlan_id: the vlan id to add
|
|
|
|
*
|
|
|
|
* Returns -ENOMEM if allocation failed.
|
|
|
|
*/
|
|
|
|
static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
|
{
|
|
|
|
struct vlan_entry *vlan;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond: %s, vlan id %d\n",
|
2009-06-12 19:02:48 +00:00
|
|
|
(bond ? bond->dev->name : "None"), vlan_id);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!vlan)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&vlan->vlan_list);
|
|
|
|
vlan->vlan_id = vlan_id;
|
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
|
|
|
list_add_tail(&vlan->vlan_list, &bond->vlan_list);
|
|
|
|
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("added VLAN ID %d on bond %s\n", vlan_id, bond->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_del_vlan - delete a vlan id from bond
|
|
|
|
* @bond: bond that got the notification
|
|
|
|
* @vlan_id: the vlan id to delete
|
|
|
|
*
|
|
|
|
* returns -ENODEV if @vlan_id was not found in @bond.
|
|
|
|
*/
|
|
|
|
static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
|
|
|
|
{
|
2008-05-18 04:10:10 +00:00
|
|
|
struct vlan_entry *vlan;
|
2005-04-16 22:20:36 +00:00
|
|
|
int res = -ENODEV;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond: %s, vlan id %d\n", bond->dev->name, vlan_id);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
2008-05-18 04:10:10 +00:00
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (vlan->vlan_id == vlan_id) {
|
|
|
|
list_del(&vlan->vlan_list);
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_alb_clear_vlan(bond, vlan_id);
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("removed VLAN ID %d from bond %s\n", vlan_id,
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->dev->name);
|
|
|
|
|
|
|
|
kfree(vlan);
|
|
|
|
|
|
|
|
if (list_empty(&bond->vlan_list) &&
|
|
|
|
(bond->slave_cnt == 0)) {
|
|
|
|
/* Last VLAN removed and no slaves, so
|
|
|
|
* restore block on adding VLANs. This will
|
|
|
|
* be removed once new slaves that are not
|
|
|
|
* VLAN challenged will be added.
|
|
|
|
*/
|
|
|
|
bond->dev->features |= NETIF_F_VLAN_CHALLENGED;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("couldn't find VLAN ID %d in bond %s\n", vlan_id,
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->dev->name);
|
|
|
|
|
|
|
|
out:
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_has_challenged_slaves
|
|
|
|
* @bond: the bond we're working on
|
|
|
|
*
|
|
|
|
* Searches the slave list. Returns 1 if a vlan challenged slave
|
|
|
|
* was found, 0 otherwise.
|
|
|
|
*
|
|
|
|
* Assumes bond->lock is held.
|
|
|
|
*/
|
|
|
|
static int bond_has_challenged_slaves(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
if (slave->dev->features & NETIF_F_VLAN_CHALLENGED) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("found VLAN challenged slave - %s\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("no VLAN challenged slaves found\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_next_vlan - safely skip to the next item in the vlans list.
|
|
|
|
* @bond: the bond we're working on
|
|
|
|
* @curr: item we're advancing from
|
|
|
|
*
|
|
|
|
* Returns %NULL if list is empty, bond->next_vlan if @curr is %NULL,
|
|
|
|
* or @curr->next otherwise (even if it is @curr itself again).
|
2009-06-12 19:02:48 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
* Caller must hold bond->lock
|
|
|
|
*/
|
|
|
|
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr)
|
|
|
|
{
|
|
|
|
struct vlan_entry *next, *last;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (list_empty(&bond->vlan_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!curr) {
|
|
|
|
next = list_entry(bond->vlan_list.next,
|
|
|
|
struct vlan_entry, vlan_list);
|
|
|
|
} else {
|
|
|
|
last = list_entry(bond->vlan_list.prev,
|
|
|
|
struct vlan_entry, vlan_list);
|
|
|
|
if (last == curr) {
|
|
|
|
next = list_entry(bond->vlan_list.next,
|
|
|
|
struct vlan_entry, vlan_list);
|
|
|
|
} else {
|
|
|
|
next = list_entry(curr->vlan_list.next,
|
|
|
|
struct vlan_entry, vlan_list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_dev_queue_xmit - Prepare skb for xmit.
|
2009-06-12 19:02:48 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
* @bond: bond device that got this skb for tx.
|
|
|
|
* @skb: hw accel VLAN tagged skb to transmit
|
|
|
|
* @slave_dev: slave that is supposed to xmit this skbuff
|
2009-06-12 19:02:48 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
* When the bond gets an skb to transmit that is
|
|
|
|
* already hardware accelerated VLAN tagged, and it
|
|
|
|
* needs to relay this skb to a slave that is not
|
|
|
|
* hw accel capable, the skb needs to be "unaccelerated",
|
|
|
|
* i.e. strip the hwaccel tag and re-insert it as part
|
|
|
|
* of the payload.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
|
|
|
|
struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-03-22 05:29:34 +00:00
|
|
|
unsigned short uninitialized_var(vlan_id);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!list_empty(&bond->vlan_list) &&
|
|
|
|
!(slave_dev->features & NETIF_F_HW_VLAN_TX) &&
|
|
|
|
vlan_get_tag(skb, &vlan_id) == 0) {
|
|
|
|
skb->dev = slave_dev;
|
|
|
|
skb = vlan_put_tag(skb, vlan_id);
|
|
|
|
if (!skb) {
|
|
|
|
/* vlan_put_tag() frees the skb in case of error,
|
|
|
|
* so return success here so the calling functions
|
|
|
|
* won't attempt to free is again.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
skb->dev = slave_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb->priority = 1;
|
|
|
|
dev_queue_xmit(skb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the following 3 functions, bond_vlan_rx_register(), bond_vlan_rx_add_vid
|
|
|
|
* and bond_vlan_rx_kill_vid, We don't protect the slave list iteration with a
|
|
|
|
* lock because:
|
|
|
|
* a. This operation is performed in IOCTL context,
|
|
|
|
* b. The operation is protected by the RTNL semaphore in the 8021q code,
|
|
|
|
* c. Holding a lock with BH disabled while directly calling a base driver
|
|
|
|
* entry point is generally a BAD idea.
|
2009-06-12 19:02:48 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
* The design of synchronization/protection for this operation in the 8021q
|
|
|
|
* module is good for one or more VLAN devices over a single physical device
|
|
|
|
* and cannot be extended for a teaming solution like bonding, so there is a
|
|
|
|
* potential race condition here where a net device from the vlan group might
|
|
|
|
* be referenced (either by a base driver or the 8021q code) while it is being
|
|
|
|
* removed from the system. However, it turns out we're not making matters
|
|
|
|
* worse, and if it works for regular VLAN usage it will work here too.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_vlan_rx_register - Propagates registration to slaves
|
|
|
|
* @bond_dev: bonding net device that got called
|
|
|
|
* @grp: vlan group being registered
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static void bond_vlan_rx_register(struct net_device *bond_dev,
|
|
|
|
struct vlan_group *grp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
bond->vlgrp = grp;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
struct net_device *slave_dev = slave->dev;
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_register) {
|
|
|
|
slave_ops->ndo_vlan_rx_register(slave_dev, grp);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_vlan_rx_add_vid - Propagates adding an id to slaves
|
|
|
|
* @bond_dev: bonding net device that got called
|
|
|
|
* @vid: vlan id being added
|
|
|
|
*/
|
|
|
|
static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
|
|
|
int i, res;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
struct net_device *slave_dev = slave->dev;
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_add_vid) {
|
|
|
|
slave_ops->ndo_vlan_rx_add_vid(slave_dev, vid);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = bond_add_vlan(bond, vid);
|
|
|
|
if (res) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: Failed to add vlan id %d\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name, vid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_vlan_rx_kill_vid - Propagates deleting an id to slaves
|
|
|
|
* @bond_dev: bonding net device that got called
|
|
|
|
* @vid: vlan id being removed
|
|
|
|
*/
|
|
|
|
static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
|
|
|
struct net_device *vlan_dev;
|
|
|
|
int i, res;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
struct net_device *slave_dev = slave->dev;
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_kill_vid) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Save and then restore vlan_dev in the grp array,
|
|
|
|
* since the slave's driver might clear it.
|
|
|
|
*/
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_dev = vlan_group_get_device(bond->vlgrp, vid);
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid);
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_group_set_device(bond->vlgrp, vid, vlan_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = bond_del_vlan(bond, vid);
|
|
|
|
if (res) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: Failed to remove vlan id %d\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name, vid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *slave_dev)
|
|
|
|
{
|
|
|
|
struct vlan_entry *vlan;
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (list_empty(&bond->vlan_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_register)
|
|
|
|
slave_ops->ndo_vlan_rx_register(slave_dev, bond->vlgrp);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
|
2008-11-20 05:56:05 +00:00
|
|
|
!(slave_ops->ndo_vlan_rx_add_vid))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list)
|
|
|
|
slave_ops->ndo_vlan_rx_add_vid(slave_dev, vlan->vlan_id);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
static void bond_del_vlans_from_slave(struct bonding *bond,
|
|
|
|
struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct vlan_entry *vlan;
|
|
|
|
struct net_device *vlan_dev;
|
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (list_empty(&bond->vlan_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
|
2008-11-20 05:56:05 +00:00
|
|
|
!(slave_ops->ndo_vlan_rx_kill_vid))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto unreg;
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
|
|
|
/* Save and then restore vlan_dev in the grp array,
|
|
|
|
* since the slave's driver might clear it.
|
|
|
|
*/
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unreg:
|
|
|
|
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_vlan_rx_register)
|
|
|
|
slave_ops->ndo_vlan_rx_register(slave_dev, NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------- Link status -------------------------------*/
|
|
|
|
|
2006-03-27 21:27:43 +00:00
|
|
|
/*
|
|
|
|
* Set the carrier state for the master according to the state of its
|
|
|
|
* slaves. If any slaves are up, the master is up. In 802.3ad mode,
|
|
|
|
* do special 802.3ad magic.
|
|
|
|
*
|
|
|
|
* Returns zero if carrier state does not change, nonzero if it does.
|
|
|
|
*/
|
|
|
|
static int bond_set_carrier(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (bond->slave_cnt == 0)
|
|
|
|
goto down;
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
|
|
|
return bond_3ad_set_carrier(bond);
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
if (slave->link == BOND_LINK_UP) {
|
|
|
|
if (!netif_carrier_ok(bond->dev)) {
|
|
|
|
netif_carrier_on(bond->dev);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
down:
|
|
|
|
if (netif_carrier_ok(bond->dev)) {
|
|
|
|
netif_carrier_off(bond->dev);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Get link speed and duplex from the slave's base driver
|
|
|
|
* using ethtool. If for some reason the call fails or the
|
|
|
|
* values are invalid, fake speed and duplex to 100/Full
|
|
|
|
* and return error.
|
|
|
|
*/
|
|
|
|
static int bond_update_speed_duplex(struct slave *slave)
|
|
|
|
{
|
|
|
|
struct net_device *slave_dev = slave->dev;
|
|
|
|
struct ethtool_cmd etool;
|
2007-07-31 21:00:02 +00:00
|
|
|
int res;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Fake speed and duplex */
|
|
|
|
slave->speed = SPEED_100;
|
|
|
|
slave->duplex = DUPLEX_FULL;
|
|
|
|
|
2007-07-31 21:00:02 +00:00
|
|
|
if (!slave_dev->ethtool_ops || !slave_dev->ethtool_ops->get_settings)
|
|
|
|
return -1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-31 21:00:02 +00:00
|
|
|
res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool);
|
|
|
|
if (res < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (etool.speed) {
|
|
|
|
case SPEED_10:
|
|
|
|
case SPEED_100:
|
|
|
|
case SPEED_1000:
|
2006-09-23 04:52:15 +00:00
|
|
|
case SPEED_10000:
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (etool.duplex) {
|
|
|
|
case DUPLEX_FULL:
|
|
|
|
case DUPLEX_HALF:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
slave->speed = etool.speed;
|
|
|
|
slave->duplex = etool.duplex;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if <dev> supports MII link status reporting, check its link status.
|
|
|
|
*
|
|
|
|
* We either do MII/ETHTOOL ioctls, or check netif_carrier_ok(),
|
2009-06-12 19:02:48 +00:00
|
|
|
* depending upon the setting of the use_carrier parameter.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* Return either BMSR_LSTATUS, meaning that the link is up (or we
|
|
|
|
* can't tell and just pretend it is), or 0, meaning that the link is
|
|
|
|
* down.
|
|
|
|
*
|
|
|
|
* If reporting is non-zero, instead of faking link up, return -1 if
|
|
|
|
* both ETHTOOL and MII ioctls fail (meaning the device does not
|
|
|
|
* support them). If use_carrier is set, return whatever it says.
|
|
|
|
* It'd be nice if there was a good way to tell if a driver supports
|
|
|
|
* netif_carrier, but there really isn't.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static int bond_check_dev_link(struct bonding *bond,
|
|
|
|
struct net_device *slave_dev, int reporting)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2009-06-12 19:02:48 +00:00
|
|
|
static int (*ioctl)(struct net_device *, struct ifreq *, int);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct ifreq ifr;
|
|
|
|
struct mii_ioctl_data *mii;
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (bond->params.use_carrier)
|
2005-04-16 22:20:36 +00:00
|
|
|
return netif_carrier_ok(slave_dev) ? BMSR_LSTATUS : 0;
|
|
|
|
|
2009-04-24 01:58:23 +00:00
|
|
|
/* Try to get link status using Ethtool first. */
|
|
|
|
if (slave_dev->ethtool_ops) {
|
|
|
|
if (slave_dev->ethtool_ops->get_link) {
|
|
|
|
u32 link;
|
|
|
|
|
|
|
|
link = slave_dev->ethtool_ops->get_link(slave_dev);
|
|
|
|
|
|
|
|
return link ? BMSR_LSTATUS : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
/* Ethtool can't be used, fallback to MII ioctls. */
|
2008-11-20 05:56:05 +00:00
|
|
|
ioctl = slave_ops->ndo_do_ioctl;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ioctl) {
|
|
|
|
/* TODO: set pointer to correct ioctl on a per team member */
|
|
|
|
/* bases to make this more efficient. that is, once */
|
|
|
|
/* we determine the correct ioctl, we will always */
|
|
|
|
/* call it and not the others for that team */
|
|
|
|
/* member. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We cannot assume that SIOCGMIIPHY will also read a
|
|
|
|
* register; not all network drivers (e.g., e100)
|
|
|
|
* support that.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Yes, the mii is overlaid on the ifreq.ifr_ifru */
|
|
|
|
strncpy(ifr.ifr_name, slave_dev->name, IFNAMSIZ);
|
|
|
|
mii = if_mii(&ifr);
|
|
|
|
if (IOCTL(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
|
|
|
|
mii->reg_num = MII_BMSR;
|
2009-06-12 19:02:48 +00:00
|
|
|
if (IOCTL(slave_dev, &ifr, SIOCGMIIREG) == 0)
|
|
|
|
return mii->val_out & BMSR_LSTATUS;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If reporting, report that either there's no dev->do_ioctl,
|
2007-07-31 21:00:02 +00:00
|
|
|
* or both SIOCGMIIREG and get_link failed (meaning that we
|
2005-04-16 22:20:36 +00:00
|
|
|
* cannot report link status). If not reporting, pretend
|
|
|
|
* we're ok.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
return reporting ? -1 : BMSR_LSTATUS;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------- Multicast list ------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns 0 if dmi1 and dmi2 are the same, non-0 otherwise
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static inline int bond_is_dmi_same(const struct dev_mc_list *dmi1,
|
|
|
|
const struct dev_mc_list *dmi2)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
return memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0 &&
|
|
|
|
dmi1->dmi_addrlen == dmi2->dmi_addrlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* returns dmi entry if found, NULL otherwise
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static struct dev_mc_list *bond_mc_list_find_dmi(struct dev_mc_list *dmi,
|
|
|
|
struct dev_mc_list *mc_list)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct dev_mc_list *idmi;
|
|
|
|
|
|
|
|
for (idmi = mc_list; idmi; idmi = idmi->next) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond_is_dmi_same(dmi, idmi))
|
2005-04-16 22:20:36 +00:00
|
|
|
return idmi;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Push the promiscuity flag down to appropriate slaves
|
|
|
|
*/
|
2008-07-15 03:51:36 +00:00
|
|
|
static int bond_set_promiscuity(struct bonding *bond, int inc)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-07-15 03:51:36 +00:00
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* write lock already acquired */
|
|
|
|
if (bond->curr_active_slave) {
|
2008-07-15 03:51:36 +00:00
|
|
|
err = dev_set_promiscuity(bond->curr_active_slave->dev,
|
|
|
|
inc);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2008-07-15 03:51:36 +00:00
|
|
|
err = dev_set_promiscuity(slave->dev, inc);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-15 03:51:36 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Push the allmulti flag down to all slaves
|
|
|
|
*/
|
2008-07-15 03:51:36 +00:00
|
|
|
static int bond_set_allmulti(struct bonding *bond, int inc)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-07-15 03:51:36 +00:00
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* write lock already acquired */
|
|
|
|
if (bond->curr_active_slave) {
|
2008-07-15 03:51:36 +00:00
|
|
|
err = dev_set_allmulti(bond->curr_active_slave->dev,
|
|
|
|
inc);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2008-07-15 03:51:36 +00:00
|
|
|
err = dev_set_allmulti(slave->dev, inc);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-15 03:51:36 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a Multicast address to slaves
|
|
|
|
* according to mode
|
|
|
|
*/
|
|
|
|
static void bond_mc_add(struct bonding *bond, void *addr, int alen)
|
|
|
|
{
|
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* write lock already acquired */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->curr_active_slave)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_mc_add(bond->curr_active_slave->dev, addr, alen, 0);
|
|
|
|
} else {
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_mc_add(slave->dev, addr, alen, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a multicast address from slave
|
|
|
|
* according to mode
|
|
|
|
*/
|
|
|
|
static void bond_mc_delete(struct bonding *bond, void *addr, int alen)
|
|
|
|
{
|
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* write lock already acquired */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->curr_active_slave)
|
|
|
|
dev_mc_delete(bond->curr_active_slave->dev, addr,
|
|
|
|
alen, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
dev_mc_delete(slave->dev, addr, alen, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
bonding: Improve IGMP join processing
In active-backup mode, the current bonding code duplicates IGMP
traffic to all slaves, so that switches are up to date in case of a
failover from an active to a backup interface. If bonding then fails
back to the original active interface, it is likely that the "active
slave" switch's IGMP forwarding for the port will be out of date until
some event occurs to refresh the switch (e.g., a membership query).
This patch alters the behavior of bonding to no longer flood
IGMP to all ports, and to issue IGMP JOINs to the newly active port at
the time of a failover. This insures that switches are kept up to date
for all cases.
"GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally
reported this problem, and included a patch. His original patch was
modified by Jay Vosburgh to additionally remove the existing IGMP flood
behavior, use RCU, streamline code paths, fix trailing white space, and
adjust for style.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-03-01 01:03:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the list of registered multicast addresses for the bonding
|
|
|
|
* device and retransmit an IGMP JOIN request to the current active
|
|
|
|
* slave.
|
|
|
|
*/
|
|
|
|
static void bond_resend_igmp_join_requests(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct in_device *in_dev;
|
|
|
|
struct ip_mc_list *im;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
in_dev = __in_dev_get_rcu(bond->dev);
|
|
|
|
if (in_dev) {
|
2009-06-12 19:02:48 +00:00
|
|
|
for (im = in_dev->mc_list; im; im = im->next)
|
bonding: Improve IGMP join processing
In active-backup mode, the current bonding code duplicates IGMP
traffic to all slaves, so that switches are up to date in case of a
failover from an active to a backup interface. If bonding then fails
back to the original active interface, it is likely that the "active
slave" switch's IGMP forwarding for the port will be out of date until
some event occurs to refresh the switch (e.g., a membership query).
This patch alters the behavior of bonding to no longer flood
IGMP to all ports, and to issue IGMP JOINs to the newly active port at
the time of a failover. This insures that switches are kept up to date
for all cases.
"GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally
reported this problem, and included a patch. His original patch was
modified by Jay Vosburgh to additionally remove the existing IGMP flood
behavior, use RCU, streamline code paths, fix trailing white space, and
adjust for style.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-03-01 01:03:37 +00:00
|
|
|
ip_mc_rejoin_group(im);
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
2009-06-12 19:02:48 +00:00
|
|
|
|
|
|
|
bond->mc_list = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy all the Multicast addresses from src to the bonding device dst
|
|
|
|
*/
|
2005-10-05 05:39:41 +00:00
|
|
|
static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond,
|
2005-10-07 06:46:04 +00:00
|
|
|
gfp_t gfp_flag)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct dev_mc_list *dmi, *new_dmi;
|
|
|
|
|
|
|
|
for (dmi = mc_list; dmi; dmi = dmi->next) {
|
2005-10-05 05:39:41 +00:00
|
|
|
new_dmi = kmalloc(sizeof(struct dev_mc_list), gfp_flag);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!new_dmi) {
|
|
|
|
/* FIXME: Potential memory leak !!! */
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_dmi->next = bond->mc_list;
|
|
|
|
bond->mc_list = new_dmi;
|
|
|
|
new_dmi->dmi_addrlen = dmi->dmi_addrlen;
|
|
|
|
memcpy(new_dmi->dmi_addr, dmi->dmi_addr, dmi->dmi_addrlen);
|
|
|
|
new_dmi->dmi_users = dmi->dmi_users;
|
|
|
|
new_dmi->dmi_gusers = dmi->dmi_gusers;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* flush all members of flush->mc_list from device dev->mc_list
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static void bond_mc_list_flush(struct net_device *bond_dev,
|
|
|
|
struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct dev_mc_list *dmi;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_mc_delete(slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
/* del lacpdu mc addr from mc list */
|
|
|
|
u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
|
|
|
|
|
|
|
|
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
|
|
|
|
* old active slaves (if any) according to the multicast mode, and
|
|
|
|
* promiscuous flags unconditionally.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
|
|
|
|
struct slave *old_active)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct dev_mc_list *dmi;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!USES_PRIMARY(bond->params.mode))
|
2005-04-16 22:20:36 +00:00
|
|
|
/* nothing to do - mc list is already up-to-date on
|
|
|
|
* all slaves
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (old_active) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->dev->flags & IFF_PROMISC)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_promiscuity(old_active->dev, -1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->dev->flags & IFF_ALLMULTI)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_allmulti(old_active->dev, -1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next)
|
|
|
|
dev_mc_delete(old_active->dev, dmi->dmi_addr,
|
|
|
|
dmi->dmi_addrlen, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (new_active) {
|
2008-07-15 03:51:36 +00:00
|
|
|
/* FIXME: Signal errors upstream. */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->dev->flags & IFF_PROMISC)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_promiscuity(new_active->dev, 1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->dev->flags & IFF_ALLMULTI)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_allmulti(new_active->dev, 1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next)
|
|
|
|
dev_mc_add(new_active->dev, dmi->dmi_addr,
|
|
|
|
dmi->dmi_addrlen, 0);
|
bonding: Improve IGMP join processing
In active-backup mode, the current bonding code duplicates IGMP
traffic to all slaves, so that switches are up to date in case of a
failover from an active to a backup interface. If bonding then fails
back to the original active interface, it is likely that the "active
slave" switch's IGMP forwarding for the port will be out of date until
some event occurs to refresh the switch (e.g., a membership query).
This patch alters the behavior of bonding to no longer flood
IGMP to all ports, and to issue IGMP JOINs to the newly active port at
the time of a failover. This insures that switches are kept up to date
for all cases.
"GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally
reported this problem, and included a patch. His original patch was
modified by Jay Vosburgh to additionally remove the existing IGMP flood
behavior, use RCU, streamline code paths, fix trailing white space, and
adjust for style.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-03-01 01:03:37 +00:00
|
|
|
bond_resend_igmp_join_requests(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
/*
|
|
|
|
* bond_do_fail_over_mac
|
|
|
|
*
|
|
|
|
* Perform special MAC address swapping for fail_over_mac settings
|
|
|
|
*
|
|
|
|
* Called with RTNL, bond->lock for read, curr_slave_lock for write_bh.
|
|
|
|
*/
|
|
|
|
static void bond_do_fail_over_mac(struct bonding *bond,
|
|
|
|
struct slave *new_active,
|
|
|
|
struct slave *old_active)
|
2009-02-14 11:15:33 +00:00
|
|
|
__releases(&bond->curr_slave_lock)
|
|
|
|
__releases(&bond->lock)
|
|
|
|
__acquires(&bond->lock)
|
|
|
|
__acquires(&bond->curr_slave_lock)
|
2008-05-18 04:10:14 +00:00
|
|
|
{
|
|
|
|
u8 tmp_mac[ETH_ALEN];
|
|
|
|
struct sockaddr saddr;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
switch (bond->params.fail_over_mac) {
|
|
|
|
case BOND_FOM_ACTIVE:
|
|
|
|
if (new_active)
|
|
|
|
memcpy(bond->dev->dev_addr, new_active->dev->dev_addr,
|
|
|
|
new_active->dev->addr_len);
|
|
|
|
break;
|
|
|
|
case BOND_FOM_FOLLOW:
|
|
|
|
/*
|
|
|
|
* if new_active && old_active, swap them
|
|
|
|
* if just old_active, do nothing (going to no active slave)
|
|
|
|
* if just new_active, set new_active to bond's MAC
|
|
|
|
*/
|
|
|
|
if (!new_active)
|
|
|
|
return;
|
|
|
|
|
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
|
|
if (old_active) {
|
|
|
|
memcpy(tmp_mac, new_active->dev->dev_addr, ETH_ALEN);
|
|
|
|
memcpy(saddr.sa_data, old_active->dev->dev_addr,
|
|
|
|
ETH_ALEN);
|
|
|
|
saddr.sa_family = new_active->dev->type;
|
|
|
|
} else {
|
|
|
|
memcpy(saddr.sa_data, bond->dev->dev_addr, ETH_ALEN);
|
|
|
|
saddr.sa_family = bond->dev->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = dev_set_mac_address(new_active->dev, &saddr);
|
|
|
|
if (rv) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": %s: Error %d setting MAC of slave %s\n",
|
|
|
|
bond->dev->name, -rv, new_active->dev->name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!old_active)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
memcpy(saddr.sa_data, tmp_mac, ETH_ALEN);
|
|
|
|
saddr.sa_family = old_active->dev->type;
|
|
|
|
|
|
|
|
rv = dev_set_mac_address(old_active->dev, &saddr);
|
|
|
|
if (rv)
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": %s: Error %d setting MAC of slave %s\n",
|
|
|
|
bond->dev->name, -rv, new_active->dev->name);
|
|
|
|
out:
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
break;
|
|
|
|
default:
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": %s: bond_do_fail_over_mac impossible: bad policy %d\n",
|
|
|
|
bond->dev->name, bond->params.fail_over_mac);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**
|
|
|
|
* 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 = bond->params.updelay;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
new_active = old_active = bond->curr_active_slave;
|
|
|
|
|
|
|
|
if (!new_active) { /* there were no active slaves left */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->slave_cnt > 0) /* found one slave */
|
2005-04-16 22:20:36 +00:00
|
|
|
new_active = bond->first_slave;
|
2009-06-12 19:02:48 +00:00
|
|
|
else
|
2005-04-16 22:20:36 +00:00
|
|
|
return NULL; /* still no slave, return NULL */
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
if ((bond->primary_slave) &&
|
|
|
|
(!bond->params.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.
|
|
|
|
*
|
2008-05-18 04:10:14 +00:00
|
|
|
* If new_active is not NULL, caller must hold bond->lock for read and
|
|
|
|
* curr_slave_lock for write_bh.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-11-09 18:35:51 +00:00
|
|
|
void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct slave *old_active = bond->curr_active_slave;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (old_active == new_active)
|
2005-04-16 22:20:36 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (new_active) {
|
2008-05-18 04:10:13 +00:00
|
|
|
new_active->jiffies = jiffies;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (new_active->link == BOND_LINK_BACK) {
|
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: making interface %s the new "
|
|
|
|
"active one %d ms earlier.\n",
|
|
|
|
bond->dev->name, new_active->dev->name,
|
|
|
|
(bond->params.updelay - new_active->delay) * bond->params.miimon);
|
|
|
|
}
|
|
|
|
|
|
|
|
new_active->delay = 0;
|
|
|
|
new_active->link = BOND_LINK_UP;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
|
|
|
|
} else {
|
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: making interface %s the new "
|
|
|
|
"active one.\n",
|
|
|
|
bond->dev->name, new_active->dev->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (USES_PRIMARY(bond->params.mode))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_mc_swap(bond, new_active, old_active);
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_alb_handle_active_change(bond, new_active);
|
2006-02-22 00:36:44 +00:00
|
|
|
if (old_active)
|
|
|
|
bond_set_slave_inactive_flags(old_active);
|
|
|
|
if (new_active)
|
|
|
|
bond_set_slave_active_flags(new_active);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
bond->curr_active_slave = new_active;
|
|
|
|
}
|
2005-06-26 21:52:20 +00:00
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (old_active)
|
2005-06-26 21:52:20 +00:00
|
|
|
bond_set_slave_inactive_flags(old_active);
|
|
|
|
|
|
|
|
if (new_active) {
|
|
|
|
bond_set_slave_active_flags(new_active);
|
2007-10-10 02:43:39 +00:00
|
|
|
|
2008-06-14 01:12:01 +00:00
|
|
|
if (bond->params.fail_over_mac)
|
|
|
|
bond_do_fail_over_mac(bond, new_active,
|
|
|
|
old_active);
|
2008-05-18 04:10:14 +00:00
|
|
|
|
2008-06-14 01:12:01 +00:00
|
|
|
bond->send_grat_arp = bond->params.num_grat_arp;
|
2008-06-14 01:12:03 +00:00
|
|
|
bond_send_gratuitous_arp(bond);
|
2008-06-14 01:12:02 +00:00
|
|
|
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
bond->send_unsol_na = bond->params.num_unsol_na;
|
|
|
|
bond_send_unsolicited_na(bond);
|
|
|
|
|
2008-06-14 01:12:02 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
|
|
|
|
netdev_bonding_change(bond->dev);
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
2008-05-18 04:10:12 +00:00
|
|
|
}
|
2005-06-26 21:52:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* bond_select_active_slave - select a new active slave, if needed
|
|
|
|
* @bond: our bonding struct
|
|
|
|
*
|
2009-06-12 19:02:48 +00:00
|
|
|
* This functions should be called when one of the following occurs:
|
2005-04-16 22:20:36 +00:00
|
|
|
* - 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.
|
|
|
|
*
|
2008-05-18 04:10:14 +00:00
|
|
|
* Caller must hold bond->lock for read and curr_slave_lock for write_bh.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-11-09 18:35:51 +00:00
|
|
|
void bond_select_active_slave(struct bonding *bond)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct slave *best_slave;
|
2006-03-27 21:27:43 +00:00
|
|
|
int rv;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
best_slave = bond_find_best_slave(bond);
|
|
|
|
if (best_slave != bond->curr_active_slave) {
|
|
|
|
bond_change_active_slave(bond, best_slave);
|
2006-03-27 21:27:43 +00:00
|
|
|
rv = bond_set_carrier(bond);
|
|
|
|
if (!rv)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (netif_carrier_ok(bond->dev)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2006-03-27 21:27:43 +00:00
|
|
|
": %s: first active interface up!\n",
|
|
|
|
bond->dev->name);
|
|
|
|
} else {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME ": %s: "
|
2006-03-27 21:27:43 +00:00
|
|
|
"now running without any active interface !\n",
|
|
|
|
bond->dev->name);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------- 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)
|
|
|
|
{
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave->next)
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->next->prev = slave->prev;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave->prev)
|
2005-04-16 22:20:36 +00:00
|
|
|
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 ----------------------------------*/
|
|
|
|
|
2007-07-09 18:51:12 +00:00
|
|
|
static int bond_sethwaddr(struct net_device *bond_dev,
|
|
|
|
struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond_dev=%p\n", bond_dev);
|
|
|
|
pr_debug("slave_dev=%p\n", slave_dev);
|
|
|
|
pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-10 22:47:58 +00:00
|
|
|
#define BOND_VLAN_FEATURES \
|
|
|
|
(NETIF_F_VLAN_CHALLENGED | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX | \
|
|
|
|
NETIF_F_HW_VLAN_FILTER)
|
2005-08-23 05:34:53 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
/*
|
2005-11-05 02:45:45 +00:00
|
|
|
* Compute the common dev->feature set available to all slaves. Some
|
2007-08-10 22:47:58 +00:00
|
|
|
* feature bits are managed elsewhere, so preserve those feature bits
|
|
|
|
* on the master device.
|
2005-08-23 05:34:53 +00:00
|
|
|
*/
|
|
|
|
static int bond_compute_features(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
struct net_device *bond_dev = bond->dev;
|
2007-08-10 22:47:58 +00:00
|
|
|
unsigned long features = bond_dev->features;
|
2007-10-10 02:43:41 +00:00
|
|
|
unsigned short max_hard_header_len = max((u16)ETH_HLEN,
|
|
|
|
bond_dev->hard_header_len);
|
2005-11-05 02:45:45 +00:00
|
|
|
int i;
|
2005-08-23 05:34:53 +00:00
|
|
|
|
2007-08-10 22:47:58 +00:00
|
|
|
features &= ~(NETIF_F_ALL_CSUM | BOND_VLAN_FEATURES);
|
2008-10-23 08:11:29 +00:00
|
|
|
features |= NETIF_F_GSO_MASK | NETIF_F_NO_CSUM;
|
|
|
|
|
|
|
|
if (!bond->first_slave)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
features &= ~NETIF_F_ONE_FOR_ALL;
|
2007-08-10 22:47:58 +00:00
|
|
|
|
2006-09-23 04:53:39 +00:00
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2008-10-23 08:11:29 +00:00
|
|
|
features = netdev_increment_features(features,
|
|
|
|
slave->dev->features,
|
|
|
|
NETIF_F_ONE_FOR_ALL);
|
2006-09-23 04:53:39 +00:00
|
|
|
if (slave->dev->hard_header_len > max_hard_header_len)
|
|
|
|
max_hard_header_len = slave->dev->hard_header_len;
|
|
|
|
}
|
2005-08-23 05:34:53 +00:00
|
|
|
|
2008-10-23 08:11:29 +00:00
|
|
|
done:
|
2007-08-10 22:47:58 +00:00
|
|
|
features |= (bond_dev->features & BOND_VLAN_FEATURES);
|
2008-10-23 08:11:29 +00:00
|
|
|
bond_dev->features = netdev_fix_features(features, NULL);
|
2006-09-23 04:53:39 +00:00
|
|
|
bond_dev->hard_header_len = max_hard_header_len;
|
2005-08-23 05:34:53 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-10 02:43:38 +00:00
|
|
|
static void bond_setup_by_slave(struct net_device *bond_dev,
|
|
|
|
struct net_device *slave_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2007-10-10 02:43:43 +00:00
|
|
|
|
2008-11-21 04:14:53 +00:00
|
|
|
bond_dev->header_ops = slave_dev->header_ops;
|
2007-10-10 02:43:38 +00:00
|
|
|
|
|
|
|
bond_dev->type = slave_dev->type;
|
|
|
|
bond_dev->hard_header_len = slave_dev->hard_header_len;
|
|
|
|
bond_dev->addr_len = slave_dev->addr_len;
|
|
|
|
|
|
|
|
memcpy(bond_dev->broadcast, slave_dev->broadcast,
|
|
|
|
slave_dev->addr_len);
|
2007-10-10 02:43:43 +00:00
|
|
|
bond->setup_by_slave = 1;
|
2007-10-10 02:43:38 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* enslave device <slave> to bond device <master> */
|
2005-11-09 18:35:51 +00:00
|
|
|
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *new_slave = NULL;
|
|
|
|
struct dev_mc_list *dmi;
|
|
|
|
struct sockaddr addr;
|
|
|
|
int link_reporting;
|
|
|
|
int old_features = bond_dev->features;
|
|
|
|
int res = 0;
|
|
|
|
|
2005-09-21 19:18:04 +00:00
|
|
|
if (!bond->params.use_carrier && slave_dev->ethtool_ops == NULL &&
|
2008-11-20 05:56:05 +00:00
|
|
|
slave_ops->ndo_do_ioctl == NULL) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: no link monitoring support for %s\n",
|
|
|
|
bond_dev->name, slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* bond must be initialized by bond_open() before enslaving */
|
|
|
|
if (!(bond_dev->flags & IFF_UP)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2007-10-10 02:43:40 +00:00
|
|
|
" %s: master_dev is not up in bond_enslave\n",
|
|
|
|
bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* already enslaved */
|
|
|
|
if (slave_dev->flags & IFF_SLAVE) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Error, Device was already enslaved\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vlan challenged mutual exclusion */
|
|
|
|
/* no need to lock since we're protected by rtnl_lock */
|
|
|
|
if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!list_empty(&bond->vlan_list)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: cannot enslave VLAN "
|
2005-04-16 22:20:36 +00:00
|
|
|
"challenged slave %s on VLAN enabled "
|
2005-11-09 18:34:57 +00:00
|
|
|
"bond %s\n", bond_dev->name, slave_dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name);
|
|
|
|
return -EPERM;
|
|
|
|
} else {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: enslaved VLAN challenged "
|
2005-04-16 22:20:36 +00:00
|
|
|
"slave %s. Adding VLANs will be blocked as "
|
|
|
|
"long as %s is part of bond %s\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, slave_dev->name, slave_dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name);
|
|
|
|
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
|
|
|
|
}
|
|
|
|
} else {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("%s: ! NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (bond->slave_cnt == 0) {
|
|
|
|
/* First slave, and it is not VLAN challenged,
|
|
|
|
* so remove the block of adding VLANs over the bond.
|
|
|
|
*/
|
|
|
|
bond_dev->features &= ~NETIF_F_VLAN_CHALLENGED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-26 23:11:50 +00:00
|
|
|
/*
|
|
|
|
* Old ifenslave binaries are no longer supported. These can
|
2009-06-12 19:02:48 +00:00
|
|
|
* be identified with moderate accuracy by the state of the slave:
|
2005-09-26 23:11:50 +00:00
|
|
|
* the current ifenslave will set the interface down prior to
|
|
|
|
* enslaving it; the old ifenslave will not.
|
|
|
|
*/
|
|
|
|
if ((slave_dev->flags & IFF_UP)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": %s is up. "
|
2005-09-26 23:11:50 +00:00
|
|
|
"This may be due to an out of date ifenslave.\n",
|
|
|
|
slave_dev->name);
|
|
|
|
res = -EPERM;
|
|
|
|
goto err_undo_flags;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-10 02:43:38 +00:00
|
|
|
/* set bonding device ether type by slave - bonding netdevices are
|
|
|
|
* created with ether_setup, so when the slave type is not ARPHRD_ETHER
|
|
|
|
* there is a need to override some of the type dependent attribs/funcs.
|
|
|
|
*
|
|
|
|
* bond ether type mutual exclusion - don't allow slaves of dissimilar
|
|
|
|
* ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond
|
|
|
|
*/
|
|
|
|
if (bond->slave_cnt == 0) {
|
|
|
|
if (slave_dev->type != ARPHRD_ETHER)
|
|
|
|
bond_setup_by_slave(bond_dev, slave_dev);
|
|
|
|
} else if (bond_dev->type != slave_dev->type) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": %s ether type (%d) is different "
|
2007-10-10 02:43:38 +00:00
|
|
|
"from other slaves (%d), can not enslave it.\n",
|
|
|
|
slave_dev->name,
|
|
|
|
slave_dev->type, bond_dev->type);
|
|
|
|
res = -EINVAL;
|
|
|
|
goto err_undo_flags;
|
|
|
|
}
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (slave_ops->ndo_set_mac_address == NULL) {
|
2007-10-10 02:43:39 +00:00
|
|
|
if (bond->slave_cnt == 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2007-10-10 02:57:24 +00:00
|
|
|
": %s: Warning: The first slave device "
|
|
|
|
"specified does not support setting the MAC "
|
2008-05-18 04:10:14 +00:00
|
|
|
"address. Setting fail_over_mac to active.",
|
2007-10-10 02:57:24 +00:00
|
|
|
bond_dev->name);
|
2008-05-18 04:10:14 +00:00
|
|
|
bond->params.fail_over_mac = BOND_FOM_ACTIVE;
|
|
|
|
} else if (bond->params.fail_over_mac != BOND_FOM_ACTIVE) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2007-10-10 02:57:24 +00:00
|
|
|
": %s: Error: The slave device specified "
|
|
|
|
"does not support setting the MAC address, "
|
2008-05-18 04:10:14 +00:00
|
|
|
"but fail_over_mac is not set to active.\n"
|
2007-10-10 02:43:39 +00:00
|
|
|
, bond_dev->name);
|
|
|
|
res = -EOPNOTSUPP;
|
|
|
|
goto err_undo_flags;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-02-06 22:16:40 +00:00
|
|
|
new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!new_slave) {
|
|
|
|
res = -ENOMEM;
|
|
|
|
goto err_undo_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save slave's original flags before calling
|
|
|
|
* netdev_set_master and dev_open
|
|
|
|
*/
|
|
|
|
new_slave->original_flags = slave_dev->flags;
|
|
|
|
|
2005-09-26 23:11:50 +00:00
|
|
|
/*
|
|
|
|
* Save slave's original ("permanent") mac address for modes
|
|
|
|
* that need it, and for restoring it upon release, and then
|
|
|
|
* set it to the master's address
|
|
|
|
*/
|
|
|
|
memcpy(new_slave->perm_hwaddr, slave_dev->dev_addr, ETH_ALEN);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-10 02:57:24 +00:00
|
|
|
if (!bond->params.fail_over_mac) {
|
2007-10-10 02:43:39 +00:00
|
|
|
/*
|
|
|
|
* Set slave to master's mac address. The application already
|
|
|
|
* set the master's mac address to that of the first slave
|
|
|
|
*/
|
|
|
|
memcpy(addr.sa_data, bond_dev->dev_addr, bond_dev->addr_len);
|
|
|
|
addr.sa_family = slave_dev->type;
|
|
|
|
res = dev_set_mac_address(slave_dev, &addr);
|
|
|
|
if (res) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Error %d calling set_mac_address\n", res);
|
2007-10-10 02:43:39 +00:00
|
|
|
goto err_free;
|
|
|
|
}
|
2005-09-26 23:11:50 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
bonding / ipv6: no addrconf for slaves separately from master
At present, when a device is enslaved to bonding, if ipv6 is
active then addrconf will be initated on the slave (because it is closed
then opened during the enslavement processing). This causes DAD and RS
packets to be sent from the slave. These packets in turn can confuse
switches that perform ipv6 snooping, causing them to incorrectly update
their forwarding tables (if, e.g., the slave being added is an inactve
backup that won't be used right away) and direct traffic away from the
active slave to a backup slave (where the incoming packets will be
dropped).
This patch alters the behavior so that addrconf will only run on
the master device itself. I believe this is logically correct, as it
prevents slaves from having an IPv6 identity independent from the
master. This is consistent with the IPv4 behavior for bonding.
This is accomplished by (a) having bonding set IFF_SLAVE sooner
in the enslavement processing than currently occurs (before open, not
after), and (b) having ipv6 addrconf ignore UP and CHANGE events on
slave devices.
The eql driver also uses the IFF_SLAVE flag. I inspected eql,
and I believe this change is reasonable for its usage of IFF_SLAVE, but
I did not test it.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-07-09 17:42:47 +00:00
|
|
|
res = netdev_set_master(slave_dev, bond_dev);
|
|
|
|
if (res) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Error %d calling netdev_set_master\n", res);
|
2008-05-03 01:06:02 +00:00
|
|
|
goto err_restore_mac;
|
bonding / ipv6: no addrconf for slaves separately from master
At present, when a device is enslaved to bonding, if ipv6 is
active then addrconf will be initated on the slave (because it is closed
then opened during the enslavement processing). This causes DAD and RS
packets to be sent from the slave. These packets in turn can confuse
switches that perform ipv6 snooping, causing them to incorrectly update
their forwarding tables (if, e.g., the slave being added is an inactve
backup that won't be used right away) and direct traffic away from the
active slave to a backup slave (where the incoming packets will be
dropped).
This patch alters the behavior so that addrconf will only run on
the master device itself. I believe this is logically correct, as it
prevents slaves from having an IPv6 identity independent from the
master. This is consistent with the IPv4 behavior for bonding.
This is accomplished by (a) having bonding set IFF_SLAVE sooner
in the enslavement processing than currently occurs (before open, not
after), and (b) having ipv6 addrconf ignore UP and CHANGE events on
slave devices.
The eql driver also uses the IFF_SLAVE flag. I inspected eql,
and I believe this change is reasonable for its usage of IFF_SLAVE, but
I did not test it.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-07-09 17:42:47 +00:00
|
|
|
}
|
2005-09-26 23:11:50 +00:00
|
|
|
/* open the slave since the application closed it */
|
|
|
|
res = dev_open(slave_dev);
|
|
|
|
if (res) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_debug("Opening slave %s failed\n", slave_dev->name);
|
2008-05-03 01:06:02 +00:00
|
|
|
goto err_unset_master;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
new_slave->dev = slave_dev;
|
2006-09-23 04:54:10 +00:00
|
|
|
slave_dev->priv_flags |= IFF_BONDING;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* bond_alb_init_slave() must be called before all other stages since
|
|
|
|
* it might fail and we do not want to have to undo everything
|
|
|
|
*/
|
|
|
|
res = bond_alb_init_slave(bond, new_slave);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res)
|
2008-05-03 01:06:02 +00:00
|
|
|
goto err_close;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the mode USES_PRIMARY, then the new slave gets the
|
|
|
|
* master's promisc (and mc) settings only if it becomes the
|
|
|
|
* curr_active_slave, and that is taken care of later when calling
|
|
|
|
* bond_change_active()
|
|
|
|
*/
|
|
|
|
if (!USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* set promiscuity level to new slave */
|
|
|
|
if (bond_dev->flags & IFF_PROMISC) {
|
2008-07-15 03:51:36 +00:00
|
|
|
res = dev_set_promiscuity(slave_dev, 1);
|
|
|
|
if (res)
|
|
|
|
goto err_close;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set allmulti level to new slave */
|
|
|
|
if (bond_dev->flags & IFF_ALLMULTI) {
|
2008-07-15 03:51:36 +00:00
|
|
|
res = dev_set_allmulti(slave_dev, 1);
|
|
|
|
if (res)
|
|
|
|
goto err_close;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_lock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* upload master's mc_list to new slave */
|
2009-06-12 19:02:48 +00:00
|
|
|
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next)
|
|
|
|
dev_mc_add(slave_dev, dmi->dmi_addr,
|
|
|
|
dmi->dmi_addrlen, 0);
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_unlock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
/* add lacpdu mc addr to mc list */
|
|
|
|
u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
|
|
|
|
|
|
|
|
dev_mc_add(slave_dev, lacpdu_multicast, ETH_ALEN, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bond_add_vlans_on_slave(bond, slave_dev);
|
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
|
|
|
bond_attach_slave(bond, new_slave);
|
|
|
|
|
|
|
|
new_slave->delay = 0;
|
|
|
|
new_slave->link_failure_count = 0;
|
|
|
|
|
2005-08-23 05:34:53 +00:00
|
|
|
bond_compute_features(bond);
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
new_slave->last_arp_rx = jiffies;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (bond->params.miimon && !bond->params.use_carrier) {
|
|
|
|
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
|
|
|
|
|
|
|
|
if ((link_reporting == -1) && !bond->params.arp_interval) {
|
|
|
|
/*
|
|
|
|
* miimon is set but a bonded network driver
|
|
|
|
* does not support ETHTOOL/MII and
|
|
|
|
* arp_interval is not set. Note: if
|
|
|
|
* use_carrier is enabled, we will never go
|
|
|
|
* here (because netif_carrier is always
|
|
|
|
* supported); thus, we don't need to change
|
|
|
|
* the messages for netif_carrier.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: MII and ETHTOOL support not "
|
2005-04-16 22:20:36 +00:00
|
|
|
"available for interface %s, and "
|
|
|
|
"arp_interval/arp_ip_target module parameters "
|
|
|
|
"not specified, thus bonding will not detect "
|
|
|
|
"link failures! see bonding.txt for details.\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else if (link_reporting == -1) {
|
|
|
|
/* unable get link status using mii/ethtool */
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: can't get link status from "
|
2005-04-16 22:20:36 +00:00
|
|
|
"interface %s; the network driver associated "
|
|
|
|
"with this interface does not support MII or "
|
|
|
|
"ETHTOOL link status reporting, thus miimon "
|
|
|
|
"has no effect on this interface.\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for initial state */
|
|
|
|
if (!bond->params.miimon ||
|
|
|
|
(bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS)) {
|
|
|
|
if (bond->params.updelay) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Initial state of slave_dev is "
|
2005-04-16 22:20:36 +00:00
|
|
|
"BOND_LINK_BACK\n");
|
|
|
|
new_slave->link = BOND_LINK_BACK;
|
|
|
|
new_slave->delay = bond->params.updelay;
|
|
|
|
} else {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Initial state of slave_dev is "
|
2005-04-16 22:20:36 +00:00
|
|
|
"BOND_LINK_UP\n");
|
|
|
|
new_slave->link = BOND_LINK_UP;
|
|
|
|
}
|
|
|
|
new_slave->jiffies = jiffies;
|
|
|
|
} else {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Initial state of slave_dev is "
|
2005-04-16 22:20:36 +00:00
|
|
|
"BOND_LINK_DOWN\n");
|
|
|
|
new_slave->link = BOND_LINK_DOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bond_update_speed_duplex(new_slave) &&
|
|
|
|
(new_slave->link != BOND_LINK_DOWN)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: failed to get speed and duplex from %s, "
|
2005-04-16 22:20:36 +00:00
|
|
|
"assumed to be 100Mb/sec and Full.\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, new_slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: Operation of 802.3ad mode requires ETHTOOL "
|
2005-04-16 22:20:36 +00:00
|
|
|
"support in base driver for proper aggregator "
|
2005-11-09 18:34:57 +00:00
|
|
|
"selection.\n", bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
|
|
|
|
/* if there is a primary slave, remember it */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (strcmp(bond->params.primary, new_slave->dev->name) == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->primary_slave = new_slave;
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (bond->params.mode) {
|
|
|
|
case BOND_MODE_ACTIVEBACKUP:
|
2006-09-23 04:56:15 +00:00
|
|
|
bond_set_slave_inactive_flags(new_slave);
|
|
|
|
bond_select_active_slave(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BOND_MODE_8023AD:
|
|
|
|
/* in 802.3ad mode, the internal mechanism
|
|
|
|
* will activate the slaves in the selected
|
|
|
|
* aggregator
|
|
|
|
*/
|
|
|
|
bond_set_slave_inactive_flags(new_slave);
|
|
|
|
/* if this is the first slave */
|
|
|
|
if (bond->slave_cnt == 1) {
|
|
|
|
SLAVE_AD_INFO(new_slave).id = 1;
|
|
|
|
/* Initialize AD with the number of times that the AD timer is called in 1 second
|
|
|
|
* can be called only after the mac address of the bond is set
|
|
|
|
*/
|
|
|
|
bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL,
|
|
|
|
bond->params.lacp_fast);
|
|
|
|
} else {
|
|
|
|
SLAVE_AD_INFO(new_slave).id =
|
|
|
|
SLAVE_AD_INFO(new_slave->prev).id + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bond_3ad_bind_slave(new_slave);
|
|
|
|
break;
|
|
|
|
case BOND_MODE_TLB:
|
|
|
|
case BOND_MODE_ALB:
|
|
|
|
new_slave->state = BOND_STATE_ACTIVE;
|
2007-10-18 00:37:49 +00:00
|
|
|
bond_set_slave_inactive_flags(new_slave);
|
bonding: select current active slave when enslaving device for mode tlb and alb
I've hit an issue on my system when I've been using RealTek RTL8139D cards in
bonding interface in mode balancing-alb. When I enslave a card, the current
active slave (bond->curr_active_slave) is not set and the link is therefore
not functional.
----
# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.5.0 (November 4, 2008)
Bonding Mode: adaptive load balancing
Primary Slave: None
Currently Active Slave: None
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0
Slave Interface: eth1
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1f:1f:01:2f:22
----
The thing that gets it right is when I unplug the cable and then I put it back
into the NIC. Then the current active slave is set to eth1 and link is working
just fine. Here is dmesg log with bonding DEBUG messages turned on:
----
ADDRCONF(NETDEV_UP): bond0: link is not ready
event_dev: bond0, event: 1
IFF_MASTER
event_dev: bond0, event: 8
IFF_MASTER
bond_ioctl: master=bond0, cmd=35216
slave_dev=cac5d800:
slave_dev->name=eth1:
eth1: ! NETIF_F_VLAN_CHALLENGED
event_dev: eth1, event: 8
eth1: link up, 100Mbps, full-duplex, lpa 0xC5E1
event_dev: eth1, event: 1
event_dev: eth1, event: 8
IFF_SLAVE
Initial state of slave_dev is BOND_LINK_UP
bonding: bond0: enslaving eth1 as an active interface with an up link.
ADDRCONF(NETDEV_CHANGE): bond0: link becomes ready
event_dev: bond0, event: 4
IFF_MASTER
bond0: no IPv6 routers present
<<<<cable unplug>>>>
eth1: link down
event_dev: eth1, event: 4
IFF_SLAVE
bonding: bond0: link status definitely down for interface eth1, disabling it
event_dev: bond0, event: 4
IFF_MASTER
<<<<cable plug>>>>
eth1: link up, 100Mbps, full-duplex, lpa 0xC5E1
event_dev: eth1, event: 4
IFF_SLAVE
bonding: bond0: link status definitely up for interface eth1.
bonding: bond0: making interface eth1 the new active one.
event_dev: eth1, event: 8
IFF_SLAVE
event_dev: eth1, event: 8
IFF_SLAVE
bonding: bond0: first active interface up!
event_dev: bond0, event: 4
IFF_MASTER
----
The current active slave is set by calling bond_select_active_slave() function
from bond_miimon_commit() function when the slave (eth1) link goes to state up.
I also tested this on other machine with Broadcom NetXtreme II BCM5708
1000Base-T NIC and there all works fine. The thing is that this adapter is down
and goes up after few seconds after it is enslaved.
This patch calls bond_select_active_slave() in bond_enslave() function for modes
alb and tlb and makes sure that the current active slave is set up properly even
when the slave state is already up. Tested on both systems, works fine.
Notice: The same problem can maybe also occrur in mode 8023AD but I'm unable to
test that.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-26 00:23:38 +00:00
|
|
|
bond_select_active_slave(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("This slave is always active in trunk mode\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* always active in trunk mode */
|
|
|
|
new_slave->state = BOND_STATE_ACTIVE;
|
|
|
|
|
|
|
|
/* In trunking mode there is little meaning to curr_active_slave
|
|
|
|
* anyway (it holds no special properties of the bond device),
|
|
|
|
* so we can change it without calling change_active_interface()
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!bond->curr_active_slave)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->curr_active_slave = new_slave;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
} /* switch(bond_mode) */
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
|
2006-03-27 21:27:43 +00:00
|
|
|
bond_set_carrier(bond);
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 18:36:41 +00:00
|
|
|
res = bond_create_slave_symlinks(bond_dev, slave_dev);
|
|
|
|
if (res)
|
2008-05-03 01:06:02 +00:00
|
|
|
goto err_close;
|
2005-11-09 18:36:41 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: enslaving %s as a%s interface with a%s link.\n",
|
|
|
|
bond_dev->name, slave_dev->name,
|
|
|
|
new_slave->state == BOND_STATE_ACTIVE ? "n active" : " backup",
|
|
|
|
new_slave->link != BOND_LINK_DOWN ? "n up" : " down");
|
|
|
|
|
|
|
|
/* enslave is successful */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Undo stages on error */
|
|
|
|
err_close:
|
|
|
|
dev_close(slave_dev);
|
|
|
|
|
2008-05-03 01:06:02 +00:00
|
|
|
err_unset_master:
|
|
|
|
netdev_set_master(slave_dev, NULL);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
err_restore_mac:
|
2007-10-10 02:57:24 +00:00
|
|
|
if (!bond->params.fail_over_mac) {
|
2008-05-18 04:10:14 +00:00
|
|
|
/* XXX TODO - fom follow mode needs to change master's
|
|
|
|
* MAC if this slave's MAC is in use by the bond, or at
|
|
|
|
* least print a warning.
|
|
|
|
*/
|
2007-10-10 02:43:39 +00:00
|
|
|
memcpy(addr.sa_data, new_slave->perm_hwaddr, ETH_ALEN);
|
|
|
|
addr.sa_family = slave_dev->type;
|
|
|
|
dev_set_mac_address(slave_dev, &addr);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
err_free:
|
|
|
|
kfree(new_slave);
|
|
|
|
|
|
|
|
err_undo_flags:
|
|
|
|
bond_dev->features = old_features;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-11-09 18:35:51 +00:00
|
|
|
int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *oldcurrent;
|
|
|
|
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)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: cannot release %s.\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name, slave_dev->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
|
|
|
slave = bond_get_slave_by_dev(bond, slave_dev);
|
|
|
|
if (!slave) {
|
|
|
|
/* not a slave of this bond */
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: %s not enslaved\n",
|
|
|
|
bond_dev->name, slave_dev->name);
|
2006-02-08 05:17:22 +00:00
|
|
|
write_unlock_bh(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
if (!bond->params.fail_over_mac) {
|
|
|
|
mac_addr_differ = memcmp(bond_dev->dev_addr, slave->perm_hwaddr,
|
|
|
|
ETH_ALEN);
|
|
|
|
if (!mac_addr_differ && (bond->slave_cnt > 1))
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": %s: Warning: the permanent HWaddr of %s - "
|
2008-10-27 22:59:26 +00:00
|
|
|
"%pM - is still in use by %s. "
|
2008-05-18 04:10:14 +00:00
|
|
|
"Set the HWaddr of %s to a different address "
|
|
|
|
"to avoid conflicts.\n",
|
|
|
|
bond_dev->name, slave_dev->name,
|
2008-10-27 22:59:26 +00:00
|
|
|
slave->perm_hwaddr,
|
2008-05-18 04:10:14 +00:00
|
|
|
bond_dev->name, slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inform AD package of unbinding of slave. */
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
/* must be called before the slave is
|
|
|
|
* detached from the list
|
|
|
|
*/
|
|
|
|
bond_3ad_unbind_slave(slave);
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: releasing %s interface %s\n",
|
|
|
|
bond_dev->name,
|
|
|
|
(slave->state == BOND_STATE_ACTIVE)
|
|
|
|
? "active" : "backup",
|
|
|
|
slave_dev->name);
|
|
|
|
|
|
|
|
oldcurrent = bond->curr_active_slave;
|
|
|
|
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
|
|
|
|
/* release the slave from its bond */
|
|
|
|
bond_detach_slave(bond, slave);
|
|
|
|
|
2005-08-23 05:34:53 +00:00
|
|
|
bond_compute_features(bond);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->primary_slave == slave)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->primary_slave = NULL;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (oldcurrent == slave)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_change_active_slave(bond, NULL);
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Must be called only after the slave has been
|
|
|
|
* detached from the list and the curr_active_slave
|
|
|
|
* has been cleared (if our_slave == old_current),
|
|
|
|
* but before a new active slave is selected.
|
|
|
|
*/
|
2008-01-18 00:24:59 +00:00
|
|
|
write_unlock_bh(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_alb_deinit_slave(bond, slave);
|
2008-01-18 00:24:59 +00:00
|
|
|
write_lock_bh(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
if (oldcurrent == slave) {
|
|
|
|
/*
|
|
|
|
* Note that we hold RTNL over this sequence, so there
|
|
|
|
* is no concern that another slave add/remove event
|
|
|
|
* will interfere.
|
|
|
|
*/
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_select_active_slave(bond);
|
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (bond->slave_cnt == 0) {
|
2006-03-27 21:27:43 +00:00
|
|
|
bond_set_carrier(bond);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* if the last slave was removed, zero the mac address
|
|
|
|
* of the master so it will be set by the application
|
|
|
|
* to the mac address of the first slave
|
|
|
|
*/
|
|
|
|
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
|
|
|
|
|
|
|
|
if (list_empty(&bond->vlan_list)) {
|
|
|
|
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
|
|
|
|
} else {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: clearing HW address of %s while it "
|
2005-04-16 22:20:36 +00:00
|
|
|
"still has VLANs.\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, bond_dev->name);
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: When re-adding slaves, make sure the bond's "
|
|
|
|
"HW address matches its VLANs'.\n",
|
|
|
|
bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
} else if ((bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
|
|
|
|
!bond_has_challenged_slaves(bond)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: last VLAN challenged slave %s "
|
2005-04-16 22:20:36 +00:00
|
|
|
"left bond %s. VLAN blocking is removed\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, slave_dev->name, bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->features &= ~NETIF_F_VLAN_CHALLENGED;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
2005-11-09 18:36:41 +00:00
|
|
|
/* must do this from outside any spinlocks */
|
|
|
|
bond_destroy_slave_symlinks(bond_dev, slave_dev);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_del_vlans_from_slave(bond, slave_dev);
|
|
|
|
|
|
|
|
/* If the mode USES_PRIMARY, then we should only remove its
|
|
|
|
* promisc and mc settings if it was the curr_active_slave, but that was
|
|
|
|
* already taken care of above when we detached the slave
|
|
|
|
*/
|
|
|
|
if (!USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* unset promiscuity level from slave */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond_dev->flags & IFF_PROMISC)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_promiscuity(slave_dev, -1);
|
|
|
|
|
|
|
|
/* unset allmulti level from slave */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond_dev->flags & IFF_ALLMULTI)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_allmulti(slave_dev, -1);
|
|
|
|
|
|
|
|
/* flush master's mc_list from slave */
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_lock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_mc_list_flush(bond_dev, slave_dev);
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_unlock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
netdev_set_master(slave_dev, NULL);
|
|
|
|
|
|
|
|
/* close slave before restoring its mac address */
|
|
|
|
dev_close(slave_dev);
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
if (bond->params.fail_over_mac != BOND_FOM_ACTIVE) {
|
2007-10-10 02:43:39 +00:00
|
|
|
/* restore original ("permanent") mac address */
|
|
|
|
memcpy(addr.sa_data, slave->perm_hwaddr, ETH_ALEN);
|
|
|
|
addr.sa_family = slave_dev->type;
|
|
|
|
dev_set_mac_address(slave_dev, &addr);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-22 00:36:44 +00:00
|
|
|
slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
|
2006-09-23 04:54:53 +00:00
|
|
|
IFF_SLAVE_INACTIVE | IFF_BONDING |
|
|
|
|
IFF_SLAVE_NEEDARP);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
kfree(slave);
|
|
|
|
|
|
|
|
return 0; /* deletion OK */
|
|
|
|
}
|
|
|
|
|
2007-10-10 02:43:43 +00:00
|
|
|
/*
|
|
|
|
* Destroy a bonding device.
|
|
|
|
* Must be under rtnl_lock when this function is called.
|
|
|
|
*/
|
2009-06-12 19:02:47 +00:00
|
|
|
static void bond_uninit(struct net_device *bond_dev)
|
2008-10-31 00:41:15 +00:00
|
|
|
{
|
2008-12-04 05:14:04 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2008-10-31 00:41:15 +00:00
|
|
|
|
2009-06-12 19:02:47 +00:00
|
|
|
bond_deinit(bond_dev);
|
|
|
|
bond_destroy_sysfs_entry(bond);
|
|
|
|
|
2008-10-31 00:41:15 +00:00
|
|
|
if (bond->wq)
|
|
|
|
destroy_workqueue(bond->wq);
|
|
|
|
|
|
|
|
netif_addr_lock_bh(bond_dev);
|
|
|
|
bond_mc_list_destroy(bond);
|
|
|
|
netif_addr_unlock_bh(bond_dev);
|
|
|
|
}
|
|
|
|
|
2007-10-10 02:43:43 +00:00
|
|
|
/*
|
2009-06-12 19:02:48 +00:00
|
|
|
* First release a slave and than destroy the bond if no more slaves are left.
|
2007-10-10 02:43:43 +00:00
|
|
|
* Must be under rtnl_lock when this function is called.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
int bond_release_and_destroy(struct net_device *bond_dev,
|
|
|
|
struct net_device *slave_dev)
|
2007-10-10 02:43:43 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2007-10-10 02:43:43 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = bond_release(bond_dev, slave_dev);
|
|
|
|
if ((ret == 0) && (bond->slave_cnt == 0)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME ": %s: destroying bond %s.\n",
|
2007-10-10 02:43:43 +00:00
|
|
|
bond_dev->name, bond_dev->name);
|
2009-06-12 19:02:47 +00:00
|
|
|
unregister_netdevice(bond_dev);
|
2007-10-10 02:43:43 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* This function releases all slaves.
|
|
|
|
*/
|
|
|
|
static int bond_release_all(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
|
|
|
struct net_device *slave_dev;
|
|
|
|
struct sockaddr addr;
|
|
|
|
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
2006-03-27 21:27:43 +00:00
|
|
|
netif_carrier_off(bond_dev);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->slave_cnt == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
bond->primary_slave = NULL;
|
|
|
|
bond_change_active_slave(bond, NULL);
|
|
|
|
|
|
|
|
while ((slave = bond->first_slave) != NULL) {
|
|
|
|
/* Inform AD package of unbinding of slave
|
|
|
|
* before slave is detached from the list.
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_3ad_unbind_slave(slave);
|
|
|
|
|
|
|
|
slave_dev = slave->dev;
|
|
|
|
bond_detach_slave(bond, slave);
|
|
|
|
|
2008-01-18 00:24:59 +00:00
|
|
|
/* now that the slave is detached, unlock and perform
|
|
|
|
* all the undo steps that should not be called from
|
|
|
|
* within a lock.
|
|
|
|
*/
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* must be called only after the slave
|
|
|
|
* has been detached from the list
|
|
|
|
*/
|
|
|
|
bond_alb_deinit_slave(bond, slave);
|
|
|
|
}
|
|
|
|
|
2005-08-23 05:34:53 +00:00
|
|
|
bond_compute_features(bond);
|
|
|
|
|
2005-11-09 18:36:41 +00:00
|
|
|
bond_destroy_slave_symlinks(bond_dev, slave_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_del_vlans_from_slave(bond, slave_dev);
|
|
|
|
|
|
|
|
/* If the mode USES_PRIMARY, then we should only remove its
|
|
|
|
* promisc and mc settings if it was the curr_active_slave, but that was
|
|
|
|
* already taken care of above when we detached the slave
|
|
|
|
*/
|
|
|
|
if (!USES_PRIMARY(bond->params.mode)) {
|
|
|
|
/* unset promiscuity level from slave */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond_dev->flags & IFF_PROMISC)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_promiscuity(slave_dev, -1);
|
|
|
|
|
|
|
|
/* unset allmulti level from slave */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond_dev->flags & IFF_ALLMULTI)
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_set_allmulti(slave_dev, -1);
|
|
|
|
|
|
|
|
/* flush master's mc_list from slave */
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_lock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_mc_list_flush(bond_dev, slave_dev);
|
2008-07-15 07:15:08 +00:00
|
|
|
netif_addr_unlock_bh(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
netdev_set_master(slave_dev, NULL);
|
|
|
|
|
|
|
|
/* close slave before restoring its mac address */
|
|
|
|
dev_close(slave_dev);
|
|
|
|
|
2007-10-10 02:57:24 +00:00
|
|
|
if (!bond->params.fail_over_mac) {
|
2007-10-10 02:43:39 +00:00
|
|
|
/* restore original ("permanent") mac address*/
|
|
|
|
memcpy(addr.sa_data, slave->perm_hwaddr, ETH_ALEN);
|
|
|
|
addr.sa_family = slave_dev->type;
|
|
|
|
dev_set_mac_address(slave_dev, &addr);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-22 00:36:44 +00:00
|
|
|
slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
|
|
|
|
IFF_SLAVE_INACTIVE);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
kfree(slave);
|
|
|
|
|
|
|
|
/* re-acquire the lock before getting the next slave */
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* zero the mac address of the master so it will be
|
|
|
|
* set by the application to the mac address of the
|
|
|
|
* first slave
|
|
|
|
*/
|
|
|
|
memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (list_empty(&bond->vlan_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
|
2009-06-12 19:02:48 +00:00
|
|
|
else {
|
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Warning: clearing HW address of %s while it "
|
2005-04-16 22:20:36 +00:00
|
|
|
"still has VLANs.\n",
|
2005-11-09 18:34:57 +00:00
|
|
|
bond_dev->name, bond_dev->name);
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: When re-adding slaves, make sure the bond's "
|
|
|
|
"HW address matches its VLANs'.\n",
|
|
|
|
bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: released all slaves\n",
|
|
|
|
bond_dev->name);
|
|
|
|
|
|
|
|
out:
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
2009-06-12 19:02:48 +00:00
|
|
|
* In these cases, this function does nothing.
|
|
|
|
* In the other cases, current_slave pointer is changed and 0 is returned.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *old_active = NULL;
|
|
|
|
struct slave *new_active = NULL;
|
|
|
|
int res = 0;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!USES_PRIMARY(bond->params.mode))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Verify that master_dev is indeed the master of slave_dev */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!(slave_dev->flags & IFF_SLAVE) || (slave_dev->master != bond_dev))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
read_lock(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
old_active = bond->curr_active_slave;
|
2007-10-18 00:37:49 +00:00
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
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)) {
|
2007-10-18 00:37:49 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((new_active) &&
|
|
|
|
(old_active) &&
|
|
|
|
(new_active->link == BOND_LINK_UP) &&
|
|
|
|
IS_UP(new_active->dev)) {
|
2007-10-18 00:37:49 +00:00
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_change_active_slave(bond, new_active);
|
2007-10-18 00:37:49 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
2009-06-12 19:02:48 +00:00
|
|
|
} else
|
2005-04-16 22:20:36 +00:00
|
|
|
res = -EINVAL;
|
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
info->bond_mode = bond->params.mode;
|
|
|
|
info->miimon = bond->params.miimon;
|
|
|
|
|
2007-10-18 00:37:50 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
info->num_slaves = bond->slave_cnt;
|
2007-10-18 00:37:50 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
2009-04-23 03:39:04 +00:00
|
|
|
int i, res = -ENODEV;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-18 00:37:50 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
if (i == (int)info->slave_id) {
|
2009-04-23 03:39:04 +00:00
|
|
|
res = 0;
|
|
|
|
strcpy(info->slave_name, slave->dev->name);
|
|
|
|
info->link = slave->link;
|
|
|
|
info->state = slave->state;
|
|
|
|
info->link_failure_count = slave->link_failure_count;
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-18 00:37:50 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-23 03:39:04 +00:00
|
|
|
return res;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------- Monitoring -------------------------------*/
|
|
|
|
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
static int bond_miimon_inspect(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i, link_state, commit = 0;
|
2009-04-24 03:57:29 +00:00
|
|
|
bool ignore_updelay;
|
|
|
|
|
|
|
|
ignore_updelay = !bond->curr_active_slave ? true : false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2008-07-03 01:21:58 +00:00
|
|
|
slave->new_link = BOND_LINK_NOCHANGE;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
link_state = bond_check_dev_link(bond, slave->dev, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
switch (slave->link) {
|
2008-07-03 01:21:58 +00:00
|
|
|
case BOND_LINK_UP:
|
|
|
|
if (link_state)
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
slave->link = BOND_LINK_FAIL;
|
|
|
|
slave->delay = bond->params.downdelay;
|
|
|
|
if (slave->delay) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-07-03 01:21:58 +00:00
|
|
|
": %s: link status down for %s"
|
|
|
|
"interface %s, disabling it in %d ms.\n",
|
|
|
|
bond->dev->name,
|
|
|
|
(bond->params.mode ==
|
|
|
|
BOND_MODE_ACTIVEBACKUP) ?
|
|
|
|
((slave->state == BOND_STATE_ACTIVE) ?
|
|
|
|
"active " : "backup ") : "",
|
|
|
|
slave->dev->name,
|
|
|
|
bond->params.downdelay * bond->params.miimon);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-07-03 01:21:58 +00:00
|
|
|
/*FALLTHRU*/
|
|
|
|
case BOND_LINK_FAIL:
|
|
|
|
if (link_state) {
|
|
|
|
/*
|
|
|
|
* recovered before downdelay expired
|
|
|
|
*/
|
|
|
|
slave->link = BOND_LINK_UP;
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->jiffies = jiffies;
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: link status up again after %d "
|
|
|
|
"ms for interface %s.\n",
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->dev->name,
|
2008-07-03 01:21:58 +00:00
|
|
|
(bond->params.downdelay - slave->delay) *
|
|
|
|
bond->params.miimon,
|
|
|
|
slave->dev->name);
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-07-03 01:21:58 +00:00
|
|
|
|
|
|
|
if (slave->delay <= 0) {
|
|
|
|
slave->new_link = BOND_LINK_DOWN;
|
|
|
|
commit++;
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
slave->delay--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BOND_LINK_DOWN:
|
|
|
|
if (!link_state)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
slave->link = BOND_LINK_BACK;
|
|
|
|
slave->delay = bond->params.updelay;
|
|
|
|
|
|
|
|
if (slave->delay) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-07-03 01:21:58 +00:00
|
|
|
": %s: link status up for "
|
|
|
|
"interface %s, enabling it in %d ms.\n",
|
|
|
|
bond->dev->name, slave->dev->name,
|
2009-04-24 03:57:29 +00:00
|
|
|
ignore_updelay ? 0 :
|
2008-07-03 01:21:58 +00:00
|
|
|
bond->params.updelay *
|
|
|
|
bond->params.miimon);
|
|
|
|
}
|
|
|
|
/*FALLTHRU*/
|
|
|
|
case BOND_LINK_BACK:
|
|
|
|
if (!link_state) {
|
|
|
|
slave->link = BOND_LINK_DOWN;
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: link status down again after %d "
|
|
|
|
"ms for interface %s.\n",
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->dev->name,
|
2008-07-03 01:21:58 +00:00
|
|
|
(bond->params.updelay - slave->delay) *
|
|
|
|
bond->params.miimon,
|
|
|
|
slave->dev->name);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-04-24 03:57:29 +00:00
|
|
|
if (ignore_updelay)
|
|
|
|
slave->delay = 0;
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
if (slave->delay <= 0) {
|
|
|
|
slave->new_link = BOND_LINK_UP;
|
|
|
|
commit++;
|
2009-04-24 03:57:29 +00:00
|
|
|
ignore_updelay = false;
|
2008-07-03 01:21:58 +00:00
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-07-03 01:21:58 +00:00
|
|
|
|
|
|
|
slave->delay--;
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
2008-07-03 01:21:58 +00:00
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
return commit;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
static void bond_miimon_commit(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
switch (slave->new_link) {
|
|
|
|
case BOND_LINK_NOCHANGE:
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
case BOND_LINK_UP:
|
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
|
slave->jiffies = jiffies;
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
/* prevent it from being the active one */
|
|
|
|
slave->state = BOND_STATE_BACKUP;
|
|
|
|
} else if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
|
|
|
|
/* make it immediately active */
|
|
|
|
slave->state = BOND_STATE_ACTIVE;
|
|
|
|
} else if (slave != bond->primary_slave) {
|
|
|
|
/* prevent it from being the active one */
|
|
|
|
slave->state = BOND_STATE_BACKUP;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-07-03 01:21:58 +00:00
|
|
|
": %s: link status definitely "
|
|
|
|
"up for interface %s.\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
/* notify ad that the link status has changed */
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
|
|
|
bond_3ad_handle_link_change(slave, BOND_LINK_UP);
|
2007-10-18 00:37:49 +00:00
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond))
|
2008-07-03 01:21:58 +00:00
|
|
|
bond_alb_handle_link_change(bond, slave,
|
|
|
|
BOND_LINK_UP);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
if (!bond->curr_active_slave ||
|
|
|
|
(slave == bond->primary_slave))
|
|
|
|
goto do_failover;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
continue;
|
2007-10-18 00:37:49 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
case BOND_LINK_DOWN:
|
2008-10-31 00:41:14 +00:00
|
|
|
if (slave->link_failure_count < UINT_MAX)
|
|
|
|
slave->link_failure_count++;
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
slave->link = BOND_LINK_DOWN;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP ||
|
|
|
|
bond->params.mode == BOND_MODE_8023AD)
|
|
|
|
bond_set_slave_inactive_flags(slave);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-07-03 01:21:58 +00:00
|
|
|
": %s: link status definitely down for "
|
|
|
|
"interface %s, disabling it\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD)
|
|
|
|
bond_3ad_handle_link_change(slave,
|
|
|
|
BOND_LINK_DOWN);
|
|
|
|
|
2009-05-27 05:42:36 +00:00
|
|
|
if (bond_is_lb(bond))
|
2008-07-03 01:21:58 +00:00
|
|
|
bond_alb_handle_link_change(bond, slave,
|
|
|
|
BOND_LINK_DOWN);
|
|
|
|
|
|
|
|
if (slave == bond->curr_active_slave)
|
|
|
|
goto do_failover;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
default:
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-07-03 01:21:58 +00:00
|
|
|
": %s: invalid new link %d on slave %s\n",
|
|
|
|
bond->dev->name, slave->new_link,
|
|
|
|
slave->dev->name);
|
|
|
|
slave->new_link = BOND_LINK_NOCHANGE;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
do_failover:
|
|
|
|
ASSERT_RTNL();
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
bond_select_active_slave(bond);
|
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
bond_set_carrier(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-10-18 00:37:48 +00:00
|
|
|
/*
|
|
|
|
* bond_mii_monitor
|
|
|
|
*
|
|
|
|
* Really a wrapper that splits the mii monitor into two phases: an
|
2008-07-03 01:21:58 +00:00
|
|
|
* inspection, then (if inspection indicates something needs to be done)
|
|
|
|
* an acquisition of appropriate locks followed by a commit phase to
|
|
|
|
* implement whatever link state changes are indicated.
|
2007-10-18 00:37:48 +00:00
|
|
|
*/
|
|
|
|
void bond_mii_monitor(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct bonding *bond = container_of(work, struct bonding,
|
|
|
|
mii_work.work);
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
2008-07-03 01:21:58 +00:00
|
|
|
if (bond->kill_timers)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (bond->slave_cnt == 0)
|
|
|
|
goto re_arm;
|
2008-06-14 01:12:03 +00:00
|
|
|
|
|
|
|
if (bond->send_grat_arp) {
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
bond_send_gratuitous_arp(bond);
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
}
|
|
|
|
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
if (bond->send_unsol_na) {
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
bond_send_unsolicited_na(bond);
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
}
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
if (bond_miimon_inspect(bond)) {
|
2007-10-18 00:37:48 +00:00
|
|
|
read_unlock(&bond->lock);
|
|
|
|
rtnl_lock();
|
|
|
|
read_lock(&bond->lock);
|
2008-07-03 01:21:58 +00:00
|
|
|
|
|
|
|
bond_miimon_commit(bond);
|
|
|
|
|
2008-01-18 00:25:03 +00:00
|
|
|
read_unlock(&bond->lock);
|
|
|
|
rtnl_unlock(); /* might sleep, hold no other locks */
|
|
|
|
read_lock(&bond->lock);
|
2007-10-18 00:37:48 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 01:21:58 +00:00
|
|
|
re_arm:
|
|
|
|
if (bond->params.miimon)
|
|
|
|
queue_delayed_work(bond->wq, &bond->mii_work,
|
|
|
|
msecs_to_jiffies(bond->params.miimon));
|
|
|
|
out:
|
2007-10-18 00:37:48 +00:00
|
|
|
read_unlock(&bond->lock);
|
|
|
|
}
|
2005-06-26 21:52:20 +00:00
|
|
|
|
2007-08-23 00:06:58 +00:00
|
|
|
static __be32 bond_glean_dev_ip(struct net_device *dev)
|
2005-06-26 21:52:20 +00:00
|
|
|
{
|
|
|
|
struct in_device *idev;
|
|
|
|
struct in_ifaddr *ifa;
|
2006-09-29 01:00:55 +00:00
|
|
|
__be32 addr = 0;
|
2005-06-26 21:52:20 +00:00
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2005-10-03 21:35:55 +00:00
|
|
|
idev = __in_dev_get_rcu(dev);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (!idev)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ifa = idev->ifa_list;
|
|
|
|
if (!ifa)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
addr = ifa->ifa_local;
|
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2007-08-23 00:06:58 +00:00
|
|
|
static int bond_has_this_ip(struct bonding *bond, __be32 ip)
|
2006-09-23 04:54:53 +00:00
|
|
|
{
|
2008-05-18 04:10:10 +00:00
|
|
|
struct vlan_entry *vlan;
|
2006-09-23 04:54:53 +00:00
|
|
|
|
|
|
|
if (ip == bond->master_ip)
|
|
|
|
return 1;
|
|
|
|
|
2008-05-18 04:10:10 +00:00
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
2006-09-23 04:54:53 +00:00
|
|
|
if (ip == vlan->vlan_ip)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
/*
|
|
|
|
* We go to the (large) trouble of VLAN tagging ARP frames because
|
|
|
|
* switches in VLAN mode (especially if ports are configured as
|
|
|
|
* "native" to a VLAN) might not pass non-tagged frames.
|
|
|
|
*/
|
2007-08-23 00:06:58 +00:00
|
|
|
static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, unsigned short vlan_id)
|
2005-06-26 21:52:20 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("arp %d on slave %s: dst %x src %x vid %d\n", arp_op,
|
2005-06-26 21:52:20 +00:00
|
|
|
slave_dev->name, dest_ip, src_ip, vlan_id);
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip,
|
|
|
|
NULL, slave_dev->dev_addr, NULL);
|
|
|
|
|
|
|
|
if (!skb) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": ARP packet allocation failed\n");
|
2005-06-26 21:52:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (vlan_id) {
|
|
|
|
skb = vlan_put_tag(skb, vlan_id);
|
|
|
|
if (!skb) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": failed to insert VLAN tag\n");
|
2005-06-26 21:52:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arp_xmit(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
|
|
|
|
{
|
2005-06-26 21:52:20 +00:00
|
|
|
int i, vlan_id, rv;
|
2007-08-23 00:06:58 +00:00
|
|
|
__be32 *targets = bond->params.arp_targets;
|
2008-05-18 04:10:10 +00:00
|
|
|
struct vlan_entry *vlan;
|
2005-06-26 21:52:20 +00:00
|
|
|
struct net_device *vlan_dev;
|
|
|
|
struct flowi fl;
|
|
|
|
struct rtable *rt;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 18:36:19 +00:00
|
|
|
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
|
|
|
|
if (!targets[i])
|
2009-04-13 07:11:30 +00:00
|
|
|
break;
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("basa: target %x\n", targets[i]);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (list_empty(&bond->vlan_list)) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("basa: empty vlan: arp_send\n");
|
2005-06-26 21:52:20 +00:00
|
|
|
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
|
bond->master_ip, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If VLANs are configured, we do a route lookup to
|
|
|
|
* determine which VLAN interface would be used, so we
|
|
|
|
* can tag the ARP with the proper VLAN tag.
|
|
|
|
*/
|
|
|
|
memset(&fl, 0, sizeof(fl));
|
|
|
|
fl.fl4_dst = targets[i];
|
|
|
|
fl.fl4_tos = RTO_ONLINK;
|
|
|
|
|
2008-01-23 06:07:34 +00:00
|
|
|
rv = ip_route_output_key(&init_net, &rt, &fl);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (rv) {
|
|
|
|
if (net_ratelimit()) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-10-31 07:56:00 +00:00
|
|
|
": %s: no route to arp_ip_target %pI4\n",
|
|
|
|
bond->dev->name, &fl.fl4_dst);
|
2005-06-26 21:52:20 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This target is not on a VLAN
|
|
|
|
*/
|
|
|
|
if (rt->u.dst.dev == bond->dev) {
|
2005-09-14 21:52:09 +00:00
|
|
|
ip_rt_put(rt);
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("basa: rtdev == bond->dev: arp_send\n");
|
2005-06-26 21:52:20 +00:00
|
|
|
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
|
bond->master_ip, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan_id = 0;
|
2008-05-18 04:10:10 +00:00
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (vlan_dev == rt->u.dst.dev) {
|
|
|
|
vlan_id = vlan->vlan_id;
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("basa: vlan match on %s %d\n",
|
2005-06-26 21:52:20 +00:00
|
|
|
vlan_dev->name, vlan_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vlan_id) {
|
2005-09-14 21:52:09 +00:00
|
|
|
ip_rt_put(rt);
|
2005-06-26 21:52:20 +00:00
|
|
|
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
|
|
|
|
vlan->vlan_ip, vlan_id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_ratelimit()) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-10-31 07:56:00 +00:00
|
|
|
": %s: no path to arp_ip_target %pI4 via rt.dev %s\n",
|
|
|
|
bond->dev->name, &fl.fl4_dst,
|
2005-06-26 21:52:20 +00:00
|
|
|
rt->u.dst.dev ? rt->u.dst.dev->name : "NULL");
|
|
|
|
}
|
2005-09-14 21:52:09 +00:00
|
|
|
ip_rt_put(rt);
|
2005-06-26 21:52:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kick out a gratuitous ARP for an IP on the bonding master plus one
|
|
|
|
* for each VLAN above us.
|
2008-06-14 01:12:03 +00:00
|
|
|
*
|
|
|
|
* Caller must hold curr_slave_lock for read or better
|
2005-06-26 21:52:20 +00:00
|
|
|
*/
|
|
|
|
static void bond_send_gratuitous_arp(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave = bond->curr_active_slave;
|
|
|
|
struct vlan_entry *vlan;
|
|
|
|
struct net_device *vlan_dev;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond_send_grat_arp: bond %s slave %s\n", bond->dev->name,
|
2005-06-26 21:52:20 +00:00
|
|
|
slave ? slave->dev->name : "NULL");
|
2008-06-14 01:12:03 +00:00
|
|
|
|
|
|
|
if (!slave || !bond->send_grat_arp ||
|
|
|
|
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
|
2005-06-26 21:52:20 +00:00
|
|
|
return;
|
|
|
|
|
2008-06-14 01:12:03 +00:00
|
|
|
bond->send_grat_arp--;
|
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
if (bond->master_ip) {
|
|
|
|
bond_arp_send(slave->dev, ARPOP_REPLY, bond->master_ip,
|
2007-10-10 02:43:42 +00:00
|
|
|
bond->master_ip, 0);
|
2005-06-26 21:52:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (vlan->vlan_ip) {
|
|
|
|
bond_arp_send(slave->dev, ARPOP_REPLY, vlan->vlan_ip,
|
|
|
|
vlan->vlan_ip, vlan->vlan_id);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-23 00:06:58 +00:00
|
|
|
static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
|
2006-09-23 04:54:53 +00:00
|
|
|
{
|
|
|
|
int i;
|
2007-08-23 00:06:58 +00:00
|
|
|
__be32 *targets = bond->params.arp_targets;
|
2006-09-23 04:54:53 +00:00
|
|
|
|
|
|
|
for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n",
|
2008-10-31 07:56:00 +00:00
|
|
|
&sip, &tip, i, &targets[i], bond_has_this_ip(bond, tip));
|
2006-09-23 04:54:53 +00:00
|
|
|
if (sip == targets[i]) {
|
|
|
|
if (bond_has_this_ip(bond, tip))
|
|
|
|
slave->last_arp_rx = jiffies;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
|
|
|
|
{
|
|
|
|
struct arphdr *arp;
|
|
|
|
struct slave *slave;
|
|
|
|
struct bonding *bond;
|
|
|
|
unsigned char *arp_ptr;
|
2007-08-23 00:06:58 +00:00
|
|
|
__be32 sip, tip;
|
2006-09-23 04:54:53 +00:00
|
|
|
|
2008-03-25 12:47:49 +00:00
|
|
|
if (dev_net(dev) != &init_net)
|
2007-09-17 18:53:39 +00:00
|
|
|
goto out;
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
|
|
|
|
goto out;
|
|
|
|
|
2008-11-13 07:37:49 +00:00
|
|
|
bond = netdev_priv(dev);
|
2006-09-23 04:54:53 +00:00
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
|
2006-09-23 04:54:53 +00:00
|
|
|
bond->dev->name, skb->dev ? skb->dev->name : "NULL",
|
|
|
|
orig_dev ? orig_dev->name : "NULL");
|
|
|
|
|
|
|
|
slave = bond_get_slave_by_dev(bond, orig_dev);
|
|
|
|
if (!slave || !slave_do_arp_validate(bond, slave))
|
|
|
|
goto out_unlock;
|
|
|
|
|
2008-03-03 20:20:57 +00:00
|
|
|
if (!pskb_may_pull(skb, arp_hdr_len(dev)))
|
2006-09-23 04:54:53 +00:00
|
|
|
goto out_unlock;
|
|
|
|
|
2007-03-12 23:56:31 +00:00
|
|
|
arp = arp_hdr(skb);
|
2006-09-23 04:54:53 +00:00
|
|
|
if (arp->ar_hln != dev->addr_len ||
|
|
|
|
skb->pkt_type == PACKET_OTHERHOST ||
|
|
|
|
skb->pkt_type == PACKET_LOOPBACK ||
|
|
|
|
arp->ar_hrd != htons(ARPHRD_ETHER) ||
|
|
|
|
arp->ar_pro != htons(ETH_P_IP) ||
|
|
|
|
arp->ar_pln != 4)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
arp_ptr = (unsigned char *)(arp + 1);
|
|
|
|
arp_ptr += dev->addr_len;
|
|
|
|
memcpy(&sip, arp_ptr, 4);
|
|
|
|
arp_ptr += 4 + dev->addr_len;
|
|
|
|
memcpy(&tip, arp_ptr, 4);
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond_arp_rcv: %s %s/%d av %d sv %d sip %pI4 tip %pI4\n",
|
2008-10-31 07:56:00 +00:00
|
|
|
bond->dev->name, slave->dev->name, slave->state,
|
|
|
|
bond->params.arp_validate, slave_do_arp_validate(bond, slave),
|
|
|
|
&sip, &tip);
|
2006-09-23 04:54:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Backup slaves won't see the ARP reply, but do come through
|
|
|
|
* here for each ARP probe (so we swap the sip/tip to validate
|
|
|
|
* the probe). In a "redundant switch, common router" type of
|
|
|
|
* configuration, the ARP probe will (hopefully) travel from
|
|
|
|
* the active, through one switch, the router, then the other
|
|
|
|
* switch before reaching the backup.
|
|
|
|
*/
|
|
|
|
if (slave->state == BOND_STATE_ACTIVE)
|
|
|
|
bond_validate_arp(bond, slave, sip, tip);
|
|
|
|
else
|
|
|
|
bond_validate_arp(bond, slave, tip, sip);
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
out:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return NET_RX_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* this function is called regularly to monitor each slave's link
|
|
|
|
* ensuring that traffic is being sent and received when arp monitoring
|
|
|
|
* is used in load-balancing mode. if the adapter has been dormant, then an
|
|
|
|
* arp is transmitted to generate traffic. see activebackup_arp_monitor for
|
|
|
|
* arp monitoring in active backup mode.
|
|
|
|
*/
|
2007-10-18 00:37:45 +00:00
|
|
|
void bond_loadbalance_arp_mon(struct work_struct *work)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-10-18 00:37:45 +00:00
|
|
|
struct bonding *bond = container_of(work, struct bonding,
|
|
|
|
arp_work.work);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *oldcurrent;
|
|
|
|
int do_failover = 0;
|
|
|
|
int delta_in_ticks;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2008-05-18 04:10:07 +00:00
|
|
|
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->kill_timers)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->slave_cnt == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto re_arm;
|
|
|
|
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
oldcurrent = bond->curr_active_slave;
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
|
|
|
|
/* see if any of the previous devices are up now (i.e. they have
|
|
|
|
* xmt and rcv traffic). the curr_active_slave does not come into
|
|
|
|
* the picture unless it is null. also, slave->jiffies is not needed
|
|
|
|
* here because we send an arp on each slave and give a slave as
|
|
|
|
* long as it needs to get the tx/rx within the delta.
|
|
|
|
* TODO: what about up/down delay in arp mode? it wasn't here before
|
|
|
|
* so it can wait
|
|
|
|
*/
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
if (slave->link != BOND_LINK_UP) {
|
2009-05-18 03:55:16 +00:00
|
|
|
if (time_before_eq(jiffies, dev_trans_start(slave->dev) + delta_in_ticks) &&
|
2007-12-07 07:40:33 +00:00
|
|
|
time_before_eq(jiffies, slave->dev->last_rx + delta_in_ticks)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
|
slave->state = BOND_STATE_ACTIVE;
|
|
|
|
|
|
|
|
/* primary_slave has no meaning in round-robin
|
|
|
|
* mode. the window of a slave being up and
|
|
|
|
* curr_active_slave being null after enslaving
|
|
|
|
* is closed.
|
|
|
|
*/
|
|
|
|
if (!oldcurrent) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: link status definitely "
|
|
|
|
"up for interface %s, ",
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
do_failover = 1;
|
|
|
|
} else {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: interface %s is now up\n",
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* slave->link == BOND_LINK_UP */
|
|
|
|
|
|
|
|
/* not all switches will respond to an arp request
|
|
|
|
* when the source ip is 0, so don't take the link down
|
|
|
|
* if we don't know our ip yet
|
|
|
|
*/
|
2009-05-18 03:55:16 +00:00
|
|
|
if (time_after_eq(jiffies, dev_trans_start(slave->dev) + 2*delta_in_ticks) ||
|
2008-05-18 04:10:08 +00:00
|
|
|
(time_after_eq(jiffies, slave->dev->last_rx + 2*delta_in_ticks))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
slave->link = BOND_LINK_DOWN;
|
|
|
|
slave->state = BOND_STATE_BACKUP;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave->link_failure_count < UINT_MAX)
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->link_failure_count++;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: interface %s is now down.\n",
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave == oldcurrent)
|
2005-04-16 22:20:36 +00:00
|
|
|
do_failover = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* note: if switch is in round-robin mode, all links
|
|
|
|
* must tx arp to ensure all links rx an arp - otherwise
|
|
|
|
* links may oscillate or not come up at all; if switch is
|
|
|
|
* in something like xor mode, there is nothing we can
|
|
|
|
* do - all replies will be rx'ed on same link causing slaves
|
|
|
|
* to be unstable during low/no traffic periods
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
if (IS_UP(slave->dev))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_arp_send_all(bond, slave);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_failover) {
|
2007-10-18 00:37:49 +00:00
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_select_active_slave(bond);
|
|
|
|
|
2007-10-18 00:37:49 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
re_arm:
|
2007-10-18 00:37:45 +00:00
|
|
|
if (bond->params.arp_interval)
|
|
|
|
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
|
2005-04-16 22:20:36 +00:00
|
|
|
out:
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-05-18 04:10:13 +00:00
|
|
|
* Called to inspect slaves for active-backup mode ARP monitor link state
|
|
|
|
* changes. Sets new_link in slaves to specify what action should take
|
|
|
|
* place for the slave. Returns 0 if no changes are found, >0 if changes
|
|
|
|
* to link states must be committed.
|
|
|
|
*
|
|
|
|
* Called with bond->lock held for read.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-05-18 04:10:13 +00:00
|
|
|
static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct slave *slave;
|
2008-05-18 04:10:13 +00:00
|
|
|
int i, commit = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
slave->new_link = BOND_LINK_NOCHANGE;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (slave->link != BOND_LINK_UP) {
|
|
|
|
if (time_before_eq(jiffies, slave_last_rx(bond, slave) +
|
|
|
|
delta_in_ticks)) {
|
|
|
|
slave->new_link = BOND_LINK_UP;
|
|
|
|
commit++;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
continue;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/*
|
|
|
|
* Give slaves 2*delta after being enslaved or made
|
|
|
|
* active. This avoids bouncing, as the last receive
|
|
|
|
* times need a full ARP monitor cycle to be updated.
|
|
|
|
*/
|
|
|
|
if (!time_after_eq(jiffies, slave->jiffies +
|
|
|
|
2 * delta_in_ticks))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Backup slave is down if:
|
|
|
|
* - No current_arp_slave AND
|
|
|
|
* - more than 3*delta since last receive AND
|
|
|
|
* - the bond has an IP address
|
|
|
|
*
|
|
|
|
* Note: a non-null current_arp_slave indicates
|
|
|
|
* the curr_active_slave went down and we are
|
|
|
|
* searching for a new one; under this condition
|
|
|
|
* we only take the curr_active_slave down - this
|
|
|
|
* gives each slave a chance to tx/rx traffic
|
|
|
|
* before being taken out
|
|
|
|
*/
|
|
|
|
if (slave->state == BOND_STATE_BACKUP &&
|
|
|
|
!bond->current_arp_slave &&
|
|
|
|
time_after(jiffies, slave_last_rx(bond, slave) +
|
|
|
|
3 * delta_in_ticks)) {
|
|
|
|
slave->new_link = BOND_LINK_DOWN;
|
|
|
|
commit++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Active slave is down if:
|
|
|
|
* - more than 2*delta since transmitting OR
|
|
|
|
* - (more than 2*delta since receive AND
|
|
|
|
* the bond has an IP address)
|
|
|
|
*/
|
|
|
|
if ((slave->state == BOND_STATE_ACTIVE) &&
|
2009-05-18 03:55:16 +00:00
|
|
|
(time_after_eq(jiffies, dev_trans_start(slave->dev) +
|
2008-05-18 04:10:13 +00:00
|
|
|
2 * delta_in_ticks) ||
|
|
|
|
(time_after_eq(jiffies, slave_last_rx(bond, slave)
|
|
|
|
+ 2 * delta_in_ticks)))) {
|
|
|
|
slave->new_link = BOND_LINK_DOWN;
|
|
|
|
commit++;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trigger a commit if the primary option setting has changed.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond->primary_slave &&
|
|
|
|
(bond->primary_slave != bond->curr_active_slave) &&
|
|
|
|
(bond->primary_slave->link == BOND_LINK_UP))
|
|
|
|
commit++;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
read_unlock(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
return commit;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/*
|
|
|
|
* Called to commit link state changes noted by inspection step of
|
|
|
|
* active-backup mode ARP monitor.
|
|
|
|
*
|
|
|
|
* Called with RTNL and bond->lock for read.
|
|
|
|
*/
|
|
|
|
static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
switch (slave->new_link) {
|
|
|
|
case BOND_LINK_NOCHANGE:
|
|
|
|
continue;
|
2006-03-27 21:27:43 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
case BOND_LINK_UP:
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (!bond->curr_active_slave &&
|
2009-05-18 03:55:16 +00:00
|
|
|
time_before_eq(jiffies, dev_trans_start(slave->dev) +
|
2008-05-18 04:10:13 +00:00
|
|
|
delta_in_ticks)) {
|
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
|
bond_change_active_slave(bond, slave);
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-05-18 04:10:13 +00:00
|
|
|
": %s: %s is up and now the "
|
|
|
|
"active interface\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
|
|
|
|
|
|
|
} else if (bond->curr_active_slave != slave) {
|
|
|
|
/* this slave has just come up but we
|
|
|
|
* already have a current slave; this can
|
|
|
|
* also happen if bond_enslave adds a new
|
|
|
|
* slave that is up while we are searching
|
|
|
|
* for a new slave
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-05-18 04:10:13 +00:00
|
|
|
slave->link = BOND_LINK_UP;
|
|
|
|
bond_set_slave_inactive_flags(slave);
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-05-18 04:10:13 +00:00
|
|
|
": %s: backup interface %s is now up\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
case BOND_LINK_DOWN:
|
|
|
|
if (slave->link_failure_count < UINT_MAX)
|
|
|
|
slave->link_failure_count++;
|
|
|
|
|
|
|
|
slave->link = BOND_LINK_DOWN;
|
|
|
|
|
|
|
|
if (slave == bond->curr_active_slave) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-05-18 04:10:13 +00:00
|
|
|
": %s: link status down for active "
|
|
|
|
"interface %s, disabling it\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_set_slave_inactive_flags(slave);
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
|
|
|
|
bond_select_active_slave(bond);
|
|
|
|
if (bond->curr_active_slave)
|
|
|
|
bond->curr_active_slave->jiffies =
|
|
|
|
jiffies;
|
|
|
|
|
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
|
|
|
|
} else if (slave->state == BOND_STATE_BACKUP) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": %s: backup interface %s is now down\n",
|
2008-05-18 04:10:13 +00:00
|
|
|
bond->dev->name, slave->dev->name);
|
|
|
|
|
|
|
|
bond_set_slave_inactive_flags(slave);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-05-18 04:10:13 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-05-18 04:10:13 +00:00
|
|
|
": %s: impossible: new_link %d on slave %s\n",
|
|
|
|
bond->dev->name, slave->new_link,
|
|
|
|
slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/*
|
|
|
|
* No race with changes to primary via sysfs, as we hold rtnl.
|
|
|
|
*/
|
|
|
|
if (bond->primary_slave &&
|
|
|
|
(bond->primary_slave != bond->curr_active_slave) &&
|
|
|
|
(bond->primary_slave->link == BOND_LINK_UP)) {
|
|
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
|
|
bond_change_active_slave(bond, bond->primary_slave);
|
|
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_set_carrier(bond);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/*
|
|
|
|
* Send ARP probes for active-backup mode ARP monitor.
|
|
|
|
*
|
|
|
|
* Called with bond->lock held for read.
|
|
|
|
*/
|
|
|
|
static void bond_ab_arp_probe(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
read_lock(&bond->curr_slave_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond->current_arp_slave && bond->curr_active_slave)
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME "PROBE: c_arp %s && cas %s BAD\n",
|
2008-05-18 04:10:13 +00:00
|
|
|
bond->current_arp_slave->dev->name,
|
|
|
|
bond->curr_active_slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond->curr_active_slave) {
|
|
|
|
bond_arp_send_all(bond, bond->curr_active_slave);
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
return;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
read_unlock(&bond->curr_slave_lock);
|
2007-10-18 00:37:49 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/* if we don't have a curr_active_slave, search for the next available
|
|
|
|
* backup slave from the current_arp_slave and make it the candidate
|
|
|
|
* for becoming the curr_active_slave
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (!bond->current_arp_slave) {
|
|
|
|
bond->current_arp_slave = bond->first_slave;
|
|
|
|
if (!bond->current_arp_slave)
|
|
|
|
return;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_set_slave_inactive_flags(bond->current_arp_slave);
|
2007-10-18 00:37:49 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/* search for next candidate */
|
|
|
|
bond_for_each_slave_from(bond, slave, i, bond->current_arp_slave->next) {
|
|
|
|
if (IS_UP(slave->dev)) {
|
|
|
|
slave->link = BOND_LINK_BACK;
|
|
|
|
bond_set_slave_active_flags(slave);
|
|
|
|
bond_arp_send_all(bond, slave);
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->jiffies = jiffies;
|
2008-05-18 04:10:13 +00:00
|
|
|
bond->current_arp_slave = slave;
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
/* if the link state is up at this point, we
|
|
|
|
* mark it down - this can happen if we have
|
|
|
|
* simultaneous link failures and
|
|
|
|
* reselect_active_interface doesn't make this
|
|
|
|
* one the current slave so it is still marked
|
|
|
|
* up when it is actually down
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-05-18 04:10:13 +00:00
|
|
|
if (slave->link == BOND_LINK_UP) {
|
|
|
|
slave->link = BOND_LINK_DOWN;
|
|
|
|
if (slave->link_failure_count < UINT_MAX)
|
|
|
|
slave->link_failure_count++;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_set_slave_inactive_flags(slave);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2008-05-18 04:10:13 +00:00
|
|
|
": %s: backup interface %s is now down.\n",
|
|
|
|
bond->dev->name, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-05-18 04:10:13 +00:00
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
void bond_activebackup_arp_mon(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct bonding *bond = container_of(work, struct bonding,
|
|
|
|
arp_work.work);
|
|
|
|
int delta_in_ticks;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond->kill_timers)
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond->slave_cnt == 0)
|
|
|
|
goto re_arm;
|
|
|
|
|
2008-06-14 01:12:03 +00:00
|
|
|
if (bond->send_grat_arp) {
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
bond_send_gratuitous_arp(bond);
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
}
|
|
|
|
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
if (bond->send_unsol_na) {
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
bond_send_unsolicited_na(bond);
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
rtnl_lock();
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
|
|
|
bond_ab_arp_commit(bond, delta_in_ticks);
|
|
|
|
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
rtnl_unlock();
|
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:13 +00:00
|
|
|
bond_ab_arp_probe(bond);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
re_arm:
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->params.arp_interval)
|
2007-10-18 00:37:45 +00:00
|
|
|
queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
|
2005-04-16 22:20:36 +00:00
|
|
|
out:
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------ proc/seq_file-------------------------------*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
|
|
|
|
static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
|
2009-02-14 11:15:33 +00:00
|
|
|
__acquires(&dev_base_lock)
|
|
|
|
__acquires(&bond->lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
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);
|
2007-10-18 00:37:50 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (*pos == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return SEQ_START_TOKEN;
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (++off == *pos)
|
2005-04-16 22:20:36 +00:00
|
|
|
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;
|
2009-06-12 19:02:48 +00:00
|
|
|
if (v == SEQ_START_TOKEN)
|
2005-04-16 22:20:36 +00:00
|
|
|
return bond->first_slave;
|
|
|
|
|
|
|
|
slave = slave->next;
|
|
|
|
|
|
|
|
return (slave == bond->first_slave) ? NULL : slave;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_info_seq_stop(struct seq_file *seq, void *v)
|
2009-02-14 11:15:33 +00:00
|
|
|
__releases(&bond->lock)
|
|
|
|
__releases(&dev_base_lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct bonding *bond = seq->private;
|
|
|
|
|
2007-10-18 00:37:50 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
read_unlock(&dev_base_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_info_show_master(struct seq_file *seq)
|
|
|
|
{
|
|
|
|
struct bonding *bond = seq->private;
|
|
|
|
struct slave *curr;
|
2005-11-09 18:36:25 +00:00
|
|
|
int i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
curr = bond->curr_active_slave;
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
|
2007-10-10 02:57:24 +00:00
|
|
|
seq_printf(seq, "Bonding Mode: %s",
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_mode_name(bond->params.mode));
|
|
|
|
|
2007-10-10 02:57:24 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP &&
|
|
|
|
bond->params.fail_over_mac)
|
2008-05-18 04:10:14 +00:00
|
|
|
seq_printf(seq, " (fail_over_mac %s)",
|
|
|
|
fail_over_mac_tbl[bond->params.fail_over_mac].modename);
|
2007-10-10 02:57:24 +00:00
|
|
|
|
|
|
|
seq_printf(seq, "\n");
|
|
|
|
|
2005-11-09 18:35:13 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_XOR ||
|
|
|
|
bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
|
|
|
|
xmit_hashtype_tbl[bond->params.xmit_policy].modename,
|
|
|
|
bond->params.xmit_policy);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (USES_PRIMARY(bond->params.mode)) {
|
|
|
|
seq_printf(seq, "Primary Slave: %s\n",
|
2005-11-09 18:35:21 +00:00
|
|
|
(bond->primary_slave) ?
|
|
|
|
bond->primary_slave->dev->name : "None");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
seq_printf(seq, "Currently Active Slave: %s\n",
|
|
|
|
(curr) ? curr->dev->name : "None");
|
|
|
|
}
|
|
|
|
|
2006-03-27 21:27:43 +00:00
|
|
|
seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ?
|
|
|
|
"up" : "down");
|
2005-04-16 22:20:36 +00:00
|
|
|
seq_printf(seq, "MII Polling Interval (ms): %d\n", bond->params.miimon);
|
|
|
|
seq_printf(seq, "Up Delay (ms): %d\n",
|
|
|
|
bond->params.updelay * bond->params.miimon);
|
|
|
|
seq_printf(seq, "Down Delay (ms): %d\n",
|
|
|
|
bond->params.downdelay * bond->params.miimon);
|
|
|
|
|
2005-11-09 18:36:25 +00:00
|
|
|
|
|
|
|
/* ARP information */
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->params.arp_interval > 0) {
|
|
|
|
int printed = 0;
|
2005-11-09 18:36:25 +00:00
|
|
|
seq_printf(seq, "ARP Polling Interval (ms): %d\n",
|
|
|
|
bond->params.arp_interval);
|
|
|
|
|
|
|
|
seq_printf(seq, "ARP IP target/s (n.n.n.n form):");
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
|
2005-11-09 18:36:25 +00:00
|
|
|
if (!bond->params.arp_targets[i])
|
2009-04-13 07:11:30 +00:00
|
|
|
break;
|
2005-11-09 18:36:25 +00:00
|
|
|
if (printed)
|
|
|
|
seq_printf(seq, ",");
|
2008-10-30 05:43:33 +00:00
|
|
|
seq_printf(seq, " %pI4", &bond->params.arp_targets[i]);
|
2005-11-09 18:36:25 +00:00
|
|
|
printed = 1;
|
|
|
|
}
|
|
|
|
seq_printf(seq, "\n");
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
struct ad_info ad_info;
|
|
|
|
|
|
|
|
seq_puts(seq, "\n802.3ad info\n");
|
|
|
|
seq_printf(seq, "LACP rate: %s\n",
|
|
|
|
(bond->params.lacp_fast) ? "fast" : "slow");
|
2008-11-05 01:51:16 +00:00
|
|
|
seq_printf(seq, "Aggregator selection policy (ad_select): %s\n",
|
|
|
|
ad_select_tbl[bond->params.ad_select].modename);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
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);
|
2008-10-27 22:59:26 +00:00
|
|
|
seq_printf(seq, "\tPartner Mac Address: %pM\n",
|
|
|
|
ad_info.partner_system);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
static void bond_info_show_slave(struct seq_file *seq,
|
|
|
|
const struct slave *slave)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct bonding *bond = seq->private;
|
|
|
|
|
|
|
|
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
|
|
|
|
seq_printf(seq, "MII Status: %s\n",
|
|
|
|
(slave->link == BOND_LINK_UP) ? "up" : "down");
|
2006-09-23 04:53:08 +00:00
|
|
|
seq_printf(seq, "Link Failure Count: %u\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->link_failure_count);
|
|
|
|
|
2008-10-27 22:59:26 +00:00
|
|
|
seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
const struct aggregator *agg
|
|
|
|
= SLAVE_AD_INFO(slave).port.aggregator;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (agg)
|
2005-04-16 22:20:36 +00:00
|
|
|
seq_printf(seq, "Aggregator ID: %d\n",
|
|
|
|
agg->aggregator_identifier);
|
2009-06-12 19:02:48 +00:00
|
|
|
else
|
2005-04-16 22:20:36 +00:00
|
|
|
seq_puts(seq, "Aggregator ID: N/A\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_info_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
|
|
seq_printf(seq, "%s\n", version);
|
|
|
|
bond_info_show_master(seq);
|
2009-06-12 19:02:48 +00:00
|
|
|
} else
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_info_show_slave(seq, v);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-14 21:52:18 +00:00
|
|
|
static const struct seq_operations bond_info_seq_ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.start = bond_info_seq_start,
|
|
|
|
.next = bond_info_seq_next,
|
|
|
|
.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;
|
|
|
|
}
|
|
|
|
|
2007-02-12 08:55:34 +00:00
|
|
|
static const struct file_operations bond_info_fops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.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;
|
|
|
|
|
|
|
|
if (bond_proc_dir) {
|
2008-04-29 08:02:29 +00:00
|
|
|
bond->proc_entry = proc_create_data(bond_dev->name,
|
|
|
|
S_IRUGO, bond_proc_dir,
|
|
|
|
&bond_info_fops, bond);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (bond->proc_entry == NULL)
|
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: Cannot create /proc/net/%s/%s\n",
|
|
|
|
DRV_NAME, bond_dev->name);
|
2009-06-12 19:02:48 +00:00
|
|
|
else
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_remove_proc_entry(struct bonding *bond)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
* Caller must hold rtnl_lock.
|
|
|
|
*/
|
|
|
|
static void bond_create_proc_dir(void)
|
|
|
|
{
|
|
|
|
if (!bond_proc_dir) {
|
2007-09-12 10:01:34 +00:00
|
|
|
bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net);
|
2009-03-25 19:48:06 +00:00
|
|
|
if (!bond_proc_dir)
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: cannot create /proc/net/%s\n",
|
|
|
|
DRV_NAME);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy the bonding directory under /proc/net, if empty.
|
|
|
|
* Caller must hold rtnl_lock.
|
|
|
|
*/
|
|
|
|
static void bond_destroy_proc_dir(void)
|
|
|
|
{
|
2009-03-25 19:48:06 +00:00
|
|
|
if (bond_proc_dir) {
|
2007-09-12 10:01:34 +00:00
|
|
|
remove_proc_entry(DRV_NAME, init_net.proc_net);
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_proc_dir = NULL;
|
|
|
|
}
|
|
|
|
}
|
2009-05-05 06:20:51 +00:00
|
|
|
|
|
|
|
#else /* !CONFIG_PROC_FS */
|
|
|
|
|
|
|
|
static int bond_create_proc_entry(struct bonding *bond)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_remove_proc_entry(struct bonding *bond)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_create_proc_dir(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_destroy_proc_dir(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
2009-05-05 06:20:51 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*-------------------------- netdev event handling --------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change device name
|
|
|
|
*/
|
|
|
|
static int bond_event_changename(struct bonding *bond)
|
|
|
|
{
|
|
|
|
bond_remove_proc_entry(bond);
|
|
|
|
bond_create_proc_entry(bond);
|
2009-06-12 19:02:46 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
bond_destroy_sysfs_entry(bond);
|
|
|
|
bond_create_sysfs_entry(bond);
|
2009-06-12 19:02:46 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
static int bond_master_netdev_event(unsigned long event,
|
|
|
|
struct net_device *bond_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *event_bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_CHANGENAME:
|
|
|
|
return bond_event_changename(event_bond);
|
|
|
|
case NETDEV_UNREGISTER:
|
2008-01-18 00:25:00 +00:00
|
|
|
bond_release_all(event_bond->dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
static int bond_slave_netdev_event(unsigned long event,
|
|
|
|
struct net_device *slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct net_device *bond_dev = slave_dev->master;
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UNREGISTER:
|
|
|
|
if (bond_dev) {
|
2007-10-15 23:44:27 +00:00
|
|
|
if (bond->setup_by_slave)
|
|
|
|
bond_release_and_destroy(bond_dev, slave_dev);
|
|
|
|
else
|
|
|
|
bond_release(bond_dev, slave_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NETDEV_CHANGE:
|
2009-03-19 01:38:25 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) {
|
|
|
|
struct slave *slave;
|
|
|
|
|
|
|
|
slave = bond_get_slave_by_dev(bond, slave_dev);
|
|
|
|
if (slave) {
|
|
|
|
u16 old_speed = slave->speed;
|
|
|
|
u16 old_duplex = slave->duplex;
|
|
|
|
|
|
|
|
bond_update_speed_duplex(slave);
|
|
|
|
|
|
|
|
if (bond_is_lb(bond))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (old_speed != slave->speed)
|
|
|
|
bond_3ad_adapter_speed_changed(slave);
|
|
|
|
if (old_duplex != slave->duplex)
|
|
|
|
bond_3ad_adapter_duplex_changed(slave);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
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;
|
2005-08-23 05:34:53 +00:00
|
|
|
case NETDEV_FEAT_CHANGE:
|
|
|
|
bond_compute_features(bond);
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bond_netdev_event: handle netdev notifier chain events.
|
|
|
|
*
|
|
|
|
* This function receives events for the netdev chain. The caller (an
|
[PATCH] Notifier chain update: API changes
The kernel's implementation of notifier chains is unsafe. There is no
protection against entries being added to or removed from a chain while the
chain is in use. The issues were discussed in this thread:
http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2
We noticed that notifier chains in the kernel fall into two basic usage
classes:
"Blocking" chains are always called from a process context
and the callout routines are allowed to sleep;
"Atomic" chains can be called from an atomic context and
the callout routines are not allowed to sleep.
We decided to codify this distinction and make it part of the API. Therefore
this set of patches introduces three new, parallel APIs: one for blocking
notifiers, one for atomic notifiers, and one for "raw" notifiers (which is
really just the old API under a new name). New kinds of data structures are
used for the heads of the chains, and new routines are defined for
registration, unregistration, and calling a chain. The three APIs are
explained in include/linux/notifier.h and their implementation is in
kernel/sys.c.
With atomic and blocking chains, the implementation guarantees that the chain
links will not be corrupted and that chain callers will not get messed up by
entries being added or removed. For raw chains the implementation provides no
guarantees at all; users of this API must provide their own protections. (The
idea was that situations may come up where the assumptions of the atomic and
blocking APIs are not appropriate, so it should be possible for users to
handle these things in their own way.)
There are some limitations, which should not be too hard to live with. For
atomic/blocking chains, registration and unregistration must always be done in
a process context since the chain is protected by a mutex/rwsem. Also, a
callout routine for a non-raw chain must not try to register or unregister
entries on its own chain. (This did happen in a couple of places and the code
had to be changed to avoid it.)
Since atomic chains may be called from within an NMI handler, they cannot use
spinlocks for synchronization. Instead we use RCU. The overhead falls almost
entirely in the unregister routine, which is okay since unregistration is much
less frequent that calling a chain.
Here is the list of chains that we adjusted and their classifications. None
of them use the raw API, so for the moment it is only a placeholder.
ATOMIC CHAINS
-------------
arch/i386/kernel/traps.c: i386die_chain
arch/ia64/kernel/traps.c: ia64die_chain
arch/powerpc/kernel/traps.c: powerpc_die_chain
arch/sparc64/kernel/traps.c: sparc64die_chain
arch/x86_64/kernel/traps.c: die_chain
drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list
kernel/panic.c: panic_notifier_list
kernel/profile.c: task_free_notifier
net/bluetooth/hci_core.c: hci_notifier
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain
net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain
net/ipv6/addrconf.c: inet6addr_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_chain
net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain
net/netlink/af_netlink.c: netlink_chain
BLOCKING CHAINS
---------------
arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain
arch/s390/kernel/process.c: idle_chain
arch/x86_64/kernel/process.c idle_notifier
drivers/base/memory.c: memory_chain
drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list
drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list
drivers/macintosh/adb.c: adb_client_list
drivers/macintosh/via-pmu.c sleep_notifier_list
drivers/macintosh/via-pmu68k.c sleep_notifier_list
drivers/macintosh/windfarm_core.c wf_client_list
drivers/usb/core/notify.c usb_notifier_list
drivers/video/fbmem.c fb_notifier_list
kernel/cpu.c cpu_chain
kernel/module.c module_notify_list
kernel/profile.c munmap_notifier
kernel/profile.c task_exit_notifier
kernel/sys.c reboot_notifier_list
net/core/dev.c netdev_chain
net/decnet/dn_dev.c: dnaddr_chain
net/ipv4/devinet.c: inetaddr_chain
It's possible that some of these classifications are wrong. If they are,
please let us know or submit a patch to fix them. Note that any chain that
gets called very frequently should be atomic, because the rwsem read-locking
used for blocking chains is very likely to incur cache misses on SMP systems.
(However, if the chain's callout routines may sleep then the chain cannot be
atomic.)
The patch set was written by Alan Stern and Chandra Seetharaman, incorporating
material written by Keith Owens and suggestions from Paul McKenney and Andrew
Morton.
[jes@sgi.com: restructure the notifier chain initialization macros]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Jes Sorensen <jes@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-27 09:16:30 +00:00
|
|
|
* ioctl handler calling blocking_notifier_call_chain) holds the necessary
|
2005-04-16 22:20:36 +00:00
|
|
|
* locks for us to safely manipulate the slave devices (RTNL lock,
|
|
|
|
* dev_probe_lock).
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
static int bond_netdev_event(struct notifier_block *this,
|
|
|
|
unsigned long event, void *ptr)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct net_device *event_dev = (struct net_device *)ptr;
|
|
|
|
|
2008-03-25 12:47:49 +00:00
|
|
|
if (dev_net(event_dev) != &init_net)
|
2007-09-12 11:02:17 +00:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("event_dev: %s, event: %lx\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
(event_dev ? event_dev->name : "None"),
|
|
|
|
event);
|
|
|
|
|
2006-09-23 04:54:10 +00:00
|
|
|
if (!(event_dev->priv_flags & IFF_BONDING))
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (event_dev->flags & IFF_MASTER) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("IFF_MASTER\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return bond_master_netdev_event(event, event_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event_dev->flags & IFF_SLAVE) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("IFF_SLAVE\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return bond_slave_netdev_event(event, event_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
/*
|
|
|
|
* bond_inetaddr_event: handle inetaddr notifier chain events.
|
|
|
|
*
|
|
|
|
* We keep track of device IPs primarily to use as source addresses in
|
|
|
|
* ARP monitor probes (rather than spewing out broadcasts all the time).
|
|
|
|
*
|
|
|
|
* We track one IP for the main device (if it has one), plus one per VLAN.
|
|
|
|
*/
|
|
|
|
static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
{
|
|
|
|
struct in_ifaddr *ifa = ptr;
|
|
|
|
struct net_device *vlan_dev, *event_dev = ifa->ifa_dev->dev;
|
2008-05-18 04:10:10 +00:00
|
|
|
struct bonding *bond;
|
|
|
|
struct vlan_entry *vlan;
|
2005-06-26 21:52:20 +00:00
|
|
|
|
2008-03-25 12:47:49 +00:00
|
|
|
if (dev_net(ifa->ifa_dev->dev) != &init_net)
|
2008-02-29 04:46:17 +00:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2008-05-18 04:10:10 +00:00
|
|
|
list_for_each_entry(bond, &bond_dev_list, bond_list) {
|
2005-06-26 21:52:20 +00:00
|
|
|
if (bond->dev == event_dev) {
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
|
|
|
bond->master_ip = ifa->ifa_local;
|
|
|
|
return NOTIFY_OK;
|
|
|
|
case NETDEV_DOWN:
|
|
|
|
bond->master_ip = bond_glean_dev_ip(bond->dev);
|
|
|
|
return NOTIFY_OK;
|
|
|
|
default:
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:10 +00:00
|
|
|
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
|
2007-03-03 04:44:51 +00:00
|
|
|
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
|
2005-06-26 21:52:20 +00:00
|
|
|
if (vlan_dev == event_dev) {
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
|
|
|
vlan->vlan_ip = ifa->ifa_local;
|
|
|
|
return NOTIFY_OK;
|
|
|
|
case NETDEV_DOWN:
|
|
|
|
vlan->vlan_ip =
|
|
|
|
bond_glean_dev_ip(vlan_dev);
|
|
|
|
return NOTIFY_OK;
|
|
|
|
default:
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static struct notifier_block bond_netdev_notifier = {
|
|
|
|
.notifier_call = bond_netdev_event,
|
|
|
|
};
|
|
|
|
|
2005-06-26 21:52:20 +00:00
|
|
|
static struct notifier_block bond_inetaddr_notifier = {
|
|
|
|
.notifier_call = bond_inetaddr_event,
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*-------------------------- 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);
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
|
|
|
|
}
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
void bond_register_arp(struct bonding *bond)
|
|
|
|
{
|
|
|
|
struct packet_type *pt = &bond->arp_mon_pt;
|
|
|
|
|
2007-03-01 01:03:20 +00:00
|
|
|
if (pt->type)
|
|
|
|
return;
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
pt->type = htons(ETH_P_ARP);
|
2007-03-01 01:03:27 +00:00
|
|
|
pt->dev = bond->dev;
|
2006-09-23 04:54:53 +00:00
|
|
|
pt->func = bond_arp_rcv;
|
|
|
|
dev_add_pack(pt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bond_unregister_arp(struct bonding *bond)
|
|
|
|
{
|
2007-03-01 01:03:20 +00:00
|
|
|
struct packet_type *pt = &bond->arp_mon_pt;
|
|
|
|
|
|
|
|
dev_remove_pack(pt);
|
|
|
|
pt->type = 0;
|
2006-09-23 04:54:53 +00:00
|
|
|
}
|
|
|
|
|
2005-06-26 21:54:11 +00:00
|
|
|
/*---------------------------- Hashing Policies -----------------------------*/
|
|
|
|
|
2007-12-07 07:40:34 +00:00
|
|
|
/*
|
|
|
|
* Hash for the output device based upon layer 2 and layer 3 data. If
|
|
|
|
* the packet is not IP mimic bond_xmit_hash_policy_l2()
|
|
|
|
*/
|
|
|
|
static int bond_xmit_hash_policy_l23(struct sk_buff *skb,
|
|
|
|
struct net_device *bond_dev, int count)
|
|
|
|
{
|
|
|
|
struct ethhdr *data = (struct ethhdr *)skb->data;
|
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
|
|
2008-09-02 14:08:08 +00:00
|
|
|
if (skb->protocol == htons(ETH_P_IP)) {
|
2007-12-07 07:40:34 +00:00
|
|
|
return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^
|
|
|
|
(data->h_dest[5] ^ bond_dev->dev_addr[5])) % count;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count;
|
|
|
|
}
|
|
|
|
|
2005-06-26 21:54:11 +00:00
|
|
|
/*
|
2007-05-09 06:57:56 +00:00
|
|
|
* Hash for the output device based upon layer 3 and layer 4 data. If
|
2005-06-26 21:54:11 +00:00
|
|
|
* the packet is a frag or not TCP or UDP, just use layer 3 data. If it is
|
|
|
|
* altogether not IP, mimic bond_xmit_hash_policy_l2()
|
|
|
|
*/
|
|
|
|
static int bond_xmit_hash_policy_l34(struct sk_buff *skb,
|
|
|
|
struct net_device *bond_dev, int count)
|
|
|
|
{
|
|
|
|
struct ethhdr *data = (struct ethhdr *)skb->data;
|
2007-04-21 05:47:35 +00:00
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
2007-08-23 00:06:58 +00:00
|
|
|
__be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl);
|
2005-06-26 21:54:11 +00:00
|
|
|
int layer4_xor = 0;
|
|
|
|
|
2008-09-02 14:08:08 +00:00
|
|
|
if (skb->protocol == htons(ETH_P_IP)) {
|
|
|
|
if (!(iph->frag_off & htons(IP_MF|IP_OFFSET)) &&
|
2005-06-26 21:54:11 +00:00
|
|
|
(iph->protocol == IPPROTO_TCP ||
|
|
|
|
iph->protocol == IPPROTO_UDP)) {
|
2007-08-23 00:06:58 +00:00
|
|
|
layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1)));
|
2005-06-26 21:54:11 +00:00
|
|
|
}
|
|
|
|
return (layer4_xor ^
|
|
|
|
((ntohl(iph->saddr ^ iph->daddr)) & 0xffff)) % count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hash for the output device based upon layer 2 data
|
|
|
|
*/
|
|
|
|
static int bond_xmit_hash_policy_l2(struct sk_buff *skb,
|
|
|
|
struct net_device *bond_dev, int count)
|
|
|
|
{
|
|
|
|
struct ethhdr *data = (struct ethhdr *)skb->data;
|
|
|
|
|
|
|
|
return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*-------------------------- Device entry points ----------------------------*/
|
|
|
|
|
|
|
|
static int bond_open(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond->kill_timers = 0;
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* bond_alb_initialize must be called before the timer
|
|
|
|
* is started.
|
|
|
|
*/
|
|
|
|
if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) {
|
|
|
|
/* something went wrong - fail the open operation */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-18 00:37:45 +00:00
|
|
|
INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
|
|
|
|
queue_delayed_work(bond->wq, &bond->alb_work, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bond->params.miimon) { /* link check interval, in milliseconds. */
|
2007-10-18 00:37:45 +00:00
|
|
|
INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
|
|
|
|
queue_delayed_work(bond->wq, &bond->mii_work, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
|
2007-10-18 00:37:45 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
|
|
|
|
INIT_DELAYED_WORK(&bond->arp_work,
|
|
|
|
bond_activebackup_arp_mon);
|
|
|
|
else
|
|
|
|
INIT_DELAYED_WORK(&bond->arp_work,
|
|
|
|
bond_loadbalance_arp_mon);
|
|
|
|
|
|
|
|
queue_delayed_work(bond->wq, &bond->arp_work, 0);
|
2006-09-23 04:54:53 +00:00
|
|
|
if (bond->params.arp_validate)
|
|
|
|
bond_register_arp(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
2007-10-24 16:27:43 +00:00
|
|
|
INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
|
2007-10-18 00:37:45 +00:00
|
|
|
queue_delayed_work(bond->wq, &bond->ad_work, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* register to receive LACPDUs */
|
|
|
|
bond_register_lacpdu(bond);
|
2008-11-05 01:51:16 +00:00
|
|
|
bond_3ad_initiate_agg_selection(bond, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_close(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD) {
|
|
|
|
/* Unregister the receive of LACPDUs */
|
|
|
|
bond_unregister_lacpdu(bond);
|
|
|
|
}
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
if (bond->params.arp_validate)
|
|
|
|
bond_unregister_arp(bond);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
|
2008-06-14 01:12:03 +00:00
|
|
|
bond->send_grat_arp = 0;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
bond->send_unsol_na = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* signal timers not to re-arm */
|
|
|
|
bond->kill_timers = 1;
|
|
|
|
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
if (bond->params.miimon) { /* link check interval, in milliseconds. */
|
2007-10-18 00:37:45 +00:00
|
|
|
cancel_delayed_work(&bond->mii_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
|
2007-10-18 00:37:45 +00:00
|
|
|
cancel_delayed_work(&bond->arp_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (bond->params.mode) {
|
|
|
|
case BOND_MODE_8023AD:
|
2007-10-18 00:37:45 +00:00
|
|
|
cancel_delayed_work(&bond->ad_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BOND_MODE_TLB:
|
|
|
|
case BOND_MODE_ALB:
|
2007-10-18 00:37:45 +00:00
|
|
|
cancel_delayed_work(&bond->alb_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-10 07:07:13 +00:00
|
|
|
if (bond_is_lb(bond)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Must be called only after all
|
|
|
|
* slaves have been released
|
|
|
|
*/
|
|
|
|
bond_alb_deinitialize(bond);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2008-11-20 05:40:23 +00:00
|
|
|
struct net_device_stats *stats = &bond->stats;
|
2008-01-30 02:07:46 +00:00
|
|
|
struct net_device_stats local_stats;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave;
|
|
|
|
int i;
|
|
|
|
|
2008-01-30 02:07:46 +00:00
|
|
|
memset(&local_stats, 0, sizeof(struct net_device_stats));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
read_lock_bh(&bond->lock);
|
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
2008-11-20 05:40:23 +00:00
|
|
|
const struct net_device_stats *sstats = dev_get_stats(slave->dev);
|
|
|
|
|
2008-01-30 02:07:46 +00:00
|
|
|
local_stats.rx_packets += sstats->rx_packets;
|
|
|
|
local_stats.rx_bytes += sstats->rx_bytes;
|
|
|
|
local_stats.rx_errors += sstats->rx_errors;
|
|
|
|
local_stats.rx_dropped += sstats->rx_dropped;
|
|
|
|
|
|
|
|
local_stats.tx_packets += sstats->tx_packets;
|
|
|
|
local_stats.tx_bytes += sstats->tx_bytes;
|
|
|
|
local_stats.tx_errors += sstats->tx_errors;
|
|
|
|
local_stats.tx_dropped += sstats->tx_dropped;
|
|
|
|
|
|
|
|
local_stats.multicast += sstats->multicast;
|
|
|
|
local_stats.collisions += sstats->collisions;
|
|
|
|
|
|
|
|
local_stats.rx_length_errors += sstats->rx_length_errors;
|
|
|
|
local_stats.rx_over_errors += sstats->rx_over_errors;
|
|
|
|
local_stats.rx_crc_errors += sstats->rx_crc_errors;
|
|
|
|
local_stats.rx_frame_errors += sstats->rx_frame_errors;
|
|
|
|
local_stats.rx_fifo_errors += sstats->rx_fifo_errors;
|
|
|
|
local_stats.rx_missed_errors += sstats->rx_missed_errors;
|
|
|
|
|
|
|
|
local_stats.tx_aborted_errors += sstats->tx_aborted_errors;
|
|
|
|
local_stats.tx_carrier_errors += sstats->tx_carrier_errors;
|
|
|
|
local_stats.tx_fifo_errors += sstats->tx_fifo_errors;
|
|
|
|
local_stats.tx_heartbeat_errors += sstats->tx_heartbeat_errors;
|
|
|
|
local_stats.tx_window_errors += sstats->tx_window_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(stats, &local_stats, sizeof(struct net_device_stats));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
read_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
|
|
|
|
{
|
|
|
|
struct net_device *slave_dev = NULL;
|
|
|
|
struct ifbond k_binfo;
|
|
|
|
struct ifbond __user *u_binfo = NULL;
|
|
|
|
struct ifslave k_sinfo;
|
|
|
|
struct ifslave __user *u_sinfo = NULL;
|
|
|
|
struct mii_ioctl_data *mii = NULL;
|
|
|
|
int res = 0;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond_ioctl: master=%s, cmd=%d\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_dev->name, cmd);
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCGMIIPHY:
|
|
|
|
mii = if_mii(ifr);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!mii)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
mii->phy_id = 0;
|
|
|
|
/* Fall Through */
|
|
|
|
case SIOCGMIIREG:
|
|
|
|
/*
|
|
|
|
* We do this again just in case we were called by SIOCGMIIREG
|
|
|
|
* instead of SIOCGMIIPHY.
|
|
|
|
*/
|
|
|
|
mii = if_mii(ifr);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!mii)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (mii->reg_num == 1) {
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
mii->val_out = 0;
|
2007-10-18 00:37:50 +00:00
|
|
|
read_lock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
read_lock(&bond->curr_slave_lock);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (netif_carrier_ok(bond->dev))
|
2005-04-16 22:20:36 +00:00
|
|
|
mii->val_out = BMSR_LSTATUS;
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
read_unlock(&bond->curr_slave_lock);
|
2007-10-18 00:37:50 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
case BOND_INFO_QUERY_OLD:
|
|
|
|
case SIOCBONDINFOQUERY:
|
|
|
|
u_binfo = (struct ifbond __user *)ifr->ifr_data;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (copy_from_user(&k_binfo, u_binfo, sizeof(ifbond)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
res = bond_info_query(bond_dev, &k_binfo);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res == 0 &&
|
|
|
|
copy_to_user(u_binfo, &k_binfo, sizeof(ifbond)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
case BOND_SLAVE_INFO_QUERY_OLD:
|
|
|
|
case SIOCBONDSLAVEINFOQUERY:
|
|
|
|
u_sinfo = (struct ifslave __user *)ifr->ifr_data;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (copy_from_user(&k_sinfo, u_sinfo, sizeof(ifslave)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
res = bond_slave_info_query(bond_dev, &k_sinfo);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res == 0 &&
|
|
|
|
copy_to_user(u_sinfo, &k_sinfo, sizeof(ifslave)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
default:
|
|
|
|
/* Go on */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EPERM;
|
|
|
|
|
2007-09-17 18:56:21 +00:00
|
|
|
slave_dev = dev_get_by_name(&init_net, ifr->ifr_slave);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("slave_dev=%p: \n", slave_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!slave_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
res = -ENODEV;
|
2009-06-12 19:02:48 +00:00
|
|
|
else {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("slave_dev->name=%s: \n", slave_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
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:
|
|
|
|
res = bond_ioctl_change_active(bond_dev, slave_dev);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
res = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_put(slave_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_set_multicast_list(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct dev_mc_list *dmi;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do promisc before checking multicast_mode
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC))
|
2008-07-15 03:51:36 +00:00
|
|
|
/*
|
|
|
|
* FIXME: Need to handle the error when one of the multi-slaves
|
|
|
|
* encounters error.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_set_promiscuity(bond, 1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
|
|
|
|
if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_set_promiscuity(bond, -1);
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* set allmulti flag to slaves */
|
2009-06-12 19:02:48 +00:00
|
|
|
if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI))
|
2008-07-15 03:51:36 +00:00
|
|
|
/*
|
|
|
|
* FIXME: Need to handle the error when one of the multi-slaves
|
|
|
|
* encounters error.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_set_allmulti(bond, 1);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
|
|
|
|
if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_set_allmulti(bond, -1);
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-30 02:07:44 +00:00
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
bond->flags = bond_dev->flags;
|
|
|
|
|
|
|
|
/* looking for addresses to add to slaves' mc list */
|
|
|
|
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!bond_mc_list_find_dmi(dmi, bond->mc_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
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) {
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!bond_mc_list_find_dmi(dmi, bond_dev->mc_list))
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_mc_delete(bond, dmi->dmi_addr, dmi->dmi_addrlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save master's multicast list */
|
|
|
|
bond_mc_list_destroy(bond);
|
|
|
|
bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC);
|
|
|
|
|
2008-01-30 02:07:44 +00:00
|
|
|
read_unlock(&bond->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-21 04:14:53 +00:00
|
|
|
static int bond_neigh_setup(struct net_device *dev, struct neigh_parms *parms)
|
|
|
|
{
|
|
|
|
struct bonding *bond = netdev_priv(dev);
|
|
|
|
struct slave *slave = bond->first_slave;
|
|
|
|
|
|
|
|
if (slave) {
|
|
|
|
const struct net_device_ops *slave_ops
|
|
|
|
= slave->dev->netdev_ops;
|
|
|
|
if (slave_ops->ndo_neigh_setup)
|
2009-03-05 09:57:44 +00:00
|
|
|
return slave_ops->ndo_neigh_setup(slave->dev, parms);
|
2008-11-21 04:14:53 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *stop_at;
|
|
|
|
int res = 0;
|
|
|
|
int i;
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond,
|
2005-04-16 22:20:36 +00:00
|
|
|
(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) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("s %p s->p %p c_m %p\n", slave,
|
2009-01-06 18:41:56 +00:00
|
|
|
slave->prev, slave->dev->netdev_ops->ndo_change_mtu);
|
2005-11-09 18:36:50 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
res = dev_set_mtu(slave->dev, new_mtu);
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
/* If we failed to set the slave's mtu to the new value
|
|
|
|
* we must abort the operation even in ACTIVE_BACKUP
|
|
|
|
* mode, because if we allow the backup slaves to have
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("err %d %s\n", res, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto unwind;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bond_dev->mtu = new_mtu;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
tmp_res = dev_set_mtu(slave->dev, bond_dev->mtu);
|
|
|
|
if (tmp_res) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("unwind err %d dev %s\n", tmp_res,
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change HW address
|
|
|
|
*
|
|
|
|
* Note that many devices must be down to change the HW address, and
|
|
|
|
* downing the master releases all slaves. We can make bonds full of
|
|
|
|
* bonding devices to test this, however.
|
|
|
|
*/
|
|
|
|
static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct sockaddr *sa = addr, tmp_sa;
|
|
|
|
struct slave *slave, *stop_at;
|
|
|
|
int res = 0;
|
|
|
|
int i;
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (bond->params.mode == BOND_MODE_ALB)
|
|
|
|
return bond_alb_set_mac_address(bond_dev, addr);
|
|
|
|
|
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("bond=%p, name=%s\n", bond, (bond_dev ? bond_dev->name : "None"));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-10 02:57:24 +00:00
|
|
|
/*
|
2008-05-18 04:10:14 +00:00
|
|
|
* If fail_over_mac is set to active, do nothing and return
|
|
|
|
* success. Returning an error causes ifenslave to fail.
|
2007-10-10 02:57:24 +00:00
|
|
|
*/
|
2008-05-18 04:10:14 +00:00
|
|
|
if (bond->params.fail_over_mac == BOND_FOM_ACTIVE)
|
2007-10-10 02:57:24 +00:00
|
|
|
return 0;
|
2007-10-10 02:43:39 +00:00
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!is_valid_ether_addr(sa->sa_data))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
|
/* 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 hw address 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) {
|
2008-11-20 05:56:05 +00:00
|
|
|
const struct net_device_ops *slave_ops = slave->dev->netdev_ops;
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("slave %p %s\n", slave, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
if (slave_ops->ndo_set_mac_address == NULL) {
|
2005-04-16 22:20:36 +00:00
|
|
|
res = -EOPNOTSUPP;
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("EOPNOTSUPP %s\n", slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto unwind;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = dev_set_mac_address(slave->dev, addr);
|
|
|
|
if (res) {
|
|
|
|
/* TODO: consider downing the slave
|
|
|
|
* and retry ?
|
|
|
|
* User should expect communications
|
|
|
|
* breakage anyway until ARP finish
|
|
|
|
* updating, so...
|
|
|
|
*/
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("err %d %s\n", res, slave->dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto unwind;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
memcpy(bond_dev->dev_addr, sa->sa_data, bond_dev->addr_len);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
unwind:
|
|
|
|
memcpy(tmp_sa.sa_data, bond_dev->dev_addr, bond_dev->addr_len);
|
|
|
|
tmp_sa.sa_family = bond_dev->type;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
tmp_res = dev_set_mac_address(slave->dev, &tmp_sa);
|
|
|
|
if (tmp_res) {
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("unwind err %d dev %s\n", tmp_res,
|
2005-04-16 22:20:36 +00:00
|
|
|
slave->dev->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *start_at;
|
2007-10-18 00:37:47 +00:00
|
|
|
int i, slave_no, res = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!BOND_IS_OK(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
2007-10-18 00:37:47 +00:00
|
|
|
/*
|
|
|
|
* Concurrent TX may collide on rr_tx_counter; we accept that
|
|
|
|
* as being rare enough not to justify using an atomic op here
|
|
|
|
*/
|
|
|
|
slave_no = bond->rr_tx_counter++ % bond->slave_cnt;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-18 00:37:47 +00:00
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
slave_no--;
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave_no < 0)
|
2007-10-18 00:37:47 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-10-18 00:37:47 +00:00
|
|
|
start_at = slave;
|
2005-04-16 22:20:36 +00:00
|
|
|
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)) {
|
|
|
|
res = bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (res) {
|
|
|
|
/* no suitable interface, frame not sent */
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-28 21:50:53 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* in active-backup mode, we know that bond->curr_active_slave is always valid if
|
|
|
|
* the bond has a usable interface.
|
|
|
|
*/
|
|
|
|
static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
int res = 1;
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!BOND_IS_OK(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
2005-09-28 21:50:53 +00:00
|
|
|
if (!bond->curr_active_slave)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
out:
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res)
|
2005-04-16 22:20:36 +00:00
|
|
|
/* no suitable interface, frame not sent */
|
|
|
|
dev_kfree_skb(skb);
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-26 21:54:11 +00:00
|
|
|
* In bond_xmit_xor() , we determine the output device by using a pre-
|
|
|
|
* determined xmit_hash_policy(), If the selected device is not enabled,
|
|
|
|
* find the next active slave.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *start_at;
|
|
|
|
int slave_no;
|
|
|
|
int i;
|
|
|
|
int res = 1;
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!BOND_IS_OK(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
2005-06-26 21:54:11 +00:00
|
|
|
slave_no = bond->xmit_hash_policy(skb, bond_dev, bond->slave_cnt);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_for_each_slave(bond, slave, i) {
|
|
|
|
slave_no--;
|
2009-06-12 19:02:48 +00:00
|
|
|
if (slave_no < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_at = slave;
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
res = bond_dev_queue_xmit(bond, skb, slave->dev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (res) {
|
|
|
|
/* no suitable interface, frame not sent */
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* in broadcast mode, we send everything to all usable interfaces.
|
|
|
|
*/
|
|
|
|
static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct slave *slave, *start_at;
|
|
|
|
struct net_device *tx_dev = NULL;
|
|
|
|
int i;
|
|
|
|
int res = 1;
|
|
|
|
|
|
|
|
read_lock(&bond->lock);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!BOND_IS_OK(bond))
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
|
|
start_at = bond->curr_active_slave;
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (!start_at)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
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) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: bond_xmit_broadcast(): "
|
|
|
|
"skb_clone() failed\n",
|
|
|
|
bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = bond_dev_queue_xmit(bond, skb2, tx_dev);
|
|
|
|
if (res) {
|
|
|
|
dev_kfree_skb(skb2);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tx_dev = slave->dev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
if (tx_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
res = bond_dev_queue_xmit(bond, skb, tx_dev);
|
|
|
|
|
|
|
|
out:
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res)
|
2005-04-16 22:20:36 +00:00
|
|
|
/* no suitable interface, frame not sent */
|
|
|
|
dev_kfree_skb(skb);
|
2009-06-12 19:02:48 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* frame sent to all suitable interfaces */
|
|
|
|
read_unlock(&bond->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------- Device initialization ---------------------------*/
|
|
|
|
|
2007-12-07 07:40:34 +00:00
|
|
|
static void bond_set_xmit_hash_policy(struct bonding *bond)
|
|
|
|
{
|
|
|
|
switch (bond->params.xmit_policy) {
|
|
|
|
case BOND_XMIT_POLICY_LAYER23:
|
|
|
|
bond->xmit_hash_policy = bond_xmit_hash_policy_l23;
|
|
|
|
break;
|
|
|
|
case BOND_XMIT_POLICY_LAYER34:
|
|
|
|
bond->xmit_hash_policy = bond_xmit_hash_policy_l34;
|
|
|
|
break;
|
|
|
|
case BOND_XMIT_POLICY_LAYER2:
|
|
|
|
default:
|
|
|
|
bond->xmit_hash_policy = bond_xmit_hash_policy_l2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-21 04:14:53 +00:00
|
|
|
static int bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
const struct bonding *bond = netdev_priv(dev);
|
|
|
|
|
|
|
|
switch (bond->params.mode) {
|
|
|
|
case BOND_MODE_ROUNDROBIN:
|
|
|
|
return bond_xmit_roundrobin(skb, dev);
|
|
|
|
case BOND_MODE_ACTIVEBACKUP:
|
|
|
|
return bond_xmit_activebackup(skb, dev);
|
|
|
|
case BOND_MODE_XOR:
|
|
|
|
return bond_xmit_xor(skb, dev);
|
|
|
|
case BOND_MODE_BROADCAST:
|
|
|
|
return bond_xmit_broadcast(skb, dev);
|
|
|
|
case BOND_MODE_8023AD:
|
|
|
|
return bond_3ad_xmit_xor(skb, dev);
|
|
|
|
case BOND_MODE_ALB:
|
|
|
|
case BOND_MODE_TLB:
|
|
|
|
return bond_alb_xmit(skb, dev);
|
|
|
|
default:
|
|
|
|
/* Should never happen, mode already checked */
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": %s: Error: Unknown bonding mode %d\n",
|
2008-11-21 04:14:53 +00:00
|
|
|
dev->name, bond->params.mode);
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* set bond mode specific net device operations
|
|
|
|
*/
|
2005-11-09 18:35:51 +00:00
|
|
|
void bond_set_mode_ops(struct bonding *bond, int mode)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-06-26 21:54:11 +00:00
|
|
|
struct net_device *bond_dev = bond->dev;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (mode) {
|
|
|
|
case BOND_MODE_ROUNDROBIN:
|
|
|
|
break;
|
|
|
|
case BOND_MODE_ACTIVEBACKUP:
|
|
|
|
break;
|
|
|
|
case BOND_MODE_XOR:
|
2007-12-07 07:40:34 +00:00
|
|
|
bond_set_xmit_hash_policy(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BOND_MODE_BROADCAST:
|
|
|
|
break;
|
|
|
|
case BOND_MODE_8023AD:
|
2006-02-22 00:36:44 +00:00
|
|
|
bond_set_master_3ad_flags(bond);
|
2007-12-07 07:40:34 +00:00
|
|
|
bond_set_xmit_hash_policy(bond);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BOND_MODE_ALB:
|
2006-02-22 00:36:44 +00:00
|
|
|
bond_set_master_alb_flags(bond);
|
|
|
|
/* FALLTHRU */
|
|
|
|
case BOND_MODE_TLB:
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Should never happen, mode already checked */
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-11-09 18:34:57 +00:00
|
|
|
": %s: Error: Unknown bonding mode %d\n",
|
|
|
|
bond_dev->name,
|
2005-04-16 22:20:36 +00:00
|
|
|
mode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-26 23:11:50 +00:00
|
|
|
static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
|
|
|
|
struct ethtool_drvinfo *drvinfo)
|
|
|
|
{
|
|
|
|
strncpy(drvinfo->driver, DRV_NAME, 32);
|
|
|
|
strncpy(drvinfo->version, DRV_VERSION, 32);
|
|
|
|
snprintf(drvinfo->fw_version, 32, "%d", BOND_ABI_VERSION);
|
|
|
|
}
|
|
|
|
|
2006-09-13 18:30:00 +00:00
|
|
|
static const struct ethtool_ops bond_ethtool_ops = {
|
2005-09-26 23:11:50 +00:00
|
|
|
.get_drvinfo = bond_ethtool_get_drvinfo,
|
2008-09-14 01:17:09 +00:00
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
.get_tx_csum = ethtool_op_get_tx_csum,
|
|
|
|
.get_sg = ethtool_op_get_sg,
|
|
|
|
.get_tso = ethtool_op_get_tso,
|
|
|
|
.get_ufo = ethtool_op_get_ufo,
|
|
|
|
.get_flags = ethtool_op_get_flags,
|
2005-08-23 05:34:53 +00:00
|
|
|
};
|
|
|
|
|
2008-11-20 05:56:05 +00:00
|
|
|
static const struct net_device_ops bond_netdev_ops = {
|
2009-06-12 19:02:47 +00:00
|
|
|
.ndo_uninit = bond_uninit,
|
2008-11-20 05:56:05 +00:00
|
|
|
.ndo_open = bond_open,
|
|
|
|
.ndo_stop = bond_close,
|
2008-11-21 04:14:53 +00:00
|
|
|
.ndo_start_xmit = bond_start_xmit,
|
2008-11-20 05:56:05 +00:00
|
|
|
.ndo_get_stats = bond_get_stats,
|
|
|
|
.ndo_do_ioctl = bond_do_ioctl,
|
|
|
|
.ndo_set_multicast_list = bond_set_multicast_list,
|
|
|
|
.ndo_change_mtu = bond_change_mtu,
|
|
|
|
.ndo_set_mac_address = bond_set_mac_address,
|
2008-11-21 04:14:53 +00:00
|
|
|
.ndo_neigh_setup = bond_neigh_setup,
|
2008-11-20 05:56:05 +00:00
|
|
|
.ndo_vlan_rx_register = bond_vlan_rx_register,
|
|
|
|
.ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
|
|
|
|
.ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid,
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Does not allocate but creates a /proc entry.
|
|
|
|
* Allowed to fail.
|
|
|
|
*/
|
2009-06-12 19:02:44 +00:00
|
|
|
static int bond_init(struct net_device *bond_dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 07:09:22 +00:00
|
|
|
pr_debug("Begin bond_init for %s\n", bond_dev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* initialize rwlocks */
|
|
|
|
rwlock_init(&bond->lock);
|
|
|
|
rwlock_init(&bond->curr_slave_lock);
|
|
|
|
|
2009-06-12 19:02:44 +00:00
|
|
|
bond->params = bonding_defaults;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-18 00:37:45 +00:00
|
|
|
bond->wq = create_singlethread_workqueue(bond_dev->name);
|
|
|
|
if (!bond->wq)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Initialize pointers */
|
|
|
|
bond->first_slave = NULL;
|
|
|
|
bond->curr_active_slave = NULL;
|
|
|
|
bond->current_arp_slave = NULL;
|
|
|
|
bond->primary_slave = NULL;
|
|
|
|
bond->dev = bond_dev;
|
2007-10-10 02:43:42 +00:00
|
|
|
bond->send_grat_arp = 0;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
bond->send_unsol_na = 0;
|
2007-10-10 02:43:43 +00:00
|
|
|
bond->setup_by_slave = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
INIT_LIST_HEAD(&bond->vlan_list);
|
|
|
|
|
|
|
|
/* Initialize the device entry points */
|
2008-11-20 05:56:05 +00:00
|
|
|
bond_dev->netdev_ops = &bond_netdev_ops;
|
2005-08-23 05:34:53 +00:00
|
|
|
bond_dev->ethtool_ops = &bond_ethtool_ops;
|
2005-06-26 21:54:11 +00:00
|
|
|
bond_set_mode_ops(bond, bond->params.mode);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-06-12 19:02:47 +00:00
|
|
|
bond_dev->destructor = free_netdev;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Initialize the device options */
|
|
|
|
bond_dev->tx_queue_len = 0;
|
|
|
|
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
|
2006-09-23 04:54:10 +00:00
|
|
|
bond_dev->priv_flags |= IFF_BONDING;
|
2008-11-04 02:16:50 +00:00
|
|
|
if (bond->params.arp_interval)
|
|
|
|
bond_dev->priv_flags |= IFF_MASTER_ARPMON;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* At first, we block adding VLANs. That's the only way to
|
|
|
|
* prevent problems that occur when adding VLANs over an
|
|
|
|
* empty bond. The block will be removed once non-challenged
|
|
|
|
* slaves are enslaved.
|
|
|
|
*/
|
|
|
|
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
|
|
|
|
|
2006-06-09 19:20:56 +00:00
|
|
|
/* don't acquire bond device's netif_tx_lock when
|
2005-04-16 22:20:36 +00:00
|
|
|
* transmitting */
|
|
|
|
bond_dev->features |= NETIF_F_LLTX;
|
|
|
|
|
|
|
|
/* By default, we declare the bond to be fully
|
|
|
|
* VLAN hardware accelerated capable. Special
|
|
|
|
* care is taken in the various xmit functions
|
|
|
|
* when there are slaves that are not hw accel
|
|
|
|
* capable
|
|
|
|
*/
|
|
|
|
bond_dev->features |= (NETIF_F_HW_VLAN_TX |
|
|
|
|
NETIF_F_HW_VLAN_RX |
|
|
|
|
NETIF_F_HW_VLAN_FILTER);
|
|
|
|
|
|
|
|
bond_create_proc_entry(bond);
|
|
|
|
list_add_tail(&bond->bond_list, &bond_dev_list);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-07 07:40:35 +00:00
|
|
|
static void bond_work_cancel_all(struct bonding *bond)
|
|
|
|
{
|
|
|
|
write_lock_bh(&bond->lock);
|
|
|
|
bond->kill_timers = 1;
|
|
|
|
write_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
if (bond->params.miimon && delayed_work_pending(&bond->mii_work))
|
|
|
|
cancel_delayed_work(&bond->mii_work);
|
|
|
|
|
|
|
|
if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work))
|
|
|
|
cancel_delayed_work(&bond->arp_work);
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_ALB &&
|
|
|
|
delayed_work_pending(&bond->alb_work))
|
|
|
|
cancel_delayed_work(&bond->alb_work);
|
|
|
|
|
|
|
|
if (bond->params.mode == BOND_MODE_8023AD &&
|
|
|
|
delayed_work_pending(&bond->ad_work))
|
|
|
|
cancel_delayed_work(&bond->ad_work);
|
|
|
|
}
|
|
|
|
|
2008-10-31 00:41:15 +00:00
|
|
|
/* De-initialize device specific data.
|
|
|
|
* Caller must hold rtnl_lock.
|
|
|
|
*/
|
|
|
|
static void bond_deinit(struct net_device *bond_dev)
|
|
|
|
{
|
2008-11-13 07:37:49 +00:00
|
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
2008-10-31 00:41:15 +00:00
|
|
|
|
|
|
|
list_del(&bond->bond_list);
|
|
|
|
|
|
|
|
bond_work_cancel_all(bond);
|
|
|
|
|
|
|
|
bond_remove_proc_entry(bond);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* 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;
|
|
|
|
|
2007-12-07 07:40:35 +00:00
|
|
|
bond_work_cancel_all(bond);
|
2006-09-23 04:54:37 +00:00
|
|
|
/* Release the bonded slaves */
|
|
|
|
bond_release_all(bond_dev);
|
2009-06-12 19:02:47 +00:00
|
|
|
unregister_netdevice(bond_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bond_destroy_proc_dir();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------- Module initialization ---------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert string input module parms. Accept either the
|
2008-01-18 00:25:01 +00:00
|
|
|
* number of the mode or its string name. A bit complicated because
|
|
|
|
* some mode names are substrings of other names, and calls from sysfs
|
|
|
|
* may have whitespace in the name (trailing newlines, for example).
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-12-10 07:10:17 +00:00
|
|
|
int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-02-14 11:15:49 +00:00
|
|
|
int modeint = -1, i, rv;
|
2008-01-30 02:07:43 +00:00
|
|
|
char *p, modestr[BOND_MAX_MODENAME_LEN + 1] = { 0, };
|
2008-01-18 00:25:01 +00:00
|
|
|
|
2008-01-30 02:07:43 +00:00
|
|
|
for (p = (char *)buf; *p; p++)
|
|
|
|
if (!(isdigit(*p) || isspace(*p)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (*p)
|
2008-01-18 00:25:01 +00:00
|
|
|
rv = sscanf(buf, "%20s", modestr);
|
2008-01-30 02:07:43 +00:00
|
|
|
else
|
2009-02-14 11:15:49 +00:00
|
|
|
rv = sscanf(buf, "%d", &modeint);
|
2008-01-30 02:07:43 +00:00
|
|
|
|
|
|
|
if (!rv)
|
|
|
|
return -1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
for (i = 0; tbl[i].modename; i++) {
|
2009-02-14 11:15:49 +00:00
|
|
|
if (modeint == tbl[i].mode)
|
2008-01-18 00:25:01 +00:00
|
|
|
return tbl[i].mode;
|
|
|
|
if (strcmp(modestr, tbl[i].modename) == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return tbl[i].mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bond_check_params(struct bond_params *params)
|
|
|
|
{
|
2008-05-18 04:10:14 +00:00
|
|
|
int arp_validate_value, fail_over_mac_value;
|
2006-09-23 04:54:53 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Convert string parameters.
|
|
|
|
*/
|
|
|
|
if (mode) {
|
|
|
|
bond_mode = bond_parse_parm(mode, bond_mode_tbl);
|
|
|
|
if (bond_mode == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Error: Invalid bonding mode \"%s\"\n",
|
|
|
|
mode == NULL ? "NULL" : mode);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-26 21:54:11 +00:00
|
|
|
if (xmit_hash_policy) {
|
|
|
|
if ((bond_mode != BOND_MODE_XOR) &&
|
|
|
|
(bond_mode != BOND_MODE_8023AD)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-06-26 21:54:11 +00:00
|
|
|
": xor_mode param is irrelevant in mode %s\n",
|
|
|
|
bond_mode_name(bond_mode));
|
|
|
|
} else {
|
|
|
|
xmit_hashtype = bond_parse_parm(xmit_hash_policy,
|
|
|
|
xmit_hashtype_tbl);
|
|
|
|
if (xmit_hashtype == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
|
|
|
": Error: Invalid xmit_hash_policy \"%s\"\n",
|
|
|
|
xmit_hash_policy == NULL ? "NULL" :
|
2005-06-26 21:54:11 +00:00
|
|
|
xmit_hash_policy);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (lacp_rate) {
|
|
|
|
if (bond_mode != BOND_MODE_8023AD) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": lacp_rate param is irrelevant in mode %s\n",
|
|
|
|
bond_mode_name(bond_mode));
|
|
|
|
} else {
|
|
|
|
lacp_fast = bond_parse_parm(lacp_rate, bond_lacp_tbl);
|
|
|
|
if (lacp_fast == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Error: Invalid lacp rate \"%s\"\n",
|
|
|
|
lacp_rate == NULL ? "NULL" : lacp_rate);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-05 01:51:16 +00:00
|
|
|
if (ad_select) {
|
|
|
|
params->ad_select = bond_parse_parm(ad_select, ad_select_tbl);
|
|
|
|
if (params->ad_select == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-11-05 01:51:16 +00:00
|
|
|
": Error: Invalid ad_select \"%s\"\n",
|
|
|
|
ad_select == NULL ? "NULL" : ad_select);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bond_mode != BOND_MODE_8023AD) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-11-05 01:51:16 +00:00
|
|
|
": ad_select param only affects 802.3ad mode\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
params->ad_select = BOND_AD_STABLE;
|
|
|
|
}
|
|
|
|
|
2008-06-14 01:12:04 +00:00
|
|
|
if (max_bonds < 0 || max_bonds > INT_MAX) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: max_bonds (%d) not in range %d-%d, so it "
|
2005-11-09 18:34:57 +00:00
|
|
|
"was reset to BOND_DEFAULT_MAX_BONDS (%d)\n",
|
2008-06-14 01:12:04 +00:00
|
|
|
max_bonds, 0, INT_MAX, BOND_DEFAULT_MAX_BONDS);
|
2005-04-16 22:20:36 +00:00
|
|
|
max_bonds = BOND_DEFAULT_MAX_BONDS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (miimon < 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: miimon module parameter (%d), "
|
|
|
|
"not in range 0-%d, so it was reset to %d\n",
|
|
|
|
miimon, INT_MAX, BOND_LINK_MON_INTERV);
|
|
|
|
miimon = BOND_LINK_MON_INTERV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (updelay < 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: updelay module parameter (%d), "
|
|
|
|
"not in range 0-%d, so it was reset to 0\n",
|
|
|
|
updelay, INT_MAX);
|
|
|
|
updelay = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (downdelay < 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: downdelay module parameter (%d), "
|
|
|
|
"not in range 0-%d, so it was reset to 0\n",
|
|
|
|
downdelay, INT_MAX);
|
|
|
|
downdelay = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((use_carrier != 0) && (use_carrier != 1)) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: use_carrier module parameter (%d), "
|
|
|
|
"not of valid value (0/1), so it was set to 1\n",
|
|
|
|
use_carrier);
|
|
|
|
use_carrier = 1;
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:12 +00:00
|
|
|
if (num_grat_arp < 0 || num_grat_arp > 255) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-05-18 04:10:12 +00:00
|
|
|
": Warning: num_grat_arp (%d) not in range 0-255 so it "
|
|
|
|
"was reset to 1 \n", num_grat_arp);
|
|
|
|
num_grat_arp = 1;
|
|
|
|
}
|
|
|
|
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
if (num_unsol_na < 0 || num_unsol_na > 255) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
": Warning: num_unsol_na (%d) not in range 0-255 so it "
|
|
|
|
"was reset to 1 \n", num_unsol_na);
|
|
|
|
num_unsol_na = 1;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* reset values for 802.3ad */
|
|
|
|
if (bond_mode == BOND_MODE_8023AD) {
|
|
|
|
if (!miimon) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: miimon must be specified, "
|
|
|
|
"otherwise bonding will not detect link "
|
|
|
|
"failure, speed and duplex which are "
|
|
|
|
"essential for 802.3ad operation\n");
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning("Forcing miimon to 100msec\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
miimon = 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset values for TLB/ALB */
|
|
|
|
if ((bond_mode == BOND_MODE_TLB) ||
|
|
|
|
(bond_mode == BOND_MODE_ALB)) {
|
|
|
|
if (!miimon) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: miimon must be specified, "
|
|
|
|
"otherwise bonding will not detect link "
|
|
|
|
"failure and link speed which are essential "
|
|
|
|
"for TLB/ALB load balancing\n");
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning("Forcing miimon to 100msec\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
miimon = 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bond_mode == BOND_MODE_ALB) {
|
|
|
|
printk(KERN_NOTICE DRV_NAME
|
|
|
|
": In ALB mode you might experience client "
|
|
|
|
"disconnections upon reconnection of a link if the "
|
|
|
|
"bonding module updelay parameter (%d msec) is "
|
|
|
|
"incompatible with the forwarding delay time of the "
|
|
|
|
"switch\n",
|
|
|
|
updelay);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!miimon) {
|
|
|
|
if (updelay || downdelay) {
|
|
|
|
/* just warn the user the up/down delay will have
|
|
|
|
* no effect since miimon is zero...
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: miimon module parameter not set "
|
|
|
|
"and updelay (%d) or downdelay (%d) module "
|
|
|
|
"parameter is set; updelay and downdelay have "
|
|
|
|
"no effect unless miimon is set\n",
|
|
|
|
updelay, downdelay);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* don't allow arp monitoring */
|
|
|
|
if (arp_interval) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: miimon (%d) and arp_interval (%d) "
|
|
|
|
"can't be used simultaneously, disabling ARP "
|
|
|
|
"monitoring\n",
|
|
|
|
miimon, arp_interval);
|
|
|
|
arp_interval = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((updelay % miimon) != 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: updelay (%d) is not a multiple "
|
|
|
|
"of miimon (%d), updelay rounded to %d ms\n",
|
|
|
|
updelay, miimon, (updelay / miimon) * miimon);
|
|
|
|
}
|
|
|
|
|
|
|
|
updelay /= miimon;
|
|
|
|
|
|
|
|
if ((downdelay % miimon) != 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: downdelay (%d) is not a multiple "
|
|
|
|
"of miimon (%d), downdelay rounded to %d ms\n",
|
|
|
|
downdelay, miimon,
|
|
|
|
(downdelay / miimon) * miimon);
|
|
|
|
}
|
|
|
|
|
|
|
|
downdelay /= miimon;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arp_interval < 0) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: arp_interval module parameter (%d) "
|
|
|
|
", not in range 0-%d, so it was reset to %d\n",
|
|
|
|
arp_interval, INT_MAX, BOND_LINK_ARP_INTERV);
|
|
|
|
arp_interval = BOND_LINK_ARP_INTERV;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (arp_ip_count = 0;
|
|
|
|
(arp_ip_count < BOND_MAX_ARP_TARGETS) && arp_ip_target[arp_ip_count];
|
|
|
|
arp_ip_count++) {
|
|
|
|
/* not complete check, but should be good enough to
|
|
|
|
catch mistakes */
|
|
|
|
if (!isdigit(arp_ip_target[arp_ip_count][0])) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: bad arp_ip_target module parameter "
|
|
|
|
"(%s), ARP monitoring will not be performed\n",
|
|
|
|
arp_ip_target[arp_ip_count]);
|
|
|
|
arp_interval = 0;
|
|
|
|
} else {
|
2007-08-23 00:06:58 +00:00
|
|
|
__be32 ip = in_aton(arp_ip_target[arp_ip_count]);
|
2005-04-16 22:20:36 +00:00
|
|
|
arp_target[arp_ip_count] = ip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arp_interval && !arp_ip_count) {
|
|
|
|
/* don't allow arping if no arp_ip_target given... */
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: arp_interval module parameter (%d) "
|
|
|
|
"specified without providing an arp_ip_target "
|
|
|
|
"parameter, arp_interval was reset to 0\n",
|
|
|
|
arp_interval);
|
|
|
|
arp_interval = 0;
|
|
|
|
}
|
|
|
|
|
2006-09-23 04:54:53 +00:00
|
|
|
if (arp_validate) {
|
|
|
|
if (bond_mode != BOND_MODE_ACTIVEBACKUP) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
|
|
|
": arp_validate only supported in active-backup mode\n");
|
2006-09-23 04:54:53 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (!arp_interval) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2006-09-23 04:54:53 +00:00
|
|
|
": arp_validate requires arp_interval\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arp_validate_value = bond_parse_parm(arp_validate,
|
|
|
|
arp_validate_tbl);
|
|
|
|
if (arp_validate_value == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2006-09-23 04:54:53 +00:00
|
|
|
": Error: invalid arp_validate \"%s\"\n",
|
|
|
|
arp_validate == NULL ? "NULL" : arp_validate);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
arp_validate_value = 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (miimon) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": MII link monitoring set to %d ms\n",
|
|
|
|
miimon);
|
|
|
|
} else if (arp_interval) {
|
|
|
|
int i;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info(DRV_NAME ": ARP monitoring set to %d ms,"
|
|
|
|
" validate %s, with %d target(s):",
|
2006-09-23 04:54:53 +00:00
|
|
|
arp_interval,
|
|
|
|
arp_validate_tbl[arp_validate_value].modename,
|
|
|
|
arp_ip_count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
for (i = 0; i < arp_ip_count; i++)
|
2009-06-12 19:02:48 +00:00
|
|
|
printk(" %s", arp_ip_target[i]);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
printk("\n");
|
|
|
|
|
2008-06-14 01:12:04 +00:00
|
|
|
} else if (max_bonds) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* miimon and arp_interval not set, we need one so things
|
|
|
|
* work as expected, see bonding.txt for details
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: either miimon or arp_interval and "
|
|
|
|
"arp_ip_target module parameters must be specified, "
|
|
|
|
"otherwise bonding will not detect link failures! see "
|
|
|
|
"bonding.txt for details.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (primary && !USES_PRIMARY(bond_mode)) {
|
|
|
|
/* currently, using a primary only makes sense
|
|
|
|
* in active backup, TLB or ALB modes
|
|
|
|
*/
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2005-04-16 22:20:36 +00:00
|
|
|
": Warning: %s primary device specified but has no "
|
|
|
|
"effect in %s mode\n",
|
|
|
|
primary, bond_mode_name(bond_mode));
|
|
|
|
primary = NULL;
|
|
|
|
}
|
|
|
|
|
2008-05-18 04:10:14 +00:00
|
|
|
if (fail_over_mac) {
|
|
|
|
fail_over_mac_value = bond_parse_parm(fail_over_mac,
|
|
|
|
fail_over_mac_tbl);
|
|
|
|
if (fail_over_mac_value == -1) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": Error: invalid fail_over_mac \"%s\"\n",
|
|
|
|
arp_validate == NULL ? "NULL" : arp_validate);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bond_mode != BOND_MODE_ACTIVEBACKUP)
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_warning(DRV_NAME
|
2008-05-18 04:10:14 +00:00
|
|
|
": Warning: fail_over_mac only affects "
|
|
|
|
"active-backup mode.\n");
|
|
|
|
} else {
|
|
|
|
fail_over_mac_value = BOND_FOM_NONE;
|
|
|
|
}
|
2007-10-10 02:57:24 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* fill params struct with the proper values */
|
|
|
|
params->mode = bond_mode;
|
2005-06-26 21:54:11 +00:00
|
|
|
params->xmit_policy = xmit_hashtype;
|
2005-04-16 22:20:36 +00:00
|
|
|
params->miimon = miimon;
|
2008-05-18 04:10:12 +00:00
|
|
|
params->num_grat_arp = num_grat_arp;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
params->num_unsol_na = num_unsol_na;
|
2005-04-16 22:20:36 +00:00
|
|
|
params->arp_interval = arp_interval;
|
2006-09-23 04:54:53 +00:00
|
|
|
params->arp_validate = arp_validate_value;
|
2005-04-16 22:20:36 +00:00
|
|
|
params->updelay = updelay;
|
|
|
|
params->downdelay = downdelay;
|
|
|
|
params->use_carrier = use_carrier;
|
|
|
|
params->lacp_fast = lacp_fast;
|
|
|
|
params->primary[0] = 0;
|
2008-05-18 04:10:14 +00:00
|
|
|
params->fail_over_mac = fail_over_mac_value;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (primary) {
|
|
|
|
strncpy(params->primary, primary, IFNAMSIZ);
|
|
|
|
params->primary[IFNAMSIZ - 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(params->arp_targets, arp_target, sizeof(arp_target));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-09 03:51:01 +00:00
|
|
|
static struct lock_class_key bonding_netdev_xmit_lock_key;
|
2008-07-22 21:16:42 +00:00
|
|
|
static struct lock_class_key bonding_netdev_addr_lock_key;
|
2006-11-09 03:51:01 +00:00
|
|
|
|
2008-07-17 07:34:19 +00:00
|
|
|
static void bond_set_lockdep_class_one(struct net_device *dev,
|
|
|
|
struct netdev_queue *txq,
|
|
|
|
void *_unused)
|
2008-07-09 06:13:53 +00:00
|
|
|
{
|
|
|
|
lockdep_set_class(&txq->_xmit_lock,
|
|
|
|
&bonding_netdev_xmit_lock_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bond_set_lockdep_class(struct net_device *dev)
|
|
|
|
{
|
2008-07-22 21:16:42 +00:00
|
|
|
lockdep_set_class(&dev->addr_list_lock,
|
|
|
|
&bonding_netdev_addr_lock_key);
|
2008-07-17 07:34:19 +00:00
|
|
|
netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL);
|
2008-07-09 06:13:53 +00:00
|
|
|
}
|
|
|
|
|
2005-11-09 18:36:04 +00:00
|
|
|
/* Create a new bond based on the specified name and bonding parameters.
|
2007-01-20 02:15:31 +00:00
|
|
|
* If name is NULL, obtain a suitable "bond%d" name for us.
|
2005-11-09 18:36:04 +00:00
|
|
|
* Caller must NOT hold rtnl_lock; we need to release it here before we
|
|
|
|
* set up our sysfs entries.
|
|
|
|
*/
|
2009-06-12 19:02:44 +00:00
|
|
|
int bond_create(const char *name)
|
2005-11-09 18:36:04 +00:00
|
|
|
{
|
|
|
|
struct net_device *bond_dev;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
rtnl_lock();
|
2008-01-18 00:25:02 +00:00
|
|
|
/* Check to see if the bond already exists. */
|
2009-06-12 19:02:50 +00:00
|
|
|
/* FIXME: pass netns from caller */
|
|
|
|
if (name && __dev_get_by_name(&init_net, name)) {
|
|
|
|
pr_err(DRV_NAME ": cannot add bond %s; already exists\n",
|
|
|
|
name);
|
|
|
|
res = -EEXIST;
|
|
|
|
goto out_rtnl;
|
2008-01-30 02:07:45 +00:00
|
|
|
}
|
2008-01-18 00:25:02 +00:00
|
|
|
|
2007-01-20 02:15:31 +00:00
|
|
|
bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
|
|
|
|
ether_setup);
|
2005-11-09 18:36:04 +00:00
|
|
|
if (!bond_dev) {
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_err(DRV_NAME ": %s: eek! can't alloc netdev!\n",
|
2005-11-09 18:36:04 +00:00
|
|
|
name);
|
|
|
|
res = -ENOMEM;
|
|
|
|
goto out_rtnl;
|
|
|
|
}
|
|
|
|
|
net: release dst entry in dev_hard_start_xmit()
One point of contention in high network loads is the dst_release() performed
when a transmited skb is freed. This is because NIC tx completion calls
dev_kree_skb() long after original call to dev_queue_xmit(skb).
CPU cache is cold and the atomic op in dst_release() stalls. On SMP, this is
quite visible if one CPU is 100% handling softirqs for a network device,
since dst_clone() is done by other cpus, involving cache line ping pongs.
It seems right place to release dst is in dev_hard_start_xmit(), for most
devices but ones that are virtual, and some exceptions.
David Miller suggested to define a new device flag, set in alloc_netdev_mq()
(so that most devices set it at init time), and carefuly unset in devices
which dont want a NULL skb->dst in their ndo_start_xmit().
List of devices that must clear this flag is :
- loopback device, because it calls netif_rx() and quoting Patrick :
"ip_route_input() doesn't accept loopback addresses, so loopback packets
already need to have a dst_entry attached."
- appletalk/ipddp.c : needs skb->dst in its xmit function
- And all devices that call again dev_queue_xmit() from their xmit function
(as some classifiers need skb->dst) : bonding, vlan, macvlan, eql, ifb, hdlc_fr
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-05-19 05:19:19 +00:00
|
|
|
bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
|
2007-01-20 02:15:31 +00:00
|
|
|
if (!name) {
|
|
|
|
res = dev_alloc_name(bond_dev, "bond%d");
|
|
|
|
if (res < 0)
|
|
|
|
goto out_netdev;
|
|
|
|
}
|
|
|
|
|
2005-11-09 18:36:04 +00:00
|
|
|
/* bond_init() must be called after dev_alloc_name() (for the
|
|
|
|
* /proc files), but before register_netdevice(), because we
|
|
|
|
* need to set function pointers.
|
|
|
|
*/
|
|
|
|
|
2009-06-12 19:02:44 +00:00
|
|
|
res = bond_init(bond_dev);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res < 0)
|
2005-11-09 18:36:04 +00:00
|
|
|
goto out_netdev;
|
|
|
|
|
2009-06-12 19:02:45 +00:00
|
|
|
bond_set_lockdep_class(bond_dev);
|
|
|
|
|
|
|
|
netif_carrier_off(bond_dev);
|
|
|
|
|
2005-11-09 18:36:04 +00:00
|
|
|
res = register_netdevice(bond_dev);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res < 0)
|
2005-11-09 18:36:04 +00:00
|
|
|
goto out_bond;
|
2006-11-09 03:51:01 +00:00
|
|
|
|
2008-11-13 07:37:49 +00:00
|
|
|
res = bond_create_sysfs_entry(netdev_priv(bond_dev));
|
2009-05-01 22:35:28 +00:00
|
|
|
if (res < 0)
|
|
|
|
goto out_unreg;
|
2007-01-20 02:15:38 +00:00
|
|
|
|
2009-06-12 19:02:46 +00:00
|
|
|
rtnl_unlock();
|
2007-01-20 02:15:38 +00:00
|
|
|
return 0;
|
|
|
|
|
2009-05-01 22:35:28 +00:00
|
|
|
out_unreg:
|
|
|
|
unregister_netdevice(bond_dev);
|
2005-11-09 18:36:04 +00:00
|
|
|
out_bond:
|
|
|
|
bond_deinit(bond_dev);
|
|
|
|
out_netdev:
|
|
|
|
free_netdev(bond_dev);
|
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static int __init bonding_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int res;
|
|
|
|
|
2009-06-12 19:02:48 +00:00
|
|
|
pr_info("%s", version);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 18:36:04 +00:00
|
|
|
res = bond_check_params(&bonding_defaults);
|
2009-06-12 19:02:48 +00:00
|
|
|
if (res)
|
2005-11-09 18:36:04 +00:00
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
bond_create_proc_dir();
|
2008-01-18 00:25:02 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
for (i = 0; i < max_bonds; i++) {
|
2009-06-12 19:02:44 +00:00
|
|
|
res = bond_create(NULL);
|
2005-11-09 18:36:04 +00:00
|
|
|
if (res)
|
|
|
|
goto err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-09 18:36:41 +00:00
|
|
|
res = bond_create_sysfs();
|
|
|
|
if (res)
|
|
|
|
goto err;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
register_netdevice_notifier(&bond_netdev_notifier);
|
2005-06-26 21:52:20 +00:00
|
|
|
register_inetaddr_notifier(&bond_inetaddr_notifier);
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
bond_register_ipv6_notifier();
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-09 18:36:04 +00:00
|
|
|
goto out;
|
|
|
|
err:
|
2005-09-18 07:24:12 +00:00
|
|
|
rtnl_lock();
|
2005-04-16 22:20:36 +00:00
|
|
|
bond_free_all();
|
|
|
|
rtnl_unlock();
|
2005-11-09 18:36:04 +00:00
|
|
|
out:
|
2005-04-16 22:20:36 +00:00
|
|
|
return res;
|
2005-11-09 18:36:04 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit bonding_exit(void)
|
|
|
|
{
|
|
|
|
unregister_netdevice_notifier(&bond_netdev_notifier);
|
2005-06-26 21:52:20 +00:00
|
|
|
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 01:51:14 +00:00
|
|
|
bond_unregister_ipv6_notifier();
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-03 00:49:39 +00:00
|
|
|
bond_destroy_sysfs();
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
rtnl_lock();
|
|
|
|
bond_free_all();
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(bonding_init);
|
|
|
|
module_exit(bonding_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(DRV_VERSION);
|
|
|
|
MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
|
|
|
|
MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others");
|