* b: if a hw mac address already is there, eth0's hw mac address
* will then be set from bond0.
*
- * v0.1 - first working version.
- * v0.2 - changed stats to be calculated by summing slaves stats.
- *
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- * - fix leaks on failure at bond_init
- *
- * 2000/09/30 - Willy Tarreau <willy at meta-x.org>
- * - added trivial code to release a slave device.
- * - fixed security bug (CAP_NET_ADMIN not checked)
- * - implemented MII link monitoring to disable dead links :
- * All MII capable slaves are checked every <miimon> milliseconds
- * (100 ms seems good). This value can be changed by passing it to
- * insmod. A value of zero disables the monitoring (default).
- * - fixed an infinite loop in bond_xmit_roundrobin() when there's no
- * good slave.
- * - made the code hopefully SMP safe
- *
- * 2000/10/03 - Willy Tarreau <willy at meta-x.org>
- * - optimized slave lists based on relevant suggestions from Thomas Davis
- * - implemented active-backup method to obtain HA with two switches:
- * stay as long as possible on the same active interface, while we
- * also monitor the backup one (MII link status) because we want to know
- * if we are able to switch at any time. ( pass "mode=1" to insmod )
- * - lots of stress testings because we need it to be more robust than the
- * wires ! :->
- *
- * 2000/10/09 - Willy Tarreau <willy at meta-x.org>
- * - added up and down delays after link state change.
- * - optimized the slaves chaining so that when we run forward, we never
- * repass through the bond itself, but we can find it by searching
- * backwards. Renders the deletion more difficult, but accelerates the
- * scan.
- * - smarter enslaving and releasing.
- * - finer and more robust SMP locking
- *
- * 2000/10/17 - Willy Tarreau <willy at meta-x.org>
- * - fixed two potential SMP race conditions
- *
- * 2000/10/18 - Willy Tarreau <willy at meta-x.org>
- * - small fixes to the monitoring FSM in case of zero delays
- * 2000/11/01 - Willy Tarreau <willy at meta-x.org>
- * - fixed first slave not automatically used in trunk mode.
- * 2000/11/10 : spelling of "EtherChannel" corrected.
- * 2000/11/13 : fixed a race condition in case of concurrent accesses to ioctl().
- * 2000/12/16 : fixed improper usage of rtnl_exlock_nowait().
- *
- * 2001/1/3 - Chad N. Tindel <ctindel at ieee dot org>
- * - The bonding driver now simulates MII status monitoring, just like
- * a normal network device. It will show that the link is down iff
- * every slave in the bond shows that their links are down. If at least
- * one slave is up, the bond's MII status will appear as up.
- *
- * 2001/2/7 - Chad N. Tindel <ctindel at ieee dot org>
- * - Applications can now query the bond from user space to get
- * information which may be useful. They do this by calling
- * the BOND_INFO_QUERY ioctl. Once the app knows how many slaves
- * are in the bond, it can call the BOND_SLAVE_INFO_QUERY ioctl to
- * get slave specific information (# link failures, etc). See
- * <linux/if_bonding.h> for more details. The structs of interest
- * are ifbond and ifslave.
- *
- * 2001/4/5 - Chad N. Tindel <ctindel at ieee dot org>
- * - Ported to 2.4 Kernel
- *
- * 2001/5/2 - Jeffrey E. Mast <jeff at mastfamily dot com>
- * - When a device is detached from a bond, the slave device is no longer
- * left thinking that is has a master.
- *
- * 2001/5/16 - Jeffrey E. Mast <jeff at mastfamily dot com>
- * - memset did not appropriately initialized the bond rw_locks. Used
- * rwlock_init to initialize to unlocked state to prevent deadlock when
- * first attempting a lock
- * - Called SET_MODULE_OWNER for bond device
- *
- * 2001/5/17 - Tim Anderson <tsa at mvista.com>
- * - 2 paths for releasing for slave release; 1 through ioctl
- * and 2) through close. Both paths need to release the same way.
- * - the free slave in bond release is changing slave status before
- * the free. The netdev_set_master() is intended to change slave state
- * so it should not be done as part of the release process.
- * - Simple rule for slave state at release: only the active in A/B and
- * only one in the trunked case.
- *
- * 2001/6/01 - Tim Anderson <tsa at mvista.com>
- * - Now call dev_close when releasing a slave so it doesn't screw up
- * out routing table.
- *
- * 2001/6/01 - Chad N. Tindel <ctindel at ieee dot org>
- * - Added /proc support for getting bond and slave information.
- * Information is in /proc/net/<bond device>/info.
- * - Changed the locking when calling bond_close to prevent deadlock.
- *
- * 2001/8/05 - Janice Girouard <girouard at us.ibm.com>
- * - correct problem where refcnt of slave is not incremented in bond_ioctl
- * so the system hangs when halting.
- * - correct locking problem when unable to malloc in bond_enslave.
- * - adding bond_xmit_xor logic.
- * - adding multiple bond device support.
- *
- * 2001/8/13 - Erik Habbinga <erik_habbinga at hp dot com>
- * - correct locking problem with rtnl_exlock_nowait
- *
- * 2001/8/23 - Janice Girouard <girouard at us.ibm.com>
- * - bzero initial dev_bonds, to correct oops
- * - convert SIOCDEVPRIVATE to new MII ioctl calls
- *
- * 2001/9/13 - Takao Indoh <indou dot takao at jp dot fujitsu dot com>
- * - Add the BOND_CHANGE_ACTIVE ioctl implementation
- *
- * 2001/9/14 - Mark Huth <mhuth at mvista dot com>
- * - Change MII_LINK_READY to not check for end of auto-negotiation,
- * but only for an up link.
- *
- * 2001/9/20 - Chad N. Tindel <ctindel at ieee dot org>
- * - Add the device field to bonding_t. Previously the net_device
- * corresponding to a bond wasn't available from the bonding_t
- * structure.
- *
- * 2001/9/25 - Janice Girouard <girouard at us.ibm.com>
- * - add arp_monitor for active backup mode
- *
- * 2001/10/23 - Takao Indoh <indou dot takao at jp dot fujitsu dot com>
- * - Various memory leak fixes
- *
- * 2001/11/5 - Mark Huth <mark dot huth at mvista dot com>
- * - Don't take rtnl lock in bond_mii_monitor as it deadlocks under
- * certain hotswap conditions.
- * Note: this same change may be required in bond_arp_monitor ???
- * - Remove possibility of calling bond_sethwaddr with NULL slave_dev ptr
- * - Handle hot swap ethernet interface deregistration events to remove
- * kernel oops following hot swap of enslaved interface
- *
- * 2002/1/2 - Chad N. Tindel <ctindel at ieee dot org>
- * - Restore original slave flags at release time.
- *
- * 2002/02/18 - Erik Habbinga <erik_habbinga at hp dot com>
- * - bond_release(): calling kfree on our_slave after call to
- * bond_restore_slave_flags, not before
- * - bond_enslave(): saving slave flags into original_flags before
- * call to netdev_set_master, so the IFF_SLAVE flag doesn't end
- * up in original_flags
- *
- * 2002/04/05 - Mark Smith <mark.smith at comdev dot cc> and
- * Steve Mead <steve.mead at comdev dot cc>
- * - Port Gleb Natapov's multicast support patchs from 2.4.12
- * to 2.4.18 adding support for multicast.
- *
- * 2002/06/10 - Tony Cureington <tony.cureington * hp_com>
- * - corrected uninitialized pointer (ifr.ifr_data) in bond_check_dev_link;
- * actually changed function to use MIIPHY, then MIIREG, and finally
- * ETHTOOL to determine the link status
- * - fixed bad ifr_data pointer assignments in bond_ioctl
- * - corrected mode 1 being reported as active-backup in bond_get_info;
- * also added text to distinguish type of load balancing (rr or xor)
- * - change arp_ip_target module param from "1-12s" (array of 12 ptrs)
- * to "s" (a single ptr)
- *
- * 2002/08/30 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Removed acquisition of xmit_lock in set_multicast_list; caused
- * deadlock on SMP (lock is held by caller).
- * - Revamped SIOCGMIIPHY, SIOCGMIIREG portion of bond_check_dev_link().
- *
- * 2002/09/18 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Fixed up bond_check_dev_link() (and callers): removed some magic
- * numbers, banished local MII_ defines, wrapped ioctl calls to
- * prevent EFAULT errors
- *
- * 2002/9/30 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - make sure the ip target matches the arp_target before saving the
- * hw address.
- *
- * 2002/9/30 - Dan Eisner <eisner at 2robots dot com>
- * - make sure my_ip is set before taking down the link, since
- * not all switches respond if the source ip is not set.
- *
- * 2002/10/8 - Janice Girouard <girouard at us dot ibm dot com>
- * - read in the local ip address when enslaving a device
- * - add primary support
- * - make sure 2*arp_interval has passed when a new device
- * is brought on-line before taking it down.
- *
- * 2002/09/11 - Philippe De Muyter <phdm at macqel dot be>
- * - Added bond_xmit_broadcast logic.
- * - Added bond_mode() support function.
- *
- * 2002/10/26 - Laurent Deniel <laurent.deniel at free.fr>
- * - allow to register multicast addresses only on active slave
- * (useful in active-backup mode)
- * - add multicast module parameter
- * - fix deletion of multicast groups after unloading module
- *
- * 2002/11/06 - Kameshwara Rayaprolu <kameshwara.rao * wipro_com>
- * - Changes to prevent panic from closing the device twice; if we close
- * the device in bond_release, we must set the original_flags to down
- * so it won't be closed again by the network layer.
- *
- * 2002/11/07 - Tony Cureington <tony.cureington * hp_com>
- * - Fix arp_target_hw_addr memory leak
- * - Created activebackup_arp_monitor function to handle arp monitoring
- * in active backup mode - the bond_arp_monitor had several problems...
- * such as allowing slaves to tx arps sequentially without any delay
- * for a response
- * - Renamed bond_arp_monitor to loadbalance_arp_monitor and re-wrote
- * this function to just handle arp monitoring in load-balancing mode;
- * it is a lot more compact now
- * - Changes to ensure one and only one slave transmits in active-backup
- * mode
- * - Robustesize parameters; warn users about bad combinations of
- * parameters; also if miimon is specified and a network driver does
- * not support MII or ETHTOOL, inform the user of this
- * - Changes to support link_failure_count when in arp monitoring mode
- * - Fix up/down delay reported in /proc
- * - Added version; log version; make version available from "modinfo -d"
- * - Fixed problem in bond_check_dev_link - if the first IOCTL (SIOCGMIIPH)
- * failed, the ETHTOOL ioctl never got a chance
- *
- * 2002/11/16 - Laurent Deniel <laurent.deniel at free.fr>
- * - fix multicast handling in activebackup_arp_monitor
- * - remove one unnecessary and confusing curr_active_slave == slave test
- * in activebackup_arp_monitor
- *
- * 2002/11/17 - Laurent Deniel <laurent.deniel at free.fr>
- * - fix bond_slave_info_query when slave_id = num_slaves
- *
- * 2002/11/19 - Janice Girouard <girouard at us dot ibm dot com>
- * - correct ifr_data reference. Update ifr_data reference
- * to mii_ioctl_data struct values to avoid confusion.
- *
- * 2002/11/22 - Bert Barbe <bert.barbe at oracle dot com>
- * - Add support for multiple arp_ip_target
- *
- * 2002/12/13 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Changed to allow text strings for mode and multicast, e.g.,
- * insmod bonding mode=active-backup. The numbers still work.
- * One change: an invalid choice will cause module load failure,
- * rather than the previous behavior of just picking one.
- * - Minor cleanups; got rid of dup ctype stuff, atoi function
- *
- * 2003/02/07 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Added use_carrier module parameter that causes miimon to
- * use netif_carrier_ok() test instead of MII/ETHTOOL ioctls.
- * - Minor cleanups; consolidated ioctl calls to one function.
- *
- * 2003/02/07 - Tony Cureington <tony.cureington * hp_com>
- * - Fix bond_mii_monitor() logic error that could result in
- * bonding round-robin mode ignoring links after failover/recovery
- *
- * 2003/03/17 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - kmalloc fix (GFP_KERNEL to GFP_ATOMIC) reported by
- * Shmulik dot Hen at intel.com.
- * - Based on discussion on mailing list, changed use of
- * update_slave_cnt(), created wrapper functions for adding/removing
- * slaves, changed bond_xmit_xor() to check slave_cnt instead of
- * checking slave and slave->dev (which only worked by accident).
- * - Misc code cleanup: get arp_send() prototype from header file,
- * add max_bonds to bonding.txt.
- *
- * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- * Shmulik Hen <shmulik.hen at intel dot com>
- * - Make sure only bond_attach_slave() and bond_detach_slave() can
- * manipulate the slave list, including slave_cnt, even when in
- * bond_release_all().
- * - Fixed hang in bond_release() with traffic running:
- * netdev_set_master() must not be called from within the bond lock.
- *
- * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- * Shmulik Hen <shmulik.hen at intel dot com>
- * - Fixed hang in bond_enslave() with traffic running:
- * netdev_set_master() must not be called from within the bond lock.
- *
- * 2003/03/18 - Amir Noam <amir.noam at intel dot com>
- * - Added support for getting slave's speed and duplex via ethtool.
- * Needed for 802.3ad and other future modes.
- *
- * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- * Shmulik Hen <shmulik.hen at intel dot com>
- * - Enable support of modes that need to use the unique mac address of
- * each slave.
- * * bond_enslave(): Moved setting the slave's mac address, and
- * openning it, from the application to the driver. This breaks
- * backward comaptibility with old versions of ifenslave that open
- * the slave before enalsving it !!!.
- * * bond_release(): The driver also takes care of closing the slave
- * and restoring its original mac address.
- * - Removed the code that restores all base driver's flags.
- * Flags are automatically restored once all undo stages are done
- * properly.
- * - Block possibility of enslaving before the master is up. This
- * prevents putting the system in an unstable state.
- *
- * 2003/03/18 - Amir Noam <amir.noam at intel dot com>,
- * Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- * Shmulik Hen <shmulik.hen at intel dot com>
- * - Added support for IEEE 802.3ad Dynamic link aggregation mode.
- *
- * 2003/05/01 - Amir Noam <amir.noam at intel dot com>
- * - Added ABI version control to restore compatibility between
- * new/old ifenslave and new/old bonding.
- *
- * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
- * - Fixed bug in bond_release_all(): save old value of curr_active_slave
- * before setting it to NULL.
- * - Changed driver versioning scheme to include version number instead
- * of release date (that is already in another field). There are 3
- * fields X.Y.Z where:
- * X - Major version - big behavior changes
- * Y - Minor version - addition of features
- * Z - Extra version - minor changes and bug fixes
- * The current version is 1.0.0 as a base line.
- *
- * 2003/05/01 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- * Amir Noam <amir.noam at intel dot com>
- * - Added support for lacp_rate module param.
- * - Code beautification and style changes (mainly in comments).
- * new version - 1.0.1
- *
- * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
- * - Based on discussion on mailing list, changed locking scheme
- * to use lock/unlock or lock_bh/unlock_bh appropriately instead
- * of lock_irqsave/unlock_irqrestore. The new scheme helps exposing
- * hidden bugs and solves system hangs that occurred due to the fact
- * that holding lock_irqsave doesn't prevent softirqs from running.
- * This also increases total throughput since interrupts are not
- * blocked on each transmitted packets or monitor timeout.
- * new version - 2.0.0
- *
- * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
- * - Added support for Transmit load balancing mode.
- * - Concentrate all assignments of curr_active_slave to a single point
- * so specific modes can take actions when the primary adapter is
- * changed.
- * - Take the updelay parameter into consideration during bond_enslave
- * since some adapters loose their link during setting the device.
- * - Renamed bond_3ad_link_status_changed() to
- * bond_3ad_handle_link_change() for compatibility with TLB.
- * new version - 2.1.0
- *
- * 2003/05/01 - Tsippy Mendelson <tsippy.mendelson at intel dot com>
- * - Added support for Adaptive load balancing mode which is
- * equivalent to Transmit load balancing + Receive load balancing.
- * new version - 2.2.0
- *
- * 2003/05/15 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Applied fix to activebackup_arp_monitor posted to bonding-devel
- * by Tony Cureington <tony.cureington * hp_com>. Fixes ARP
- * monitor endless failover bug. Version to 2.2.10
- *
- * 2003/05/20 - Amir Noam <amir.noam at intel dot com>
- * - Fixed bug in ABI version control - Don't commit to a specific
- * ABI version if receiving unsupported ioctl commands.
- *
- * 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
- * - Fix ifenslave -c causing bond to loose existing routes;
- * added bond_set_mac_address() that doesn't require the
- * bond to be down.
- * - In conjunction with fix for ifenslave -c, in
- * bond_change_active(), changing to the already active slave
- * is no longer an error (it successfully does nothing).
- *
- * 2003/06/30 - Amir Noam <amir.noam at intel dot com>
- * - Fixed bond_change_active() for ALB/TLB modes.
- * Version to 2.2.14.
- *
- * 2003/07/29 - Amir Noam <amir.noam at intel dot com>
- * - Fixed ARP monitoring bug.
- * Version to 2.2.15.
- *
- * 2003/07/31 - Willy Tarreau <willy at ods dot org>
- * - Fixed kernel panic when using ARP monitoring without
- * setting bond's IP address.
- * Version to 2.2.16.
- *
- * 2003/08/06 - Amir Noam <amir.noam at intel dot com>
- * - Back port from 2.6: use alloc_netdev(); fix /proc handling;
- * made stats a part of bond struct so no need to allocate
- * and free it separately; use standard list operations instead
- * of pre-allocated array of bonds.
- * Version to 2.3.0.
- *
- * 2003/08/07 - Jay Vosburgh <fubar at us dot ibm dot com>,
- * Amir Noam <amir.noam at intel dot com> and
- * Shmulik Hen <shmulik.hen at intel dot com>
- * - Propagating master's settings: Distinguish between modes that
- * use a primary slave from those that don't, and propagate settings
- * accordingly; Consolidate change_active opeartions and add
- * reselect_active and find_best opeartions; Decouple promiscuous
- * handling from the multicast mode setting; Add support for changing
- * HW address and MTU with proper unwind; Consolidate procfs code,
- * add CHANGENAME handler; Enhance netdev notification handling.
- * Version to 2.4.0.
- *
- * 2003/09/15 - Stephen Hemminger <shemminger at osdl dot org>,
- * Amir Noam <amir.noam at intel dot com>
- * - Convert /proc to seq_file interface.
- * Change /proc/net/bondX/info to /proc/net/bonding/bondX.
- * Set version to 2.4.1.
- *
- * 2003/11/20 - Amir Noam <amir.noam at intel dot com>
- * - Fix /proc creation/destruction.
- *
- * 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
- * - Massive cleanup - Set version to 2.5.0
- * Code changes:
- * o Consolidate format of prints and debug prints.
- * o Remove bonding_t/slave_t typedefs and consolidate all casts.
- * o Remove dead code and unnecessary checks.
- * o Consolidate starting/stopping timers.
- * o Consolidate handling of primary module param throughout the code.
- * o Removed multicast module param support - all settings are done
- * according to mode.
- * o Slave list iteration - bond is no longer part of the list,
- * added cyclic list iteration macros.
- * o Consolidate error handling in all xmit functions.
- * Style changes:
- * o Consolidate function naming and declarations.
- * o Consolidate function params and local variables names.
- * o Consolidate return values.
- * o Consolidate curly braces.
- * o Consolidate conditionals format.
- * o Change struct member names and types.
- * o Chomp trailing spaces, remove empty lines, fix indentations.
- * o Re-organize code according to context.
- *
- * 2003/12/30 - Amir Noam <amir.noam at intel dot com>
- * - Fixed: Cannot remove and re-enslave the original active slave.
- * - Fixed: Releasing the original active slave causes mac address
- * duplication.
- * - Add support for slaves that use ethtool_ops.
- * Set version to 2.5.3.
- *
- * 2004/01/05 - Amir Noam <amir.noam at intel dot com>
- * - Save bonding parameters per bond instead of using the global values.
- * Set version to 2.5.4.
- *
- * 2004/01/14 - Shmulik Hen <shmulik.hen at intel dot com>
- * - Enhance VLAN support:
- * * Add support for VLAN hardware acceleration capable slaves.
- * * Add capability to tag self generated packets in ALB/TLB modes.
- * Set version to 2.6.0.
- * 2004/10/29 - Mitch Williams <mitch.a.williams at intel dot com>
- * - Fixed bug when unloading module while using 802.3ad. If
- * spinlock debugging is turned on, this causes a stack dump.
- * Solution is to move call to dev_remove_pack outside of the
- * spinlock.
- * Set version to 2.6.1.
- * 2005/06/05 - Jay Vosburgh <fubar@us.ibm.com>
- * - Support for generating gratuitous ARPs in active-backup mode.
- * Includes support for VLAN tagging all bonding-generated ARPs
- * as needed. Set version to 2.6.2.
- * 2005/06/08 - Jason Gabler <jygabler at lbl dot gov>
- * - alternate hashing policy support for mode 2
- * * Added kernel parameter "xmit_hash_policy" to allow the selection
- * of different hashing policies for mode 2. The original mode 2
- * policy is the default, now found in xmit_hash_policy_layer2().
- * * Added xmit_hash_policy_layer34()
- * - Modified by Jay Vosburgh <fubar@us.ibm.com> to also support mode 4.
- * Set version to 2.6.3.
- * 2005/09/26 - Jay Vosburgh <fubar@us.ibm.com>
- * - Removed backwards compatibility for old ifenslaves. Version 2.6.4.
*/
//#define BONDING_DEBUG 1
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
+#include <linux/igmp.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/if_bonding.h>
+#include <linux/jiffies.h>
#include <net/route.h>
+#include <net/net_namespace.h>
#include "bonding.h"
#include "bond_3ad.h"
#include "bond_alb.h"
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
+static char *arp_validate = NULL;
+static int fail_over_mac = 0;
struct bond_params bonding_defaults;
module_param(max_bonds, 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");
+module_param(arp_validate, charp, 0);
+MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all");
+module_param(fail_over_mac, int, 0);
+MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the same MAC. 0 of off (default), 1 for on.");
/*----------------------------- Global variables ----------------------------*/
-static const char *version =
+static const char * const version =
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
LIST_HEAD(bond_dev_list);
static struct proc_dir_entry *bond_proc_dir = NULL;
#endif
-static u32 arp_target[BOND_MAX_ARP_TARGETS] = { 0, } ;
+extern struct rw_semaphore bonding_rwsem;
+static __be32 arp_target[BOND_MAX_ARP_TARGETS] = { 0, } ;
static int arp_ip_count = 0;
static int bond_mode = BOND_MODE_ROUNDROBIN;
static int xmit_hashtype= BOND_XMIT_POLICY_LAYER2;
struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ "layer2", BOND_XMIT_POLICY_LAYER2},
{ "layer3+4", BOND_XMIT_POLICY_LAYER34},
+{ "layer2+3", BOND_XMIT_POLICY_LAYER23},
+{ NULL, -1},
+};
+
+struct bond_parm_tbl arp_validate_tbl[] = {
+{ "none", BOND_ARP_VALIDATE_NONE},
+{ "active", BOND_ARP_VALIDATE_ACTIVE},
+{ "backup", BOND_ARP_VALIDATE_BACKUP},
+{ "all", BOND_ARP_VALIDATE_ALL},
{ NULL, -1},
};
/*-------------------------- Forward declarations ---------------------------*/
static void bond_send_gratuitous_arp(struct bonding *bond);
+static void bond_deinit(struct net_device *bond_dev);
/*---------------------------- General routines -----------------------------*/
-const char *bond_mode_name(int mode)
+static const char *bond_mode_name(int mode)
{
switch (mode) {
case BOND_MODE_ROUNDROBIN :
*/
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev)
{
- unsigned short vlan_id;
+ unsigned short uninitialized_var(vlan_id);
if (!list_empty(&bond->vlan_list) &&
!(slave_dev->features & NETIF_F_HW_VLAN_TX) &&
/* Save and then restore vlan_dev in the grp array,
* since the slave's driver might clear it.
*/
- vlan_dev = bond->vlgrp->vlan_devices[vid];
+ vlan_dev = vlan_group_get_device(bond->vlgrp, vid);
slave_dev->vlan_rx_kill_vid(slave_dev, vid);
- bond->vlgrp->vlan_devices[vid] = vlan_dev;
+ vlan_group_set_device(bond->vlgrp, vid, vlan_dev);
}
}
/* Save and then restore vlan_dev in the grp array,
* since the slave's driver might clear it.
*/
- vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id];
+ vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
slave_dev->vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
- bond->vlgrp->vlan_devices[vlan->vlan_id] = vlan_dev;
+ vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev);
}
unreg:
/*------------------------------- Link status -------------------------------*/
/*
+ * 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;
+}
+
+/*
* 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
static int bond_update_speed_duplex(struct slave *slave)
{
struct net_device *slave_dev = slave->dev;
- static int (* ioctl)(struct net_device *, struct ifreq *, int);
- struct ifreq ifr;
struct ethtool_cmd etool;
+ int res;
/* Fake speed and duplex */
slave->speed = SPEED_100;
slave->duplex = DUPLEX_FULL;
- if (slave_dev->ethtool_ops) {
- u32 res;
-
- if (!slave_dev->ethtool_ops->get_settings) {
- return -1;
- }
-
- res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool);
- if (res < 0) {
- return -1;
- }
-
- goto verify;
- }
+ if (!slave_dev->ethtool_ops || !slave_dev->ethtool_ops->get_settings)
+ return -1;
- ioctl = slave_dev->do_ioctl;
- strncpy(ifr.ifr_name, slave_dev->name, IFNAMSIZ);
- etool.cmd = ETHTOOL_GSET;
- ifr.ifr_data = (char*)&etool;
- if (!ioctl || (IOCTL(slave_dev, &ifr, SIOCETHTOOL) < 0)) {
+ res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool);
+ if (res < 0)
return -1;
- }
-verify:
switch (etool.speed) {
case SPEED_10:
case SPEED_100:
case SPEED_1000:
+ case SPEED_10000:
break;
default:
return -1;
static int (* ioctl)(struct net_device *, struct ifreq *, int);
struct ifreq ifr;
struct mii_ioctl_data *mii;
- struct ethtool_value etool;
if (bond->params.use_carrier) {
return netif_carrier_ok(slave_dev) ? BMSR_LSTATUS : 0;
}
}
- /* try SIOCETHTOOL ioctl, some drivers cache ETHTOOL_GLINK */
- /* for a period of time so we attempt to get link status */
- /* from it last if the above MII ioctls fail... */
+ /*
+ * Some drivers cache ETHTOOL_GLINK for a period of time so we only
+ * attempt to get link status from it if the above MII ioctls fail.
+ */
if (slave_dev->ethtool_ops) {
if (slave_dev->ethtool_ops->get_link) {
u32 link;
}
}
- if (ioctl) {
- strncpy(ifr.ifr_name, slave_dev->name, IFNAMSIZ);
- etool.cmd = ETHTOOL_GLINK;
- ifr.ifr_data = (char*)&etool;
- if (IOCTL(slave_dev, &ifr, SIOCETHTOOL) == 0) {
- if (etool.data == 1) {
- return BMSR_LSTATUS;
- } else {
- dprintk("SIOCETHTOOL shows link down\n");
- return 0;
- }
- }
- }
-
/*
* If reporting, report that either there's no dev->do_ioctl,
- * or both SIOCGMIIREG and SIOCETHTOOL failed (meaning that we
+ * or both SIOCGMIIREG and get_link failed (meaning that we
* cannot report link status). If not reporting, pretend
* we're ok.
*/
}
}
+
+/*
+ * 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) {
+ for (im = in_dev->mc_list; im; im = im->next) {
+ ip_mc_rejoin_group(im);
+ }
+ }
+
+ rcu_read_unlock();
+}
+
/*
* Totally destroys the mc_list in bond
*/
kfree(dmi);
dmi = bond->mc_list;
}
+ bond->mc_list = NULL;
}
/*
for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) {
dev_mc_add(new_active->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
}
+ bond_resend_igmp_join_requests(bond);
}
}
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
bond_alb_handle_active_change(bond, new_active);
+ if (old_active)
+ bond_set_slave_inactive_flags(old_active);
+ if (new_active)
+ bond_set_slave_active_flags(new_active);
} else {
bond->curr_active_slave = new_active;
}
if (new_active) {
bond_set_slave_active_flags(new_active);
}
- bond_send_gratuitous_arp(bond);
+
+ /* when bonding does not set the slave MAC address, the bond MAC
+ * address is the one of the active slave.
+ */
+ if (new_active && bond->params.fail_over_mac)
+ memcpy(bond->dev->dev_addr, new_active->dev->dev_addr,
+ new_active->dev->addr_len);
+ if (bond->curr_active_slave &&
+ test_bit(__LINK_STATE_LINKWATCH_PENDING,
+ &bond->curr_active_slave->dev->state)) {
+ dprintk("delaying gratuitous arp on %s\n",
+ bond->curr_active_slave->dev->name);
+ bond->send_grat_arp = 1;
+ } else
+ bond_send_gratuitous_arp(bond);
}
}
void bond_select_active_slave(struct bonding *bond)
{
struct slave *best_slave;
+ int rv;
best_slave = bond_find_best_slave(bond);
if (best_slave != bond->curr_active_slave) {
bond_change_active_slave(bond, best_slave);
+ rv = bond_set_carrier(bond);
+ if (!rv)
+ return;
+
+ if (netif_carrier_ok(bond->dev)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: first active interface up!\n",
+ bond->dev->name);
+ } else {
+ printk(KERN_INFO DRV_NAME ": %s: "
+ "now running without any active interface !\n",
+ bond->dev->name);
+ }
}
}
/*---------------------------------- IOCTL ----------------------------------*/
-int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev)
+static int bond_sethwaddr(struct net_device *bond_dev,
+ struct net_device *slave_dev)
{
dprintk("bond_dev=%p\n", bond_dev);
dprintk("slave_dev=%p\n", slave_dev);
return 0;
}
-#define BOND_INTERSECT_FEATURES \
- (NETIF_F_SG|NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM)
+#define BOND_VLAN_FEATURES \
+ (NETIF_F_VLAN_CHALLENGED | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX | \
+ NETIF_F_HW_VLAN_FILTER)
/*
* Compute the common dev->feature set available to all slaves. Some
- * feature bits are managed elsewhere, so preserve feature bits set on
- * master device that are not part of the examined set.
+ * feature bits are managed elsewhere, so preserve those feature bits
+ * on the master device.
*/
static int bond_compute_features(struct bonding *bond)
{
- unsigned long features = BOND_INTERSECT_FEATURES;
struct slave *slave;
struct net_device *bond_dev = bond->dev;
+ unsigned long features = bond_dev->features;
+ unsigned short max_hard_header_len = max((u16)ETH_HLEN,
+ bond_dev->hard_header_len);
int i;
- bond_for_each_slave(bond, slave, i)
- features &= (slave->dev->features & BOND_INTERSECT_FEATURES);
+ features &= ~(NETIF_F_ALL_CSUM | BOND_VLAN_FEATURES);
+ features |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
+ NETIF_F_GSO_MASK | NETIF_F_NO_CSUM;
- if ((features & NETIF_F_SG) &&
- !(features & (NETIF_F_IP_CSUM |
- NETIF_F_NO_CSUM |
- NETIF_F_HW_CSUM)))
- features &= ~NETIF_F_SG;
+ bond_for_each_slave(bond, slave, i) {
+ features = netdev_compute_features(features,
+ slave->dev->features);
+ if (slave->dev->hard_header_len > max_hard_header_len)
+ max_hard_header_len = slave->dev->hard_header_len;
+ }
- features |= (bond_dev->features & ~BOND_INTERSECT_FEATURES);
+ features |= (bond_dev->features & BOND_VLAN_FEATURES);
bond_dev->features = features;
+ bond_dev->hard_header_len = max_hard_header_len;
return 0;
}
+
+static void bond_setup_by_slave(struct net_device *bond_dev,
+ struct net_device *slave_dev)
+{
+ struct bonding *bond = bond_dev->priv;
+
+ bond_dev->neigh_setup = slave_dev->neigh_setup;
+ bond_dev->header_ops = slave_dev->header_ops;
+
+ 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);
+ bond->setup_by_slave = 1;
+}
+
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
/* bond must be initialized by bond_open() before enslaving */
if (!(bond_dev->flags & IFF_UP)) {
- dprintk("Error, master_dev is not up\n");
- return -EPERM;
+ printk(KERN_WARNING DRV_NAME
+ " %s: master_dev is not up in bond_enslave\n",
+ bond_dev->name);
}
/* already enslaved */
goto err_undo_flags;
}
+ /* 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) {
+ printk(KERN_ERR DRV_NAME ": %s ether type (%d) is different "
+ "from other slaves (%d), can not enslave it.\n",
+ slave_dev->name,
+ slave_dev->type, bond_dev->type);
+ res = -EINVAL;
+ goto err_undo_flags;
+ }
+
if (slave_dev->set_mac_address == NULL) {
- printk(KERN_ERR DRV_NAME
- ": %s: Error: The slave device you specified does "
- "not support setting the MAC address. "
- "Your kernel likely does not support slave "
- "devices.\n", bond_dev->name);
- res = -EOPNOTSUPP;
- goto err_undo_flags;
+ if (bond->slave_cnt == 0) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Warning: The first slave device "
+ "specified does not support setting the MAC "
+ "address. Enabling the fail_over_mac option.",
+ bond_dev->name);
+ bond->params.fail_over_mac = 1;
+ } else if (!bond->params.fail_over_mac) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Error: The slave device specified "
+ "does not support setting the MAC address, "
+ "but fail_over_mac is not enabled.\n"
+ , bond_dev->name);
+ res = -EOPNOTSUPP;
+ goto err_undo_flags;
+ }
}
- new_slave = kmalloc(sizeof(struct slave), GFP_KERNEL);
+ new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
if (!new_slave) {
res = -ENOMEM;
goto err_undo_flags;
}
- memset(new_slave, 0, sizeof(struct slave));
-
/* save slave's original flags before calling
* netdev_set_master and dev_open
*/
*/
memcpy(new_slave->perm_hwaddr, slave_dev->dev_addr, ETH_ALEN);
- /*
- * 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) {
- dprintk("Error %d calling set_mac_address\n", res);
- goto err_free;
+ if (!bond->params.fail_over_mac) {
+ /*
+ * 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) {
+ dprintk("Error %d calling set_mac_address\n", res);
+ goto err_free;
+ }
}
+ res = netdev_set_master(slave_dev, bond_dev);
+ if (res) {
+ dprintk("Error %d calling netdev_set_master\n", res);
+ goto err_close;
+ }
/* open the slave since the application closed it */
res = dev_open(slave_dev);
if (res) {
goto err_restore_mac;
}
- res = netdev_set_master(slave_dev, bond_dev);
- if (res) {
- dprintk("Error %d calling netdev_set_master\n", res);
- goto err_close;
- }
-
new_slave->dev = slave_dev;
+ slave_dev->priv_flags |= IFF_BONDING;
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
dev_set_allmulti(slave_dev, 1);
}
+ netif_tx_lock_bh(bond_dev);
/* upload master's mc_list to new slave */
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
}
+ netif_tx_unlock_bh(bond_dev);
}
if (bond->params.mode == BOND_MODE_8023AD) {
bond_compute_features(bond);
+ new_slave->last_arp_rx = jiffies;
+
if (bond->params.miimon && !bond->params.use_carrier) {
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
switch (bond->params.mode) {
case BOND_MODE_ACTIVEBACKUP:
- /* if we're in active-backup mode, we need one and only one active
- * interface. The backup interfaces will have their NOARP flag set
- * because we need them to be completely deaf and not to respond to
- * any ARP request on the network to avoid fooling a switch. Thus,
- * since we guarantee that curr_active_slave always point to the last
- * usable interface, we just have to verify this interface's flag.
- */
- if (((!bond->curr_active_slave) ||
- (bond->curr_active_slave->dev->flags & IFF_NOARP)) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- dprintk("This is the first active slave\n");
- /* first slave or no active slave yet, and this link
- is OK, so make this interface the active one */
- bond_change_active_slave(bond, new_slave);
- } else {
- dprintk("This is just a backup slave\n");
- bond_set_slave_inactive_flags(new_slave);
- }
+ bond_set_slave_inactive_flags(new_slave);
+ bond_select_active_slave(bond);
break;
case BOND_MODE_8023AD:
/* in 802.3ad mode, the internal mechanism
case BOND_MODE_TLB:
case BOND_MODE_ALB:
new_slave->state = BOND_STATE_ACTIVE;
- if ((!bond->curr_active_slave) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- /* first slave or no active slave yet, and this link
- * is OK, so make this interface the active one
- */
- bond_change_active_slave(bond, new_slave);
- }
+ bond_set_slave_inactive_flags(new_slave);
break;
default:
dprintk("This slave is always active in trunk mode\n");
break;
} /* switch(bond_mode) */
+ bond_set_carrier(bond);
+
write_unlock_bh(&bond->lock);
+ res = bond_create_slave_symlinks(bond_dev, slave_dev);
+ if (res)
+ goto err_unset_master;
+
printk(KERN_INFO DRV_NAME
": %s: enslaving %s as a%s interface with a%s link.\n",
bond_dev->name, slave_dev->name,
dev_close(slave_dev);
err_restore_mac:
- memcpy(addr.sa_data, new_slave->perm_hwaddr, ETH_ALEN);
- addr.sa_family = slave_dev->type;
- dev_set_mac_address(slave_dev, &addr);
+ if (!bond->params.fail_over_mac) {
+ memcpy(addr.sa_data, new_slave->perm_hwaddr, ETH_ALEN);
+ addr.sa_family = slave_dev->type;
+ dev_set_mac_address(slave_dev, &addr);
+ }
err_free:
kfree(new_slave);
struct slave *slave, *oldcurrent;
struct sockaddr addr;
int mac_addr_differ;
+ DECLARE_MAC_BUF(mac);
/* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_SLAVE) ||
printk(KERN_INFO DRV_NAME
": %s: %s not enslaved\n",
bond_dev->name, slave_dev->name);
+ write_unlock_bh(&bond->lock);
return -EINVAL;
}
ETH_ALEN);
if (!mac_addr_differ && (bond->slave_cnt > 1)) {
printk(KERN_WARNING DRV_NAME
- ": %s: Warning: the permanent HWaddr of %s "
- "- %02X:%02X:%02X:%02X:%02X:%02X - is "
- "still in use by %s. Set the HWaddr of "
- "%s to a different address to avoid "
- "conflicts.\n",
+ ": %s: Warning: the permanent HWaddr of %s - "
+ "%s - is still in use by %s. "
+ "Set the HWaddr of %s to a different address "
+ "to avoid conflicts.\n",
bond_dev->name,
slave_dev->name,
- slave->perm_hwaddr[0],
- slave->perm_hwaddr[1],
- slave->perm_hwaddr[2],
- slave->perm_hwaddr[3],
- slave->perm_hwaddr[4],
- slave->perm_hwaddr[5],
+ print_mac(mac, slave->perm_hwaddr),
bond_dev->name,
slave_dev->name);
}
* has been cleared (if our_slave == old_current),
* but before a new active slave is selected.
*/
+ write_unlock_bh(&bond->lock);
bond_alb_deinit_slave(bond, slave);
+ write_lock_bh(&bond->lock);
}
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);
+
bond_select_active_slave(bond);
- if (!bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
+ write_unlock_bh(&bond->curr_slave_lock);
+ read_unlock(&bond->lock);
+ write_lock_bh(&bond->lock);
}
if (bond->slave_cnt == 0) {
+ bond_set_carrier(bond);
+
/* 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
write_unlock_bh(&bond->lock);
+ /* must do this from outside any spinlocks */
+ bond_destroy_slave_symlinks(bond_dev, slave_dev);
+
bond_del_vlans_from_slave(bond, slave_dev);
/* If the mode USES_PRIMARY, then we should only remove its
}
/* flush master's mc_list from slave */
+ netif_tx_lock_bh(bond_dev);
bond_mc_list_flush(bond_dev, slave_dev);
+ netif_tx_unlock_bh(bond_dev);
}
netdev_set_master(slave_dev, NULL);
/* close slave before restoring its mac address */
dev_close(slave_dev);
- /* 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);
-
- /* restore the original state of the
- * IFF_NOARP flag that might have been
- * set by bond_set_slave_inactive_flags()
- */
- if ((slave->original_flags & IFF_NOARP) == 0) {
- slave_dev->flags &= ~IFF_NOARP;
+ if (!bond->params.fail_over_mac) {
+ /* 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);
}
+ slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
+ IFF_SLAVE_INACTIVE | IFF_BONDING |
+ IFF_SLAVE_NEEDARP);
+
kfree(slave);
return 0; /* deletion OK */
}
/*
+* Destroy a bonding device.
+* Must be under rtnl_lock when this function is called.
+*/
+void bond_destroy(struct bonding *bond)
+{
+ bond_deinit(bond->dev);
+ bond_destroy_sysfs_entry(bond);
+ unregister_netdevice(bond->dev);
+}
+
+/*
+* First release a slave and than destroy the bond if no more slaves iare left.
+* Must be under rtnl_lock when this function is called.
+*/
+int bond_release_and_destroy(struct net_device *bond_dev, struct net_device *slave_dev)
+{
+ struct bonding *bond = bond_dev->priv;
+ int ret;
+
+ ret = bond_release(bond_dev, slave_dev);
+ if ((ret == 0) && (bond->slave_cnt == 0)) {
+ printk(KERN_INFO DRV_NAME ": %s: destroying bond %s.\n",
+ bond_dev->name, bond_dev->name);
+ bond_destroy(bond);
+ }
+ return ret;
+}
+
+/*
* This function releases all slaves.
*/
static int bond_release_all(struct net_device *bond_dev)
write_lock_bh(&bond->lock);
+ netif_carrier_off(bond_dev);
+
if (bond->slave_cnt == 0) {
goto out;
}
slave_dev = slave->dev;
bond_detach_slave(bond, slave);
+ /* 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);
+
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
/* must be called only after the slave
bond_compute_features(bond);
- /* 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);
-
+ bond_destroy_slave_symlinks(bond_dev, slave_dev);
bond_del_vlans_from_slave(bond, slave_dev);
/* If the mode USES_PRIMARY, then we should only remove its
}
/* flush master's mc_list from slave */
+ netif_tx_lock_bh(bond_dev);
bond_mc_list_flush(bond_dev, slave_dev);
+ netif_tx_unlock_bh(bond_dev);
}
netdev_set_master(slave_dev, NULL);
/* close slave before restoring its mac address */
dev_close(slave_dev);
- /* 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);
-
- /* restore the original state of the IFF_NOARP flag that might have
- * been set by bond_set_slave_inactive_flags()
- */
- if ((slave->original_flags & IFF_NOARP) == 0) {
- slave_dev->flags &= ~IFF_NOARP;
+ if (!bond->params.fail_over_mac) {
+ /* 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);
}
+ slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
+ IFF_SLAVE_INACTIVE);
+
kfree(slave);
/* re-acquire the lock before getting the next slave */
return -EINVAL;
}
- write_lock_bh(&bond->lock);
+ read_lock(&bond->lock);
+ read_lock(&bond->curr_slave_lock);
old_active = bond->curr_active_slave;
+ read_unlock(&bond->curr_slave_lock);
+
new_active = bond_get_slave_by_dev(bond, slave_dev);
/*
* Changing to the current active: do nothing; return success.
*/
if (new_active && (new_active == old_active)) {
- write_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
return 0;
}
(old_active) &&
(new_active->link == BOND_LINK_UP) &&
IS_UP(new_active->dev)) {
+ write_lock_bh(&bond->curr_slave_lock);
bond_change_active_slave(bond, new_active);
+ write_unlock_bh(&bond->curr_slave_lock);
} else {
res = -EINVAL;
}
- write_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
return res;
}
info->bond_mode = bond->params.mode;
info->miimon = bond->params.miimon;
- read_lock_bh(&bond->lock);
+ read_lock(&bond->lock);
info->num_slaves = bond->slave_cnt;
- read_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
return 0;
}
return -ENODEV;
}
- read_lock_bh(&bond->lock);
+ read_lock(&bond->lock);
bond_for_each_slave(bond, slave, i) {
if (i == (int)info->slave_id) {
}
}
- read_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
if (found) {
strcpy(info->slave_name, slave->dev->name);
/*-------------------------------- Monitoring -------------------------------*/
-/* this function is called regularly to monitor each slave's link. */
-void bond_mii_monitor(struct net_device *bond_dev)
+/*
+ * if !have_locks, return nonzero if a failover is necessary. if
+ * have_locks, do whatever failover activities are needed.
+ *
+ * This is to separate the inspection and failover steps for locking
+ * purposes; failover requires rtnl, but acquiring it for every
+ * inspection is undesirable, so a wrapper first does inspection, and
+ * the acquires the necessary locks and calls again to perform
+ * failover if needed. Since all locks are dropped, a complete
+ * restart is needed between calls.
+ */
+static int __bond_mii_monitor(struct bonding *bond, int have_locks)
{
- struct bonding *bond = bond_dev->priv;
struct slave *slave, *oldcurrent;
int do_failover = 0;
- int delta_in_ticks;
- int i;
-
- read_lock(&bond->lock);
-
- delta_in_ticks = (bond->params.miimon * HZ) / 1000;
-
- if (bond->kill_timers) {
- goto out;
- }
-
- if (bond->slave_cnt == 0) {
- goto re_arm;
- }
+ int i;
+
+ if (bond->slave_cnt == 0)
+ goto out;
/* we will try to read the link status of each of our slaves, and
* set their IFF_RUNNING flag appropriately. For each slave not
* program could monitor the link itself if needed.
*/
+ if (bond->send_grat_arp) {
+ if (bond->curr_active_slave && test_bit(__LINK_STATE_LINKWATCH_PENDING,
+ &bond->curr_active_slave->dev->state))
+ dprintk("Needs to send gratuitous arp but not yet\n");
+ else {
+ dprintk("sending delayed gratuitous arp on on %s\n",
+ bond->curr_active_slave->dev->name);
+ bond_send_gratuitous_arp(bond);
+ bond->send_grat_arp = 0;
+ }
+ }
read_lock(&bond->curr_slave_lock);
oldcurrent = bond->curr_active_slave;
read_unlock(&bond->curr_slave_lock);
switch (slave->link) {
case BOND_LINK_UP: /* the link was up */
if (link_state == BMSR_LSTATUS) {
- /* link stays up, nothing more to do */
+ if (!oldcurrent) {
+ if (!have_locks)
+ return 1;
+ do_failover = 1;
+ }
break;
} else { /* link going down */
slave->link = BOND_LINK_FAIL;
": %s: link status down for %s "
"interface %s, disabling it in "
"%d ms.\n",
- bond_dev->name,
+ bond->dev->name,
IS_UP(slave_dev)
? ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
? ((slave == oldcurrent)
if (link_state != BMSR_LSTATUS) {
/* link stays down */
if (slave->delay <= 0) {
+ if (!have_locks)
+ return 1;
+
/* link down for too long time */
slave->link = BOND_LINK_DOWN;
": %s: link status definitely "
"down for interface %s, "
"disabling it\n",
- bond_dev->name,
+ bond->dev->name,
slave_dev->name);
/* notify ad that the link status has changed */
printk(KERN_INFO DRV_NAME
": %s: link status up again after %d "
"ms for interface %s.\n",
- bond_dev->name,
+ bond->dev->name,
(bond->params.downdelay - slave->delay) * bond->params.miimon,
slave_dev->name);
}
": %s: link status up for "
"interface %s, enabling it "
"in %d ms.\n",
- bond_dev->name,
+ bond->dev->name,
slave_dev->name,
bond->params.updelay * bond->params.miimon);
}
printk(KERN_INFO DRV_NAME
": %s: link status down again after %d "
"ms for interface %s.\n",
- bond_dev->name,
+ bond->dev->name,
(bond->params.updelay - slave->delay) * bond->params.miimon,
slave_dev->name);
} else {
/* link stays up */
if (slave->delay == 0) {
+ if (!have_locks)
+ return 1;
+
/* now the link has been up for long time enough */
slave->link = BOND_LINK_UP;
slave->jiffies = jiffies;
printk(KERN_INFO DRV_NAME
": %s: link status definitely "
"up for interface %s.\n",
- bond_dev->name,
+ bond->dev->name,
slave_dev->name);
/* notify ad that the link status has changed */
/* Should not happen */
printk(KERN_ERR DRV_NAME
": %s: Error: %s Illegal value (link=%d)\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name,
slave->link);
goto out;
} /* end of for */
if (do_failover) {
- write_lock(&bond->curr_slave_lock);
+ ASSERT_RTNL();
+
+ write_lock_bh(&bond->curr_slave_lock);
bond_select_active_slave(bond);
- if (oldcurrent && !bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
+ write_unlock_bh(&bond->curr_slave_lock);
- write_unlock(&bond->curr_slave_lock);
- }
+ } else
+ bond_set_carrier(bond);
-re_arm:
- if (bond->params.miimon) {
- mod_timer(&bond->mii_timer, jiffies + delta_in_ticks);
- }
out:
- read_unlock(&bond->lock);
+ return 0;
}
+/*
+ * bond_mii_monitor
+ *
+ * Really a wrapper that splits the mii monitor into two phases: an
+ * inspection, then (if inspection indicates something needs to be
+ * done) an acquisition of appropriate locks followed by another pass
+ * to implement whatever link state changes are indicated.
+ */
+void bond_mii_monitor(struct work_struct *work)
+{
+ struct bonding *bond = container_of(work, struct bonding,
+ mii_work.work);
+ unsigned long delay;
+
+ read_lock(&bond->lock);
+ if (bond->kill_timers) {
+ read_unlock(&bond->lock);
+ return;
+ }
+ if (__bond_mii_monitor(bond, 0)) {
+ read_unlock(&bond->lock);
+ rtnl_lock();
+ read_lock(&bond->lock);
+ __bond_mii_monitor(bond, 1);
+ read_unlock(&bond->lock);
+ rtnl_unlock(); /* might sleep, hold no other locks */
+ read_lock(&bond->lock);
+ }
+
+ delay = ((bond->params.miimon * HZ) / 1000) ? : 1;
+ read_unlock(&bond->lock);
+ queue_delayed_work(bond->wq, &bond->mii_work, delay);
+}
-static u32 bond_glean_dev_ip(struct net_device *dev)
+static __be32 bond_glean_dev_ip(struct net_device *dev)
{
struct in_device *idev;
struct in_ifaddr *ifa;
- u32 addr = 0;
+ __be32 addr = 0;
if (!dev)
return 0;
return 0;
}
+static int bond_has_this_ip(struct bonding *bond, __be32 ip)
+{
+ struct vlan_entry *vlan, *vlan_next;
+
+ if (ip == bond->master_ip)
+ return 1;
+
+ if (list_empty(&bond->vlan_list))
+ return 0;
+
+ list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list,
+ vlan_list) {
+ if (ip == vlan->vlan_ip)
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* 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.
*/
-static void bond_arp_send(struct net_device *slave_dev, int arp_op, u32 dest_ip, u32 src_ip, unsigned short vlan_id)
+static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, unsigned short vlan_id)
{
struct sk_buff *skb;
static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
{
int i, vlan_id, rv;
- u32 *targets = bond->params.arp_targets;
+ __be32 *targets = bond->params.arp_targets;
struct vlan_entry *vlan, *vlan_next;
struct net_device *vlan_dev;
struct flowi fl;
struct rtable *rt;
- for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
+ if (!targets[i])
+ continue;
dprintk("basa: target %x\n", targets[i]);
if (list_empty(&bond->vlan_list)) {
dprintk("basa: empty vlan: arp_send\n");
fl.fl4_dst = targets[i];
fl.fl4_tos = RTO_ONLINK;
- rv = ip_route_output_key(&rt, &fl);
+ rv = ip_route_output_key(&init_net, &rt, &fl);
if (rv) {
if (net_ratelimit()) {
printk(KERN_WARNING DRV_NAME
vlan_id = 0;
list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list,
vlan_list) {
- vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id];
+ vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
if (vlan_dev == rt->u.dst.dev) {
vlan_id = vlan->vlan_id;
dprintk("basa: vlan match on %s %d\n",
if (bond->master_ip) {
bond_arp_send(slave->dev, ARPOP_REPLY, bond->master_ip,
- bond->master_ip, 0);
+ bond->master_ip, 0);
}
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id];
+ vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
if (vlan->vlan_ip) {
bond_arp_send(slave->dev, ARPOP_REPLY, vlan->vlan_ip,
vlan->vlan_ip, vlan->vlan_id);
}
}
+static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
+{
+ int i;
+ __be32 *targets = bond->params.arp_targets;
+
+ targets = bond->params.arp_targets;
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
+ dprintk("bva: sip %u.%u.%u.%u tip %u.%u.%u.%u t[%d] "
+ "%u.%u.%u.%u bhti(tip) %d\n",
+ NIPQUAD(sip), NIPQUAD(tip), i, NIPQUAD(targets[i]),
+ bond_has_this_ip(bond, tip));
+ 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;
+ __be32 sip, tip;
+
+ if (dev_net(dev) != &init_net)
+ goto out;
+
+ if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
+ goto out;
+
+ bond = dev->priv;
+ read_lock(&bond->lock);
+
+ dprintk("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
+ 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;
+
+ if (!pskb_may_pull(skb, arp_hdr_len(dev)))
+ goto out_unlock;
+
+ arp = arp_hdr(skb);
+ 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);
+
+ dprintk("bond_arp_rcv: %s %s/%d av %d sv %d sip %u.%u.%u.%u"
+ " tip %u.%u.%u.%u\n", bond->dev->name, slave->dev->name,
+ slave->state, bond->params.arp_validate,
+ slave_do_arp_validate(bond, slave), NIPQUAD(sip), NIPQUAD(tip));
+
+ /*
+ * 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;
+}
+
/*
* this function is called regularly to monitor each slave's link
* ensuring that traffic is being sent and received when arp monitoring
* arp is transmitted to generate traffic. see activebackup_arp_monitor for
* arp monitoring in active backup mode.
*/
-void bond_loadbalance_arp_mon(struct net_device *bond_dev)
+void bond_loadbalance_arp_mon(struct work_struct *work)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = container_of(work, struct bonding,
+ arp_work.work);
struct slave *slave, *oldcurrent;
int do_failover = 0;
int delta_in_ticks;
*/
bond_for_each_slave(bond, slave, i) {
if (slave->link != BOND_LINK_UP) {
- if (((jiffies - slave->dev->trans_start) <= delta_in_ticks) &&
- ((jiffies - slave->dev->last_rx) <= delta_in_ticks)) {
+ if (time_before_eq(jiffies, slave->dev->trans_start + delta_in_ticks) &&
+ time_before_eq(jiffies, slave->dev->last_rx + delta_in_ticks)) {
slave->link = BOND_LINK_UP;
slave->state = BOND_STATE_ACTIVE;
printk(KERN_INFO DRV_NAME
": %s: link status definitely "
"up for interface %s, ",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
do_failover = 1;
} else {
printk(KERN_INFO DRV_NAME
": %s: interface %s is now up\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
}
}
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (((jiffies - slave->dev->trans_start) >= (2*delta_in_ticks)) ||
- (((jiffies - slave->dev->last_rx) >= (2*delta_in_ticks)) &&
+ if (time_after_eq(jiffies, slave->dev->trans_start + 2*delta_in_ticks) ||
+ (time_after_eq(jiffies, slave->dev->last_rx + 2*delta_in_ticks) &&
bond_has_ip(bond))) {
slave->link = BOND_LINK_DOWN;
printk(KERN_INFO DRV_NAME
": %s: interface %s is now down.\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
if (slave == oldcurrent) {
}
if (do_failover) {
- write_lock(&bond->curr_slave_lock);
+ write_lock_bh(&bond->curr_slave_lock);
bond_select_active_slave(bond);
- if (oldcurrent && !bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
-
- write_unlock(&bond->curr_slave_lock);
+ write_unlock_bh(&bond->curr_slave_lock);
}
re_arm:
- if (bond->params.arp_interval) {
- mod_timer(&bond->arp_timer, jiffies + delta_in_ticks);
- }
+ if (bond->params.arp_interval)
+ queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
out:
read_unlock(&bond->lock);
}
* may have received.
* see loadbalance_arp_monitor for arp monitoring in load balancing mode
*/
-void bond_activebackup_arp_mon(struct net_device *bond_dev)
+void bond_activebackup_arp_mon(struct work_struct *work)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = container_of(work, struct bonding,
+ arp_work.work);
struct slave *slave;
int delta_in_ticks;
int i;
*/
bond_for_each_slave(bond, slave, i) {
if (slave->link != BOND_LINK_UP) {
- if ((jiffies - slave->dev->last_rx) <= delta_in_ticks) {
+ if (time_before_eq(jiffies,
+ slave_last_rx(bond, slave) + delta_in_ticks)) {
slave->link = BOND_LINK_UP;
- write_lock(&bond->curr_slave_lock);
+ write_lock_bh(&bond->curr_slave_lock);
if ((!bond->curr_active_slave) &&
- ((jiffies - slave->dev->trans_start) <= delta_in_ticks)) {
+ time_before_eq(jiffies, slave->dev->trans_start + delta_in_ticks)) {
bond_change_active_slave(bond, slave);
bond->current_arp_slave = NULL;
} else if (bond->curr_active_slave != slave) {
bond->current_arp_slave = NULL;
}
+ bond_set_carrier(bond);
+
if (slave == bond->curr_active_slave) {
printk(KERN_INFO DRV_NAME
": %s: %s is up and now the "
"active interface\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
+ netif_carrier_on(bond->dev);
} else {
printk(KERN_INFO DRV_NAME
": %s: backup interface %s is "
"now up\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
}
- write_unlock(&bond->curr_slave_lock);
+ write_unlock_bh(&bond->curr_slave_lock);
}
} else {
read_lock(&bond->curr_slave_lock);
if ((slave != bond->curr_active_slave) &&
(!bond->current_arp_slave) &&
- (((jiffies - slave->dev->last_rx) >= 3*delta_in_ticks) &&
+ (time_after_eq(jiffies, slave_last_rx(bond, slave) + 3*delta_in_ticks) &&
bond_has_ip(bond))) {
/* a backup slave has gone down; three times
* the delta allows the current slave to be
printk(KERN_INFO DRV_NAME
": %s: backup interface %s is now down\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
} else {
read_unlock(&bond->curr_slave_lock);
* before being taken out. if a primary is being used, check
* if it is up and needs to take over as the curr_active_slave
*/
- if ((((jiffies - slave->dev->trans_start) >= (2*delta_in_ticks)) ||
- (((jiffies - slave->dev->last_rx) >= (2*delta_in_ticks)) &&
- bond_has_ip(bond))) &&
- ((jiffies - slave->jiffies) >= 2*delta_in_ticks)) {
+ if ((time_after_eq(jiffies, slave->dev->trans_start + 2*delta_in_ticks) ||
+ (time_after_eq(jiffies, slave_last_rx(bond, slave) + 2*delta_in_ticks) &&
+ bond_has_ip(bond))) &&
+ time_after_eq(jiffies, slave->jiffies + 2*delta_in_ticks)) {
slave->link = BOND_LINK_DOWN;
printk(KERN_INFO DRV_NAME
": %s: link status down for active interface "
"%s, disabling it\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
- write_lock(&bond->curr_slave_lock);
+ write_lock_bh(&bond->curr_slave_lock);
bond_select_active_slave(bond);
slave = bond->curr_active_slave;
- write_unlock(&bond->curr_slave_lock);
+ write_unlock_bh(&bond->curr_slave_lock);
bond->current_arp_slave = slave;
printk(KERN_INFO DRV_NAME
": %s: changing from interface %s to primary "
"interface %s\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name,
bond->primary_slave->dev->name);
/* primary is up so switch to it */
- write_lock(&bond->curr_slave_lock);
+ write_lock_bh(&bond->curr_slave_lock);
bond_change_active_slave(bond, bond->primary_slave);
- write_unlock(&bond->curr_slave_lock);
+ write_unlock_bh(&bond->curr_slave_lock);
slave = bond->primary_slave;
slave->jiffies = jiffies;
printk(KERN_INFO DRV_NAME
": %s: backup interface %s is "
"now down.\n",
- bond_dev->name,
+ bond->dev->name,
slave->dev->name);
}
}
re_arm:
if (bond->params.arp_interval) {
- mod_timer(&bond->arp_timer, jiffies + delta_in_ticks);
+ queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
}
out:
read_unlock(&bond->lock);
/* make sure the bond won't be taken away */
read_lock(&dev_base_lock);
- read_lock_bh(&bond->lock);
+ read_lock(&bond->lock);
if (*pos == 0) {
return SEQ_START_TOKEN;
{
struct bonding *bond = seq->private;
- read_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
read_unlock(&dev_base_lock);
}
{
struct bonding *bond = seq->private;
struct slave *curr;
+ int i;
+ u32 target;
read_lock(&bond->curr_slave_lock);
curr = bond->curr_active_slave;
read_unlock(&bond->curr_slave_lock);
- seq_printf(seq, "Bonding Mode: %s\n",
+ seq_printf(seq, "Bonding Mode: %s",
bond_mode_name(bond->params.mode));
+ if (bond->params.mode == BOND_MODE_ACTIVEBACKUP &&
+ bond->params.fail_over_mac)
+ seq_printf(seq, " (fail_over_mac)");
+
+ seq_printf(seq, "\n");
+
if (bond->params.mode == BOND_MODE_XOR ||
bond->params.mode == BOND_MODE_8023AD) {
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
(curr) ? curr->dev->name : "None");
}
- seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down");
+ seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ?
+ "up" : "down");
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);
+
+ /* ARP information */
+ if(bond->params.arp_interval > 0) {
+ int printed=0;
+ 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):");
+
+ for(i = 0; (i < BOND_MAX_ARP_TARGETS) ;i++) {
+ if (!bond->params.arp_targets[i])
+ continue;
+ if (printed)
+ seq_printf(seq, ",");
+ target = ntohl(bond->params.arp_targets[i]);
+ seq_printf(seq, " %d.%d.%d.%d", HIPQUAD(target));
+ printed = 1;
+ }
+ seq_printf(seq, "\n");
+ }
+
if (bond->params.mode == BOND_MODE_8023AD) {
struct ad_info ad_info;
+ DECLARE_MAC_BUF(mac);
seq_puts(seq, "\n802.3ad info\n");
seq_printf(seq, "LACP rate: %s\n",
ad_info.actor_key);
seq_printf(seq, "\tPartner Key: %d\n",
ad_info.partner_key);
- seq_printf(seq, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
- ad_info.partner_system[0],
- ad_info.partner_system[1],
- ad_info.partner_system[2],
- ad_info.partner_system[3],
- ad_info.partner_system[4],
- ad_info.partner_system[5]);
+ seq_printf(seq, "\tPartner Mac Address: %s\n",
+ print_mac(mac, ad_info.partner_system));
}
}
}
static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave)
{
struct bonding *bond = seq->private;
+ DECLARE_MAC_BUF(mac);
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Link Failure Count: %d\n",
+ seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
seq_printf(seq,
- "Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
- slave->perm_hwaddr[0], slave->perm_hwaddr[1],
- slave->perm_hwaddr[2], slave->perm_hwaddr[3],
- slave->perm_hwaddr[4], slave->perm_hwaddr[5]);
+ "Permanent HW addr: %s\n",
+ print_mac(mac, slave->perm_hwaddr));
if (bond->params.mode == BOND_MODE_8023AD) {
const struct aggregator *agg
return res;
}
-static struct file_operations bond_info_fops = {
+static const struct file_operations bond_info_fops = {
.owner = THIS_MODULE,
.open = bond_info_open,
.read = seq_read,
{
int len = strlen(DRV_NAME);
- for (bond_proc_dir = proc_net->subdir; bond_proc_dir;
+ for (bond_proc_dir = init_net.proc_net->subdir; bond_proc_dir;
bond_proc_dir = bond_proc_dir->next) {
if ((bond_proc_dir->namelen == len) &&
!memcmp(bond_proc_dir->name, DRV_NAME, len)) {
}
if (!bond_proc_dir) {
- bond_proc_dir = proc_mkdir(DRV_NAME, proc_net);
+ bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net);
if (bond_proc_dir) {
bond_proc_dir->owner = THIS_MODULE;
} else {
bond_proc_dir->owner = NULL;
}
} else {
- remove_proc_entry(DRV_NAME, proc_net);
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
bond_proc_dir = NULL;
}
}
bond_remove_proc_entry(bond);
bond_create_proc_entry(bond);
#endif
-
+ down_write(&(bonding_rwsem));
+ bond_destroy_sysfs_entry(bond);
+ bond_create_sysfs_entry(bond);
+ up_write(&(bonding_rwsem));
return NOTIFY_DONE;
}
case NETDEV_CHANGENAME:
return bond_event_changename(event_bond);
case NETDEV_UNREGISTER:
- /*
- * TODO: remove a bond from the list?
- */
+ bond_release_all(event_bond->dev);
break;
default:
break;
switch (event) {
case NETDEV_UNREGISTER:
if (bond_dev) {
- bond_release(bond_dev, slave_dev);
+ if (bond->setup_by_slave)
+ bond_release_and_destroy(bond_dev, slave_dev);
+ else
+ bond_release(bond_dev, slave_dev);
}
break;
case NETDEV_CHANGE:
* bond_netdev_event: handle netdev notifier chain events.
*
* This function receives events for the netdev chain. The caller (an
- * ioctl handler calling notifier_call_chain) holds the necessary
+ * ioctl handler calling blocking_notifier_call_chain) holds the necessary
* locks for us to safely manipulate the slave devices (RTNL lock,
* dev_probe_lock).
*/
{
struct net_device *event_dev = (struct net_device *)ptr;
+ if (dev_net(event_dev) != &init_net)
+ return NOTIFY_DONE;
+
dprintk("event_dev: %s, event: %lx\n",
(event_dev ? event_dev->name : "None"),
event);
+ if (!(event_dev->priv_flags & IFF_BONDING))
+ return NOTIFY_DONE;
+
if (event_dev->flags & IFF_MASTER) {
dprintk("IFF_MASTER\n");
return bond_master_netdev_event(event, event_dev);
struct bonding *bond, *bond_next;
struct vlan_entry *vlan, *vlan_next;
+ if (dev_net(ifa->ifa_dev->dev) != &init_net)
+ return NOTIFY_DONE;
+
list_for_each_entry_safe(bond, bond_next, &bond_dev_list, bond_list) {
if (bond->dev == event_dev) {
switch (event) {
list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list,
vlan_list) {
- vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id];
+ vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
if (vlan_dev == event_dev) {
switch (event) {
case NETDEV_UP:
dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
}
+void bond_register_arp(struct bonding *bond)
+{
+ struct packet_type *pt = &bond->arp_mon_pt;
+
+ if (pt->type)
+ return;
+
+ pt->type = htons(ETH_P_ARP);
+ pt->dev = bond->dev;
+ pt->func = bond_arp_rcv;
+ dev_add_pack(pt);
+}
+
+void bond_unregister_arp(struct bonding *bond)
+{
+ struct packet_type *pt = &bond->arp_mon_pt;
+
+ dev_remove_pack(pt);
+ pt->type = 0;
+}
+
/*---------------------------- Hashing Policies -----------------------------*/
/*
- * Hash for the the output device based upon layer 3 and layer 4 data. If
+ * 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);
+
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ 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;
+}
+
+/*
+ * Hash for the output device based upon layer 3 and layer 4 data. If
* 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()
*/
struct net_device *bond_dev, int count)
{
struct ethhdr *data = (struct ethhdr *)skb->data;
- struct iphdr *iph = skb->nh.iph;
- u16 *layer4hdr = (u16 *)((u32 *)iph + iph->ihl);
+ struct iphdr *iph = ip_hdr(skb);
+ __be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl);
int layer4_xor = 0;
if (skb->protocol == __constant_htons(ETH_P_IP)) {
if (!(iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) &&
(iph->protocol == IPPROTO_TCP ||
iph->protocol == IPPROTO_UDP)) {
- layer4_xor = htons((*layer4hdr ^ *(layer4hdr + 1)));
+ layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1)));
}
return (layer4_xor ^
((ntohl(iph->saddr ^ iph->daddr)) & 0xffff)) % count;
static int bond_open(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
- struct timer_list *mii_timer = &bond->mii_timer;
- struct timer_list *arp_timer = &bond->arp_timer;
bond->kill_timers = 0;
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
- struct timer_list *alb_timer = &(BOND_ALB_INFO(bond).alb_timer);
-
/* bond_alb_initialize must be called before the timer
* is started.
*/
return -1;
}
- init_timer(alb_timer);
- alb_timer->expires = jiffies + 1;
- alb_timer->data = (unsigned long)bond;
- alb_timer->function = (void *)&bond_alb_monitor;
- add_timer(alb_timer);
+ INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
+ queue_delayed_work(bond->wq, &bond->alb_work, 0);
}
if (bond->params.miimon) { /* link check interval, in milliseconds. */
- init_timer(mii_timer);
- mii_timer->expires = jiffies + 1;
- mii_timer->data = (unsigned long)bond_dev;
- mii_timer->function = (void *)&bond_mii_monitor;
- add_timer(mii_timer);
+ INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
+ queue_delayed_work(bond->wq, &bond->mii_work, 0);
}
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- init_timer(arp_timer);
- arp_timer->expires = jiffies + 1;
- arp_timer->data = (unsigned long)bond_dev;
- if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
- arp_timer->function = (void *)&bond_activebackup_arp_mon;
- } else {
- arp_timer->function = (void *)&bond_loadbalance_arp_mon;
- }
- add_timer(arp_timer);
+ if (bond->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);
+ if (bond->params.arp_validate)
+ bond_register_arp(bond);
}
if (bond->params.mode == BOND_MODE_8023AD) {
- struct timer_list *ad_timer = &(BOND_AD_INFO(bond).ad_timer);
- init_timer(ad_timer);
- ad_timer->expires = jiffies + 1;
- ad_timer->data = (unsigned long)bond;
- ad_timer->function = (void *)&bond_3ad_state_machine_handler;
- add_timer(ad_timer);
-
+ INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
+ queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
bond_register_lacpdu(bond);
}
bond_unregister_lacpdu(bond);
}
+ if (bond->params.arp_validate)
+ bond_unregister_arp(bond);
+
write_lock_bh(&bond->lock);
- bond_mc_list_destroy(bond);
/* signal timers not to re-arm */
bond->kill_timers = 1;
write_unlock_bh(&bond->lock);
- /* del_timer_sync must run without holding the bond->lock
- * because a running timer might be trying to hold it too
- */
-
if (bond->params.miimon) { /* link check interval, in milliseconds. */
- del_timer_sync(&bond->mii_timer);
+ cancel_delayed_work(&bond->mii_work);
}
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
- del_timer_sync(&bond->arp_timer);
+ cancel_delayed_work(&bond->arp_work);
}
switch (bond->params.mode) {
case BOND_MODE_8023AD:
- del_timer_sync(&(BOND_AD_INFO(bond).ad_timer));
+ cancel_delayed_work(&bond->ad_work);
break;
case BOND_MODE_TLB:
case BOND_MODE_ALB:
- del_timer_sync(&(BOND_ALB_INFO(bond).alb_timer));
+ cancel_delayed_work(&bond->alb_work);
break;
default:
break;
}
- /* Release the bonded slaves */
- bond_release_all(bond_dev);
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
{
struct bonding *bond = bond_dev->priv;
struct net_device_stats *stats = &(bond->stats), *sstats;
+ struct net_device_stats local_stats;
struct slave *slave;
int i;
- memset(stats, 0, sizeof(struct net_device_stats));
+ memset(&local_stats, 0, sizeof(struct net_device_stats));
read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, i) {
sstats = slave->dev->get_stats(slave->dev);
+ 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;
- stats->rx_packets += sstats->rx_packets;
- stats->rx_bytes += sstats->rx_bytes;
- stats->rx_errors += sstats->rx_errors;
- stats->rx_dropped += sstats->rx_dropped;
-
- stats->tx_packets += sstats->tx_packets;
- stats->tx_bytes += sstats->tx_bytes;
- stats->tx_errors += sstats->tx_errors;
- stats->tx_dropped += sstats->tx_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;
- stats->multicast += sstats->multicast;
- stats->collisions += sstats->collisions;
+ local_stats.multicast += sstats->multicast;
+ local_stats.collisions += sstats->collisions;
- stats->rx_length_errors += sstats->rx_length_errors;
- stats->rx_over_errors += sstats->rx_over_errors;
- stats->rx_crc_errors += sstats->rx_crc_errors;
- stats->rx_frame_errors += sstats->rx_frame_errors;
- stats->rx_fifo_errors += sstats->rx_fifo_errors;
- stats->rx_missed_errors += sstats->rx_missed_errors;
+ 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;
- stats->tx_aborted_errors += sstats->tx_aborted_errors;
- stats->tx_carrier_errors += sstats->tx_carrier_errors;
- stats->tx_fifo_errors += sstats->tx_fifo_errors;
- stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
- stats->tx_window_errors += sstats->tx_window_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));
+
read_unlock_bh(&bond->lock);
return stats;
if (mii->reg_num == 1) {
struct bonding *bond = bond_dev->priv;
mii->val_out = 0;
- read_lock_bh(&bond->lock);
+ read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
- if (bond->curr_active_slave) {
+ if (netif_carrier_ok(bond->dev)) {
mii->val_out = BMSR_LSTATUS;
}
read_unlock(&bond->curr_slave_lock);
- read_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
}
return 0;
return -EPERM;
}
- slave_dev = dev_get_by_name(ifr->ifr_slave);
+ down_write(&(bonding_rwsem));
+ slave_dev = dev_get_by_name(&init_net, ifr->ifr_slave);
dprintk("slave_dev=%p: \n", slave_dev);
dev_put(slave_dev);
}
+ up_write(&(bonding_rwsem));
return res;
}
struct bonding *bond = bond_dev->priv;
struct dev_mc_list *dmi;
- write_lock_bh(&bond->lock);
-
/*
* Do promisc before checking multicast_mode
*/
bond_set_allmulti(bond, -1);
}
+ read_lock(&bond->lock);
+
bond->flags = bond_dev->flags;
/* looking for addresses to add to slaves' mc list */
bond_mc_list_destroy(bond);
bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC);
- write_unlock_bh(&bond->lock);
+ read_unlock(&bond->lock);
}
/*
bond_for_each_slave(bond, slave, i) {
dprintk("s %p s->p %p c_m %p\n", slave,
slave->prev, slave->dev->change_mtu);
+
res = dev_set_mtu(slave->dev, new_mtu);
if (res) {
dprintk("bond=%p, name=%s\n", bond, (bond_dev ? bond_dev->name : "None"));
+ /*
+ * If fail_over_mac is enabled, do nothing and return success.
+ * Returning an error causes ifenslave to fail.
+ */
+ if (bond->params.fail_over_mac)
+ return 0;
+
if (!is_valid_ether_addr(sa->sa_data)) {
return -EADDRNOTAVAIL;
}
{
struct bonding *bond = bond_dev->priv;
struct slave *slave, *start_at;
- int i;
- int res = 1;
+ int i, slave_no, res = 1;
read_lock(&bond->lock);
goto out;
}
- read_lock(&bond->curr_slave_lock);
- slave = start_at = bond->curr_active_slave;
- read_unlock(&bond->curr_slave_lock);
+ /*
+ * 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;
- if (!slave) {
- goto out;
+ bond_for_each_slave(bond, slave, i) {
+ slave_no--;
+ if (slave_no < 0) {
+ 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);
-
- write_lock(&bond->curr_slave_lock);
- bond->curr_active_slave = slave->next;
- write_unlock(&bond->curr_slave_lock);
-
break;
}
}
-
out:
if (res) {
/* no suitable interface, frame not sent */
return 0;
}
-static void bond_activebackup_xmit_copy(struct sk_buff *skb,
- struct bonding *bond,
- struct slave *slave)
-{
- struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
- struct ethhdr *eth_data;
- u8 *hwaddr;
- int res;
-
- if (!skb2) {
- printk(KERN_ERR DRV_NAME ": Error: "
- "bond_activebackup_xmit_copy(): skb_copy() failed\n");
- return;
- }
-
- skb2->mac.raw = (unsigned char *)skb2->data;
- eth_data = eth_hdr(skb2);
-
- /* Pick an appropriate source MAC address
- * -- use slave's perm MAC addr, unless used by bond
- * -- otherwise, borrow active slave's perm MAC addr
- * since that will not be used
- */
- hwaddr = slave->perm_hwaddr;
- if (!memcmp(eth_data->h_source, hwaddr, ETH_ALEN))
- hwaddr = bond->curr_active_slave->perm_hwaddr;
-
- /* Set source MAC address appropriately */
- memcpy(eth_data->h_source, hwaddr, ETH_ALEN);
-
- res = bond_dev_queue_xmit(bond, skb2, slave->dev);
- if (res)
- dev_kfree_skb(skb2);
-
- return;
-}
/*
* in active-backup mode, we know that bond->curr_active_slave is always valid if
if (!bond->curr_active_slave)
goto out;
- /* Xmit IGMP frames on all slaves to ensure rapid fail-over
- for multicast traffic on snooping switches */
- if (skb->protocol == __constant_htons(ETH_P_IP) &&
- skb->nh.iph->protocol == IPPROTO_IGMP) {
- struct slave *slave, *active_slave;
- int i;
-
- active_slave = bond->curr_active_slave;
- bond_for_each_slave_from_to(bond, slave, i, active_slave->next,
- active_slave->prev)
- if (IS_UP(slave->dev) &&
- (slave->link == BOND_LINK_UP))
- bond_activebackup_xmit_copy(skb, bond, slave);
- }
-
res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev);
out:
/*------------------------- Device initialization ---------------------------*/
+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;
+ }
+}
+
/*
* set bond mode specific net device operations
*/
break;
case BOND_MODE_XOR:
bond_dev->hard_start_xmit = bond_xmit_xor;
- if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34)
- bond->xmit_hash_policy = bond_xmit_hash_policy_l34;
- else
- bond->xmit_hash_policy = bond_xmit_hash_policy_l2;
+ bond_set_xmit_hash_policy(bond);
break;
case BOND_MODE_BROADCAST:
bond_dev->hard_start_xmit = bond_xmit_broadcast;
break;
case BOND_MODE_8023AD:
+ bond_set_master_3ad_flags(bond);
bond_dev->hard_start_xmit = bond_3ad_xmit_xor;
- if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34)
- bond->xmit_hash_policy = bond_xmit_hash_policy_l34;
- else
- bond->xmit_hash_policy = bond_xmit_hash_policy_l2;
+ bond_set_xmit_hash_policy(bond);
break;
- case BOND_MODE_TLB:
case BOND_MODE_ALB:
+ bond_set_master_alb_flags(bond);
+ /* FALLTHRU */
+ case BOND_MODE_TLB:
bond_dev->hard_start_xmit = bond_alb_xmit;
bond_dev->set_mac_address = bond_alb_set_mac_address;
break;
snprintf(drvinfo->fw_version, 32, "%d", BOND_ABI_VERSION);
}
-static struct ethtool_ops bond_ethtool_ops = {
- .get_tx_csum = ethtool_op_get_tx_csum,
- .get_sg = ethtool_op_get_sg,
+static const struct ethtool_ops bond_ethtool_ops = {
.get_drvinfo = bond_ethtool_get_drvinfo,
};
* Does not allocate but creates a /proc entry.
* Allowed to fail.
*/
-static int __init bond_init(struct net_device *bond_dev, struct bond_params *params)
+static int bond_init(struct net_device *bond_dev, struct bond_params *params)
{
struct bonding *bond = bond_dev->priv;
bond->params = *params; /* copy params struct */
+ bond->wq = create_singlethread_workqueue(bond_dev->name);
+ if (!bond->wq)
+ return -ENOMEM;
+
/* Initialize pointers */
bond->first_slave = NULL;
bond->curr_active_slave = NULL;
bond->current_arp_slave = NULL;
bond->primary_slave = NULL;
bond->dev = bond_dev;
+ bond->send_grat_arp = 0;
+ bond->setup_by_slave = 0;
INIT_LIST_HEAD(&bond->vlan_list);
/* Initialize the device entry points */
bond_dev->set_multicast_list = bond_set_multicast_list;
bond_dev->change_mtu = bond_change_mtu;
bond_dev->set_mac_address = bond_set_mac_address;
+ bond_dev->validate_addr = NULL;
bond_set_mode_ops(bond, bond->params.mode);
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
+ bond_dev->priv_flags |= IFF_BONDING;
/* At first, we block adding VLANs. That's the only way to
* prevent problems that occur when adding VLANs over an
*/
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
- /* don't acquire bond device's xmit_lock when
+ /* don't acquire bond device's netif_tx_lock when
* transmitting */
bond_dev->features |= NETIF_F_LLTX;
#ifdef CONFIG_PROC_FS
bond_create_proc_entry(bond);
#endif
-
list_add_tail(&bond->bond_list, &bond_dev_list);
return 0;
/* De-initialize device specific data.
* Caller must hold rtnl_lock.
*/
-void bond_deinit(struct net_device *bond_dev)
+static void bond_deinit(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
#endif
}
+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);
+}
+
/* Unregister and free all bond devices.
* Caller must hold rtnl_lock.
*/
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
struct net_device *bond_dev = bond->dev;
- unregister_netdevice(bond_dev);
- bond_deinit(bond_dev);
+ bond_work_cancel_all(bond);
+ netif_tx_lock_bh(bond_dev);
+ bond_mc_list_destroy(bond);
+ netif_tx_unlock_bh(bond_dev);
+ /* Release the bonded slaves */
+ bond_release_all(bond_dev);
+ bond_destroy(bond);
}
#ifdef CONFIG_PROC_FS
/*
* Convert string input module parms. Accept either the
- * number of the mode or its string name.
+ * 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).
*/
-int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
+int bond_parse_parm(const char *buf, struct bond_parm_tbl *tbl)
{
- int i;
+ int mode = -1, i, rv;
+ char *p, modestr[BOND_MAX_MODENAME_LEN + 1] = { 0, };
+
+ for (p = (char *)buf; *p; p++)
+ if (!(isdigit(*p) || isspace(*p)))
+ break;
+
+ if (*p)
+ rv = sscanf(buf, "%20s", modestr);
+ else
+ rv = sscanf(buf, "%d", &mode);
+
+ if (!rv)
+ return -1;
for (i = 0; tbl[i].modename; i++) {
- if ((isdigit(*mode_arg) &&
- tbl[i].mode == simple_strtol(mode_arg, NULL, 0)) ||
- (strncmp(mode_arg, tbl[i].modename,
- strlen(tbl[i].modename)) == 0)) {
+ if (mode == tbl[i].mode)
+ return tbl[i].mode;
+ if (strcmp(modestr, tbl[i].modename) == 0)
return tbl[i].mode;
- }
}
return -1;
static int bond_check_params(struct bond_params *params)
{
+ int arp_validate_value;
+
/*
* Convert string parameters.
*/
arp_ip_target[arp_ip_count]);
arp_interval = 0;
} else {
- u32 ip = in_aton(arp_ip_target[arp_ip_count]);
+ __be32 ip = in_aton(arp_ip_target[arp_ip_count]);
arp_target[arp_ip_count] = ip;
}
}
arp_interval = 0;
}
+ if (arp_validate) {
+ if (bond_mode != BOND_MODE_ACTIVEBACKUP) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate only supported in active-backup mode\n");
+ return -EINVAL;
+ }
+ if (!arp_interval) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate requires arp_interval\n");
+ return -EINVAL;
+ }
+
+ arp_validate_value = bond_parse_parm(arp_validate,
+ arp_validate_tbl);
+ if (arp_validate_value == -1) {
+ printk(KERN_ERR DRV_NAME
+ ": Error: invalid arp_validate \"%s\"\n",
+ arp_validate == NULL ? "NULL" : arp_validate);
+ return -EINVAL;
+ }
+ } else
+ arp_validate_value = 0;
+
if (miimon) {
printk(KERN_INFO DRV_NAME
": MII link monitoring set to %d ms\n",
int i;
printk(KERN_INFO DRV_NAME
- ": ARP monitoring set to %d ms with %d target(s):",
- arp_interval, arp_ip_count);
+ ": ARP monitoring set to %d ms, validate %s, with %d target(s):",
+ arp_interval,
+ arp_validate_tbl[arp_validate_value].modename,
+ arp_ip_count);
for (i = 0; i < arp_ip_count; i++)
printk (" %s", arp_ip_target[i]);
primary = NULL;
}
+ if (fail_over_mac && (bond_mode != BOND_MODE_ACTIVEBACKUP))
+ printk(KERN_WARNING DRV_NAME
+ ": Warning: fail_over_mac only affects "
+ "active-backup mode.\n");
+
/* fill params struct with the proper values */
params->mode = bond_mode;
params->xmit_policy = xmit_hashtype;
params->miimon = miimon;
params->arp_interval = arp_interval;
+ params->arp_validate = arp_validate_value;
params->updelay = updelay;
params->downdelay = downdelay;
params->use_carrier = use_carrier;
params->lacp_fast = lacp_fast;
params->primary[0] = 0;
+ params->fail_over_mac = fail_over_mac;
if (primary) {
strncpy(params->primary, primary, IFNAMSIZ);
return 0;
}
+static struct lock_class_key bonding_netdev_xmit_lock_key;
+
/* Create a new bond based on the specified name and bonding parameters.
+ * If name is NULL, obtain a suitable "bond%d" name for us.
* Caller must NOT hold rtnl_lock; we need to release it here before we
* set up our sysfs entries.
*/
int bond_create(char *name, struct bond_params *params, struct bonding **newbond)
{
struct net_device *bond_dev;
+ struct bonding *bond, *nxt;
int res;
rtnl_lock();
- bond_dev = alloc_netdev(sizeof(struct bonding), name, ether_setup);
+ down_write(&bonding_rwsem);
+
+ /* Check to see if the bond already exists. */
+ if (name) {
+ list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
+ if (strnicmp(bond->dev->name, name, IFNAMSIZ) == 0) {
+ printk(KERN_ERR DRV_NAME
+ ": cannot add bond %s; it already exists\n",
+ name);
+ res = -EPERM;
+ goto out_rtnl;
+ }
+ }
+
+ bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
+ ether_setup);
if (!bond_dev) {
printk(KERN_ERR DRV_NAME
": %s: eek! can't alloc netdev!\n",
goto out_rtnl;
}
+ if (!name) {
+ res = dev_alloc_name(bond_dev, "bond%d");
+ if (res < 0)
+ goto out_netdev;
+ }
+
/* bond_init() must be called after dev_alloc_name() (for the
* /proc files), but before register_netdevice(), because we
* need to set function pointers.
goto out_netdev;
}
- SET_MODULE_OWNER(bond_dev);
-
res = register_netdevice(bond_dev);
if (res < 0) {
goto out_bond;
}
+
+ lockdep_set_class(&bond_dev->_xmit_lock, &bonding_netdev_xmit_lock_key);
+
if (newbond)
*newbond = bond_dev->priv;
+ netif_carrier_off(bond_dev);
+
+ up_write(&bonding_rwsem);
rtnl_unlock(); /* allows sysfs registration of net device */
- goto done;
+ res = bond_create_sysfs_entry(bond_dev->priv);
+ if (res < 0) {
+ rtnl_lock();
+ down_write(&bonding_rwsem);
+ goto out_bond;
+ }
+
+ return 0;
+
out_bond:
bond_deinit(bond_dev);
out_netdev:
free_netdev(bond_dev);
out_rtnl:
+ up_write(&bonding_rwsem);
rtnl_unlock();
-done:
return res;
}
{
int i;
int res;
- char new_bond_name[8]; /* Enough room for 999 bonds at init. */
+ struct bonding *bond, *nxt;
printk(KERN_INFO "%s", version);
#ifdef CONFIG_PROC_FS
bond_create_proc_dir();
#endif
+
+ init_rwsem(&bonding_rwsem);
+
for (i = 0; i < max_bonds; i++) {
- sprintf(new_bond_name, "bond%d",i);
- res = bond_create(new_bond_name,&bonding_defaults, NULL);
+ res = bond_create(NULL, &bonding_defaults, NULL);
if (res)
goto err;
}
+ res = bond_create_sysfs();
+ if (res)
+ goto err;
+
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
goto out;
err:
+ list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
+ bond_work_cancel_all(bond);
+ destroy_workqueue(bond->wq);
+ }
+
rtnl_lock();
bond_free_all();
+ bond_destroy_sysfs();
rtnl_unlock();
out:
return res;
rtnl_lock();
bond_free_all();
+ bond_destroy_sysfs();
rtnl_unlock();
}