[kernel] r19094 - in dists/sid/linux/debian: . config patches patches/features/all patches/features/all/team

Ben Hutchings benh at alioth.debian.org
Thu Jun 7 06:09:28 UTC 2012


Author: benh
Date: Thu Jun  7 06:09:25 2012
New Revision: 19094

Log:
net: Backport team driver from Linux 3.5-rc1

Added:
   dists/sid/linux/debian/patches/features/all/define-netdev_features_t.patch
   dists/sid/linux/debian/patches/features/all/filter-Allow-to-create-sk-unattached-filters.patch
   dists/sid/linux/debian/patches/features/all/team/
   dists/sid/linux/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch
   dists/sid/linux/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
   dists/sid/linux/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
   dists/sid/linux/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch
   dists/sid/linux/debian/patches/features/all/team/0005-team-add-fix_features.patch
   dists/sid/linux/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch
   dists/sid/linux/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
   dists/sid/linux/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
   dists/sid/linux/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
   dists/sid/linux/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
   dists/sid/linux/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
   dists/sid/linux/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
   dists/sid/linux/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch
   dists/sid/linux/debian/patches/features/all/team/0014-team-add-binary-option-type.patch
   dists/sid/linux/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch
   dists/sid/linux/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch
   dists/sid/linux/debian/patches/features/all/team/0017-team-add-bool-option-type.patch
   dists/sid/linux/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
   dists/sid/linux/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
   dists/sid/linux/debian/patches/features/all/team/0020-team-add-missed-statics.patch
   dists/sid/linux/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
   dists/sid/linux/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch
   dists/sid/linux/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch
Modified:
   dists/sid/linux/debian/changelog
   dists/sid/linux/debian/config/config
   dists/sid/linux/debian/patches/series-all

Modified: dists/sid/linux/debian/changelog
==============================================================================
--- dists/sid/linux/debian/changelog	Thu Jun  7 05:45:57 2012	(r19093)
+++ dists/sid/linux/debian/changelog	Thu Jun  7 06:09:25 2012	(r19094)
@@ -19,6 +19,8 @@
   * Convert patch system to quilt, except for the 'orig' patch series
   * udeb: Build-Depend on kernel-wedge >= 2.84; this allows us to list
     modules as required even if they are built-in in some configurations
+  * filter: Allow to create sk-unattached filters
+  * net: Backport team driver from Linux 3.5-rc1
 
  -- Ben Hutchings <ben at decadent.org.uk>  Sat, 02 Jun 2012 20:31:53 +0100
 

Modified: dists/sid/linux/debian/config/config
==============================================================================
--- dists/sid/linux/debian/config/config	Thu Jun  7 05:45:57 2012	(r19093)
+++ dists/sid/linux/debian/config/config	Thu Jun  7 06:09:25 2012	(r19094)
@@ -2076,6 +2076,14 @@
 CONFIG_SLIP_MODE_SLIP6=y
 
 ##
+## file: drivers/net/team/Kconfig
+##
+CONFIG_NET_TEAM=m
+CONFIG_NET_TEAM_MODE_ROUNDROBIN=m
+CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m
+CONFIG_NET_TEAM_MODE_LOADBALANCE=m
+
+##
 ## file: drivers/net/tokenring/Kconfig
 ##
 CONFIG_TR=y

Added: dists/sid/linux/debian/patches/features/all/define-netdev_features_t.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/define-netdev_features_t.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,18 @@
+From: Ben Hutchings <ben at decadent.org.uk>
+Subject: Define netdev_features_t
+
+This was done in upstream commit
+c8f44affb7244f2ac3e703cab13d55ede27621bb, but we just want the type
+definition for use by backported drivers rather than changing
+everything to use it.
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -950,6 +950,8 @@
+ 						    u32 features);
+ };
+ 
++typedef u32 netdev_features_t;
++
+ /*
+  *	The DEVICE structure.
+  *	Actually, this whole structure is a big mistake.  It mixes I/O

Added: dists/sid/linux/debian/patches/features/all/filter-Allow-to-create-sk-unattached-filters.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/filter-Allow-to-create-sk-unattached-filters.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,120 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Sat, 31 Mar 2012 11:01:19 +0000
+Subject: filter: Allow to create sk-unattached filters
+
+commit 302d663740cfaf2c364df6bb61cd339014ed714c upstream.
+
+Today, BPF filters are bind to sockets. Since BPF machine becomes handy
+for other purposes, this patch allows to create unattached filter.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ include/linux/filter.h |    3 +++
+ net/core/filter.c      |   66 +++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 65 insertions(+), 4 deletions(-)
+
+diff --git a/include/linux/filter.h b/include/linux/filter.h
+index 8eeb205..92dd993 100644
+--- a/include/linux/filter.h
++++ b/include/linux/filter.h
+@@ -153,6 +153,9 @@ static inline unsigned int sk_filter_len(const struct sk_filter *fp)
+ extern int sk_filter(struct sock *sk, struct sk_buff *skb);
+ extern unsigned int sk_run_filter(const struct sk_buff *skb,
+ 				  const struct sock_filter *filter);
++extern int sk_unattached_filter_create(struct sk_filter **pfp,
++				       struct sock_fprog *fprog);
++extern void sk_unattached_filter_destroy(struct sk_filter *fp);
+ extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+ extern int sk_detach_filter(struct sock *sk);
+ extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
+diff --git a/net/core/filter.c b/net/core/filter.c
+index 5dea452..cfbea88 100644
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -587,6 +587,67 @@ void sk_filter_release_rcu(struct rcu_head *rcu)
+ }
+ EXPORT_SYMBOL(sk_filter_release_rcu);
+ 
++static int __sk_prepare_filter(struct sk_filter *fp)
++{
++	int err;
++
++	fp->bpf_func = sk_run_filter;
++
++	err = sk_chk_filter(fp->insns, fp->len);
++	if (err)
++		return err;
++
++	bpf_jit_compile(fp);
++	return 0;
++}
++
++/**
++ *	sk_unattached_filter_create - create an unattached filter
++ *	@fprog: the filter program
++ *	@sk: the socket to use
++ *
++ * Create a filter independent ofr any socket. We first run some
++ * sanity checks on it to make sure it does not explode on us later.
++ * If an error occurs or there is insufficient memory for the filter
++ * a negative errno code is returned. On success the return is zero.
++ */
++int sk_unattached_filter_create(struct sk_filter **pfp,
++				struct sock_fprog *fprog)
++{
++	struct sk_filter *fp;
++	unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
++	int err;
++
++	/* Make sure new filter is there and in the right amounts. */
++	if (fprog->filter == NULL)
++		return -EINVAL;
++
++	fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL);
++	if (!fp)
++		return -ENOMEM;
++	memcpy(fp->insns, fprog->filter, fsize);
++
++	atomic_set(&fp->refcnt, 1);
++	fp->len = fprog->len;
++
++	err = __sk_prepare_filter(fp);
++	if (err)
++		goto free_mem;
++
++	*pfp = fp;
++	return 0;
++free_mem:
++	kfree(fp);
++	return err;
++}
++EXPORT_SYMBOL_GPL(sk_unattached_filter_create);
++
++void sk_unattached_filter_destroy(struct sk_filter *fp)
++{
++	sk_filter_release(fp);
++}
++EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy);
++
+ /**
+  *	sk_attach_filter - attach a socket filter
+  *	@fprog: the filter program
+@@ -617,16 +678,13 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+ 
+ 	atomic_set(&fp->refcnt, 1);
+ 	fp->len = fprog->len;
+-	fp->bpf_func = sk_run_filter;
+ 
+-	err = sk_chk_filter(fp->insns, fp->len);
++	err = __sk_prepare_filter(fp);
+ 	if (err) {
+ 		sk_filter_uncharge(sk, fp);
+ 		return err;
+ 	}
+ 
+-	bpf_jit_compile(fp);
+-
+ 	old_fp = rcu_dereference_protected(sk->sk_filter,
+ 					   sock_owned_by_user(sk));
+ 	rcu_assign_pointer(sk->sk_filter, fp);

Added: dists/sid/linux/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,2322 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Fri, 11 Nov 2011 22:16:48 +0000
+Subject: [01/23] net: introduce ethernet teaming device
+
+commit 3d249d4ca7d0ed6629a135ea1ea21c72286c0d80 upstream.
+
+This patch introduces new network device called team. It supposes to be
+very fast, simple, userspace-driven alternative to existing bonding
+driver.
+
+Userspace library called libteam with couple of demo apps is available
+here:
+https://github.com/jpirko/libteam
+Note it's still in its dipers atm.
+
+team<->libteam use generic netlink for communication. That and rtnl
+suppose to be the only way to configure team device, no sysfs etc.
+
+Python binding of libteam was recently introduced.
+Daemon providing arpmon/miimon active-backup functionality will be
+introduced shortly. All what's necessary is already implemented in
+kernel team driver.
+
+v7->v8:
+	- check ndo_ndo_vlan_rx_[add/kill]_vid functions before calling
+	  them.
+	- use dev_kfree_skb_any() instead of dev_kfree_skb()
+
+v6->v7:
+	- transmit and receive functions are not checked in hot paths.
+	  That also resolves memory leak on transmit when no port is
+	  present
+
+v5->v6:
+	- changed couple of _rcu calls to non _rcu ones in non-readers
+
+v4->v5:
+	- team_change_mtu() uses team->lock while travesing though port
+	  list
+	- mac address changes are moved completely to jurisdiction of
+	  userspace daemon. This way the daemon can do FOM1, FOM2 and
+	  possibly other weird things with mac addresses.
+	  Only round-robin mode sets up all ports to bond's address then
+	  enslaved.
+	- Extended Kconfig text
+
+v3->v4:
+	- remove redundant synchronize_rcu from __team_change_mode()
+	- revert "set and clear of mode_ops happens per pointer, not per
+	  byte"
+	- extend comment of function __team_change_mode()
+
+v2->v3:
+	- team_change_mtu() uses rcu version of list traversal to unwind
+	- set and clear of mode_ops happens per pointer, not per byte
+	- port hashlist changed to be embedded into team structure
+	- error branch in team_port_enter() does cleanup now
+	- fixed rtln->rtnl
+
+v1->v2:
+	- modes are made as modules. Makes team more modular and
+	  extendable.
+	- several commenters' nitpicks found on v1 were fixed
+	- several other bugs were fixed.
+	- note I ignored Eric's comment about roundrobin port selector
+	  as Eric's way may be easily implemented as another mode (mode
+	  "random") in future.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ Documentation/networking/team.txt         |    2 +
+ MAINTAINERS                               |    7 +
+ drivers/net/Kconfig                       |    2 +
+ drivers/net/Makefile                      |    1 +
+ drivers/net/team/Kconfig                  |   43 +
+ drivers/net/team/Makefile                 |    7 +
+ drivers/net/team/team.c                   | 1583 +++++++++++++++++++++++++++++
+ drivers/net/team/team_mode_activebackup.c |  137 +++
+ drivers/net/team/team_mode_roundrobin.c   |  107 ++
+ include/linux/Kbuild                      |    1 +
+ include/linux/if.h                        |    1 +
+ include/linux/if_team.h                   |  242 +++++
+ 12 files changed, 2133 insertions(+)
+ create mode 100644 Documentation/networking/team.txt
+ create mode 100644 drivers/net/team/Kconfig
+ create mode 100644 drivers/net/team/Makefile
+ create mode 100644 drivers/net/team/team.c
+ create mode 100644 drivers/net/team/team_mode_activebackup.c
+ create mode 100644 drivers/net/team/team_mode_roundrobin.c
+ create mode 100644 include/linux/if_team.h
+
+diff --git a/Documentation/networking/team.txt b/Documentation/networking/team.txt
+new file mode 100644
+index 0000000..5a01368
+--- /dev/null
++++ b/Documentation/networking/team.txt
+@@ -0,0 +1,2 @@
++Team devices are driven from userspace via libteam library which is here:
++	https://github.com/jpirko/libteam
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 4808256..8d94169 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -6484,6 +6484,13 @@ W:	http://tcp-lp-mod.sourceforge.net/
+ S:	Maintained
+ F:	net/ipv4/tcp_lp.c
+ 
++TEAM DRIVER
++M:	Jiri Pirko <jpirko at redhat.com>
++L:	netdev at vger.kernel.org
++S:	Supported
++F:	drivers/net/team/
++F:	include/linux/if_team.h
++
+ TEGRA SUPPORT
+ M:	Colin Cross <ccross at android.com>
+ M:	Olof Johansson <olof at lixom.net>
+diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
+index 583f66c..b3020be 100644
+--- a/drivers/net/Kconfig
++++ b/drivers/net/Kconfig
+@@ -125,6 +125,8 @@ config IFB
+ 	  'ifb1' etc.
+ 	  Look at the iproute2 documentation directory for usage etc
+ 
++source "drivers/net/team/Kconfig"
++
+ config MACVLAN
+ 	tristate "MAC-VLAN support (EXPERIMENTAL)"
+ 	depends on EXPERIMENTAL
+diff --git a/drivers/net/Makefile b/drivers/net/Makefile
+index fa877cd..4e4ebfe 100644
+--- a/drivers/net/Makefile
++++ b/drivers/net/Makefile
+@@ -17,6 +17,7 @@ obj-$(CONFIG_NET) += Space.o loopback.o
+ obj-$(CONFIG_NETCONSOLE) += netconsole.o
+ obj-$(CONFIG_PHYLIB) += phy/
+ obj-$(CONFIG_RIONET) += rionet.o
++obj-$(CONFIG_NET_TEAM) += team/
+ obj-$(CONFIG_TUN) += tun.o
+ obj-$(CONFIG_VETH) += veth.o
+ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
+diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
+new file mode 100644
+index 0000000..248a144
+--- /dev/null
++++ b/drivers/net/team/Kconfig
+@@ -0,0 +1,43 @@
++menuconfig NET_TEAM
++	tristate "Ethernet team driver support (EXPERIMENTAL)"
++	depends on EXPERIMENTAL
++	---help---
++	  This allows one to create virtual interfaces that teams together
++	  multiple ethernet devices.
++
++	  Team devices can be added using the "ip" command from the
++	  iproute2 package:
++
++	  "ip link add link [ address MAC ] [ NAME ] type team"
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called team.
++
++if NET_TEAM
++
++config NET_TEAM_MODE_ROUNDROBIN
++	tristate "Round-robin mode support"
++	depends on NET_TEAM
++	---help---
++	  Basic mode where port used for transmitting packets is selected in
++	  round-robin fashion using packet counter.
++
++	  All added ports are setup to have bond's mac address.
++
++	  To compile this team mode as a module, choose M here: the module
++	  will be called team_mode_roundrobin.
++
++config NET_TEAM_MODE_ACTIVEBACKUP
++	tristate "Active-backup mode support"
++	depends on NET_TEAM
++	---help---
++	  Only one port is active at a time and the rest of ports are used
++	  for backup.
++
++	  Mac addresses of ports are not modified. Userspace is responsible
++	  to do so.
++
++	  To compile this team mode as a module, choose M here: the module
++	  will be called team_mode_activebackup.
++
++endif # NET_TEAM
+diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
+new file mode 100644
+index 0000000..85f2028
+--- /dev/null
++++ b/drivers/net/team/Makefile
+@@ -0,0 +1,7 @@
++#
++# Makefile for the network team driver
++#
++
++obj-$(CONFIG_NET_TEAM) += team.o
++obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
++obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+new file mode 100644
+index 0000000..60672bb
+--- /dev/null
++++ b/drivers/net/team/team.c
+@@ -0,0 +1,1583 @@
++/*
++ * net/drivers/team/team.c - Network team device driver
++ * Copyright (c) 2011 Jiri Pirko <jpirko at redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/rcupdate.h>
++#include <linux/errno.h>
++#include <linux/ctype.h>
++#include <linux/notifier.h>
++#include <linux/netdevice.h>
++#include <linux/if_arp.h>
++#include <linux/socket.h>
++#include <linux/etherdevice.h>
++#include <linux/rtnetlink.h>
++#include <net/rtnetlink.h>
++#include <net/genetlink.h>
++#include <net/netlink.h>
++#include <linux/if_team.h>
++
++#define DRV_NAME "team"
++
++
++/**********
++ * Helpers
++ **********/
++
++#define team_port_exists(dev) (dev->priv_flags & IFF_TEAM_PORT)
++
++static struct team_port *team_port_get_rcu(const struct net_device *dev)
++{
++	struct team_port *port = rcu_dereference(dev->rx_handler_data);
++
++	return team_port_exists(dev) ? port : NULL;
++}
++
++static struct team_port *team_port_get_rtnl(const struct net_device *dev)
++{
++	struct team_port *port = rtnl_dereference(dev->rx_handler_data);
++
++	return team_port_exists(dev) ? port : NULL;
++}
++
++/*
++ * Since the ability to change mac address for open port device is tested in
++ * team_port_add, this function can be called without control of return value
++ */
++static int __set_port_mac(struct net_device *port_dev,
++			  const unsigned char *dev_addr)
++{
++	struct sockaddr addr;
++
++	memcpy(addr.sa_data, dev_addr, ETH_ALEN);
++	addr.sa_family = ARPHRD_ETHER;
++	return dev_set_mac_address(port_dev, &addr);
++}
++
++int team_port_set_orig_mac(struct team_port *port)
++{
++	return __set_port_mac(port->dev, port->orig.dev_addr);
++}
++
++int team_port_set_team_mac(struct team_port *port)
++{
++	return __set_port_mac(port->dev, port->team->dev->dev_addr);
++}
++EXPORT_SYMBOL(team_port_set_team_mac);
++
++
++/*******************
++ * Options handling
++ *******************/
++
++void team_options_register(struct team *team, struct team_option *option,
++			   size_t option_count)
++{
++	int i;
++
++	for (i = 0; i < option_count; i++, option++)
++		list_add_tail(&option->list, &team->option_list);
++}
++EXPORT_SYMBOL(team_options_register);
++
++static void __team_options_change_check(struct team *team,
++					struct team_option *changed_option);
++
++static void __team_options_unregister(struct team *team,
++				      struct team_option *option,
++				      size_t option_count)
++{
++	int i;
++
++	for (i = 0; i < option_count; i++, option++)
++		list_del(&option->list);
++}
++
++void team_options_unregister(struct team *team, struct team_option *option,
++			     size_t option_count)
++{
++	__team_options_unregister(team, option, option_count);
++	__team_options_change_check(team, NULL);
++}
++EXPORT_SYMBOL(team_options_unregister);
++
++static int team_option_get(struct team *team, struct team_option *option,
++			   void *arg)
++{
++	return option->getter(team, arg);
++}
++
++static int team_option_set(struct team *team, struct team_option *option,
++			   void *arg)
++{
++	int err;
++
++	err = option->setter(team, arg);
++	if (err)
++		return err;
++
++	__team_options_change_check(team, option);
++	return err;
++}
++
++/****************
++ * Mode handling
++ ****************/
++
++static LIST_HEAD(mode_list);
++static DEFINE_SPINLOCK(mode_list_lock);
++
++static struct team_mode *__find_mode(const char *kind)
++{
++	struct team_mode *mode;
++
++	list_for_each_entry(mode, &mode_list, list) {
++		if (strcmp(mode->kind, kind) == 0)
++			return mode;
++	}
++	return NULL;
++}
++
++static bool is_good_mode_name(const char *name)
++{
++	while (*name != '\0') {
++		if (!isalpha(*name) && !isdigit(*name) && *name != '_')
++			return false;
++		name++;
++	}
++	return true;
++}
++
++int team_mode_register(struct team_mode *mode)
++{
++	int err = 0;
++
++	if (!is_good_mode_name(mode->kind) ||
++	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
++		return -EINVAL;
++	spin_lock(&mode_list_lock);
++	if (__find_mode(mode->kind)) {
++		err = -EEXIST;
++		goto unlock;
++	}
++	list_add_tail(&mode->list, &mode_list);
++unlock:
++	spin_unlock(&mode_list_lock);
++	return err;
++}
++EXPORT_SYMBOL(team_mode_register);
++
++int team_mode_unregister(struct team_mode *mode)
++{
++	spin_lock(&mode_list_lock);
++	list_del_init(&mode->list);
++	spin_unlock(&mode_list_lock);
++	return 0;
++}
++EXPORT_SYMBOL(team_mode_unregister);
++
++static struct team_mode *team_mode_get(const char *kind)
++{
++	struct team_mode *mode;
++
++	spin_lock(&mode_list_lock);
++	mode = __find_mode(kind);
++	if (!mode) {
++		spin_unlock(&mode_list_lock);
++		request_module("team-mode-%s", kind);
++		spin_lock(&mode_list_lock);
++		mode = __find_mode(kind);
++	}
++	if (mode)
++		if (!try_module_get(mode->owner))
++			mode = NULL;
++
++	spin_unlock(&mode_list_lock);
++	return mode;
++}
++
++static void team_mode_put(const struct team_mode *mode)
++{
++	module_put(mode->owner);
++}
++
++static bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
++{
++	dev_kfree_skb_any(skb);
++	return false;
++}
++
++rx_handler_result_t team_dummy_receive(struct team *team,
++				       struct team_port *port,
++				       struct sk_buff *skb)
++{
++	return RX_HANDLER_ANOTHER;
++}
++
++static void team_adjust_ops(struct team *team)
++{
++	/*
++	 * To avoid checks in rx/tx skb paths, ensure here that non-null and
++	 * correct ops are always set.
++	 */
++
++	if (list_empty(&team->port_list) ||
++	    !team->mode || !team->mode->ops->transmit)
++		team->ops.transmit = team_dummy_transmit;
++	else
++		team->ops.transmit = team->mode->ops->transmit;
++
++	if (list_empty(&team->port_list) ||
++	    !team->mode || !team->mode->ops->receive)
++		team->ops.receive = team_dummy_receive;
++	else
++		team->ops.receive = team->mode->ops->receive;
++}
++
++/*
++ * We can benefit from the fact that it's ensured no port is present
++ * at the time of mode change. Therefore no packets are in fly so there's no
++ * need to set mode operations in any special way.
++ */
++static int __team_change_mode(struct team *team,
++			      const struct team_mode *new_mode)
++{
++	/* Check if mode was previously set and do cleanup if so */
++	if (team->mode) {
++		void (*exit_op)(struct team *team) = team->ops.exit;
++
++		/* Clear ops area so no callback is called any longer */
++		memset(&team->ops, 0, sizeof(struct team_mode_ops));
++		team_adjust_ops(team);
++
++		if (exit_op)
++			exit_op(team);
++		team_mode_put(team->mode);
++		team->mode = NULL;
++		/* zero private data area */
++		memset(&team->mode_priv, 0,
++		       sizeof(struct team) - offsetof(struct team, mode_priv));
++	}
++
++	if (!new_mode)
++		return 0;
++
++	if (new_mode->ops->init) {
++		int err;
++
++		err = new_mode->ops->init(team);
++		if (err)
++			return err;
++	}
++
++	team->mode = new_mode;
++	memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
++	team_adjust_ops(team);
++
++	return 0;
++}
++
++static int team_change_mode(struct team *team, const char *kind)
++{
++	struct team_mode *new_mode;
++	struct net_device *dev = team->dev;
++	int err;
++
++	if (!list_empty(&team->port_list)) {
++		netdev_err(dev, "No ports can be present during mode change\n");
++		return -EBUSY;
++	}
++
++	if (team->mode && strcmp(team->mode->kind, kind) == 0) {
++		netdev_err(dev, "Unable to change to the same mode the team is in\n");
++		return -EINVAL;
++	}
++
++	new_mode = team_mode_get(kind);
++	if (!new_mode) {
++		netdev_err(dev, "Mode \"%s\" not found\n", kind);
++		return -EINVAL;
++	}
++
++	err = __team_change_mode(team, new_mode);
++	if (err) {
++		netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
++		team_mode_put(new_mode);
++		return err;
++	}
++
++	netdev_info(dev, "Mode changed to \"%s\"\n", kind);
++	return 0;
++}
++
++
++/************************
++ * Rx path frame handler
++ ************************/
++
++/* note: already called with rcu_read_lock */
++static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
++{
++	struct sk_buff *skb = *pskb;
++	struct team_port *port;
++	struct team *team;
++	rx_handler_result_t res;
++
++	skb = skb_share_check(skb, GFP_ATOMIC);
++	if (!skb)
++		return RX_HANDLER_CONSUMED;
++
++	*pskb = skb;
++
++	port = team_port_get_rcu(skb->dev);
++	team = port->team;
++
++	res = team->ops.receive(team, port, skb);
++	if (res == RX_HANDLER_ANOTHER) {
++		struct team_pcpu_stats *pcpu_stats;
++
++		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
++		u64_stats_update_begin(&pcpu_stats->syncp);
++		pcpu_stats->rx_packets++;
++		pcpu_stats->rx_bytes += skb->len;
++		if (skb->pkt_type == PACKET_MULTICAST)
++			pcpu_stats->rx_multicast++;
++		u64_stats_update_end(&pcpu_stats->syncp);
++
++		skb->dev = team->dev;
++	} else {
++		this_cpu_inc(team->pcpu_stats->rx_dropped);
++	}
++
++	return res;
++}
++
++
++/****************
++ * Port handling
++ ****************/
++
++static bool team_port_find(const struct team *team,
++			   const struct team_port *port)
++{
++	struct team_port *cur;
++
++	list_for_each_entry(cur, &team->port_list, list)
++		if (cur == port)
++			return true;
++	return false;
++}
++
++/*
++ * Add/delete port to the team port list. Write guarded by rtnl_lock.
++ * Takes care of correct port->index setup (might be racy).
++ */
++static void team_port_list_add_port(struct team *team,
++				    struct team_port *port)
++{
++	port->index = team->port_count++;
++	hlist_add_head_rcu(&port->hlist,
++			   team_port_index_hash(team, port->index));
++	list_add_tail_rcu(&port->list, &team->port_list);
++}
++
++static void __reconstruct_port_hlist(struct team *team, int rm_index)
++{
++	int i;
++	struct team_port *port;
++
++	for (i = rm_index + 1; i < team->port_count; i++) {
++		port = team_get_port_by_index(team, i);
++		hlist_del_rcu(&port->hlist);
++		port->index--;
++		hlist_add_head_rcu(&port->hlist,
++				   team_port_index_hash(team, port->index));
++	}
++}
++
++static void team_port_list_del_port(struct team *team,
++				   struct team_port *port)
++{
++	int rm_index = port->index;
++
++	hlist_del_rcu(&port->hlist);
++	list_del_rcu(&port->list);
++	__reconstruct_port_hlist(team, rm_index);
++	team->port_count--;
++}
++
++#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
++			    NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
++			    NETIF_F_HIGHDMA | NETIF_F_LRO)
++
++static void __team_compute_features(struct team *team)
++{
++	struct team_port *port;
++	u32 vlan_features = TEAM_VLAN_FEATURES;
++	unsigned short max_hard_header_len = ETH_HLEN;
++
++	list_for_each_entry(port, &team->port_list, list) {
++		vlan_features = netdev_increment_features(vlan_features,
++					port->dev->vlan_features,
++					TEAM_VLAN_FEATURES);
++
++		if (port->dev->hard_header_len > max_hard_header_len)
++			max_hard_header_len = port->dev->hard_header_len;
++	}
++
++	team->dev->vlan_features = vlan_features;
++	team->dev->hard_header_len = max_hard_header_len;
++
++	netdev_change_features(team->dev);
++}
++
++static void team_compute_features(struct team *team)
++{
++	spin_lock(&team->lock);
++	__team_compute_features(team);
++	spin_unlock(&team->lock);
++}
++
++static int team_port_enter(struct team *team, struct team_port *port)
++{
++	int err = 0;
++
++	dev_hold(team->dev);
++	port->dev->priv_flags |= IFF_TEAM_PORT;
++	if (team->ops.port_enter) {
++		err = team->ops.port_enter(team, port);
++		if (err) {
++			netdev_err(team->dev, "Device %s failed to enter team mode\n",
++				   port->dev->name);
++			goto err_port_enter;
++		}
++	}
++
++	return 0;
++
++err_port_enter:
++	port->dev->priv_flags &= ~IFF_TEAM_PORT;
++	dev_put(team->dev);
++
++	return err;
++}
++
++static void team_port_leave(struct team *team, struct team_port *port)
++{
++	if (team->ops.port_leave)
++		team->ops.port_leave(team, port);
++	port->dev->priv_flags &= ~IFF_TEAM_PORT;
++	dev_put(team->dev);
++}
++
++static void __team_port_change_check(struct team_port *port, bool linkup);
++
++static int team_port_add(struct team *team, struct net_device *port_dev)
++{
++	struct net_device *dev = team->dev;
++	struct team_port *port;
++	char *portname = port_dev->name;
++	int err;
++
++	if (port_dev->flags & IFF_LOOPBACK ||
++	    port_dev->type != ARPHRD_ETHER) {
++		netdev_err(dev, "Device %s is of an unsupported type\n",
++			   portname);
++		return -EINVAL;
++	}
++
++	if (team_port_exists(port_dev)) {
++		netdev_err(dev, "Device %s is already a port "
++				"of a team device\n", portname);
++		return -EBUSY;
++	}
++
++	if (port_dev->flags & IFF_UP) {
++		netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
++			   portname);
++		return -EBUSY;
++	}
++
++	port = kzalloc(sizeof(struct team_port), GFP_KERNEL);
++	if (!port)
++		return -ENOMEM;
++
++	port->dev = port_dev;
++	port->team = team;
++
++	port->orig.mtu = port_dev->mtu;
++	err = dev_set_mtu(port_dev, dev->mtu);
++	if (err) {
++		netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
++		goto err_set_mtu;
++	}
++
++	memcpy(port->orig.dev_addr, port_dev->dev_addr, ETH_ALEN);
++
++	err = team_port_enter(team, port);
++	if (err) {
++		netdev_err(dev, "Device %s failed to enter team mode\n",
++			   portname);
++		goto err_port_enter;
++	}
++
++	err = dev_open(port_dev);
++	if (err) {
++		netdev_dbg(dev, "Device %s opening failed\n",
++			   portname);
++		goto err_dev_open;
++	}
++
++	err = netdev_set_master(port_dev, dev);
++	if (err) {
++		netdev_err(dev, "Device %s failed to set master\n", portname);
++		goto err_set_master;
++	}
++
++	err = netdev_rx_handler_register(port_dev, team_handle_frame,
++					 port);
++	if (err) {
++		netdev_err(dev, "Device %s failed to register rx_handler\n",
++			   portname);
++		goto err_handler_register;
++	}
++
++	team_port_list_add_port(team, port);
++	team_adjust_ops(team);
++	__team_compute_features(team);
++	__team_port_change_check(port, !!netif_carrier_ok(port_dev));
++
++	netdev_info(dev, "Port device %s added\n", portname);
++
++	return 0;
++
++err_handler_register:
++	netdev_set_master(port_dev, NULL);
++
++err_set_master:
++	dev_close(port_dev);
++
++err_dev_open:
++	team_port_leave(team, port);
++	team_port_set_orig_mac(port);
++
++err_port_enter:
++	dev_set_mtu(port_dev, port->orig.mtu);
++
++err_set_mtu:
++	kfree(port);
++
++	return err;
++}
++
++static int team_port_del(struct team *team, struct net_device *port_dev)
++{
++	struct net_device *dev = team->dev;
++	struct team_port *port;
++	char *portname = port_dev->name;
++
++	port = team_port_get_rtnl(port_dev);
++	if (!port || !team_port_find(team, port)) {
++		netdev_err(dev, "Device %s does not act as a port of this team\n",
++			   portname);
++		return -ENOENT;
++	}
++
++	__team_port_change_check(port, false);
++	team_port_list_del_port(team, port);
++	team_adjust_ops(team);
++	netdev_rx_handler_unregister(port_dev);
++	netdev_set_master(port_dev, NULL);
++	dev_close(port_dev);
++	team_port_leave(team, port);
++	team_port_set_orig_mac(port);
++	dev_set_mtu(port_dev, port->orig.mtu);
++	synchronize_rcu();
++	kfree(port);
++	netdev_info(dev, "Port device %s removed\n", portname);
++	__team_compute_features(team);
++
++	return 0;
++}
++
++
++/*****************
++ * Net device ops
++ *****************/
++
++static const char team_no_mode_kind[] = "*NOMODE*";
++
++static int team_mode_option_get(struct team *team, void *arg)
++{
++	const char **str = arg;
++
++	*str = team->mode ? team->mode->kind : team_no_mode_kind;
++	return 0;
++}
++
++static int team_mode_option_set(struct team *team, void *arg)
++{
++	const char **str = arg;
++
++	return team_change_mode(team, *str);
++}
++
++static struct team_option team_options[] = {
++	{
++		.name = "mode",
++		.type = TEAM_OPTION_TYPE_STRING,
++		.getter = team_mode_option_get,
++		.setter = team_mode_option_set,
++	},
++};
++
++static int team_init(struct net_device *dev)
++{
++	struct team *team = netdev_priv(dev);
++	int i;
++
++	team->dev = dev;
++	spin_lock_init(&team->lock);
++
++	team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
++	if (!team->pcpu_stats)
++		return -ENOMEM;
++
++	for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
++		INIT_HLIST_HEAD(&team->port_hlist[i]);
++	INIT_LIST_HEAD(&team->port_list);
++
++	team_adjust_ops(team);
++
++	INIT_LIST_HEAD(&team->option_list);
++	team_options_register(team, team_options, ARRAY_SIZE(team_options));
++	netif_carrier_off(dev);
++
++	return 0;
++}
++
++static void team_uninit(struct net_device *dev)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++	struct team_port *tmp;
++
++	spin_lock(&team->lock);
++	list_for_each_entry_safe(port, tmp, &team->port_list, list)
++		team_port_del(team, port->dev);
++
++	__team_change_mode(team, NULL); /* cleanup */
++	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
++	spin_unlock(&team->lock);
++}
++
++static void team_destructor(struct net_device *dev)
++{
++	struct team *team = netdev_priv(dev);
++
++	free_percpu(team->pcpu_stats);
++	free_netdev(dev);
++}
++
++static int team_open(struct net_device *dev)
++{
++	netif_carrier_on(dev);
++	return 0;
++}
++
++static int team_close(struct net_device *dev)
++{
++	netif_carrier_off(dev);
++	return 0;
++}
++
++/*
++ * note: already called with rcu_read_lock
++ */
++static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++	struct team *team = netdev_priv(dev);
++	bool tx_success = false;
++	unsigned int len = skb->len;
++
++	tx_success = team->ops.transmit(team, skb);
++	if (tx_success) {
++		struct team_pcpu_stats *pcpu_stats;
++
++		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
++		u64_stats_update_begin(&pcpu_stats->syncp);
++		pcpu_stats->tx_packets++;
++		pcpu_stats->tx_bytes += len;
++		u64_stats_update_end(&pcpu_stats->syncp);
++	} else {
++		this_cpu_inc(team->pcpu_stats->tx_dropped);
++	}
++
++	return NETDEV_TX_OK;
++}
++
++static void team_change_rx_flags(struct net_device *dev, int change)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++	int inc;
++
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		if (change & IFF_PROMISC) {
++			inc = dev->flags & IFF_PROMISC ? 1 : -1;
++			dev_set_promiscuity(port->dev, inc);
++		}
++		if (change & IFF_ALLMULTI) {
++			inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
++			dev_set_allmulti(port->dev, inc);
++		}
++	}
++	rcu_read_unlock();
++}
++
++static void team_set_rx_mode(struct net_device *dev)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		dev_uc_sync(port->dev, dev);
++		dev_mc_sync(port->dev, dev);
++	}
++	rcu_read_unlock();
++}
++
++static int team_set_mac_address(struct net_device *dev, void *p)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++	struct sockaddr *addr = p;
++
++	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list)
++		if (team->ops.port_change_mac)
++			team->ops.port_change_mac(team, port);
++	rcu_read_unlock();
++	return 0;
++}
++
++static int team_change_mtu(struct net_device *dev, int new_mtu)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++	int err;
++
++	/*
++	 * Alhough this is reader, it's guarded by team lock. It's not possible
++	 * to traverse list in reverse under rcu_read_lock
++	 */
++	spin_lock(&team->lock);
++	list_for_each_entry(port, &team->port_list, list) {
++		err = dev_set_mtu(port->dev, new_mtu);
++		if (err) {
++			netdev_err(dev, "Device %s failed to change mtu",
++				   port->dev->name);
++			goto unwind;
++		}
++	}
++	spin_unlock(&team->lock);
++
++	dev->mtu = new_mtu;
++
++	return 0;
++
++unwind:
++	list_for_each_entry_continue_reverse(port, &team->port_list, list)
++		dev_set_mtu(port->dev, dev->mtu);
++	spin_unlock(&team->lock);
++
++	return err;
++}
++
++static struct rtnl_link_stats64 *
++team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_pcpu_stats *p;
++	u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
++	u32 rx_dropped = 0, tx_dropped = 0;
++	unsigned int start;
++	int i;
++
++	for_each_possible_cpu(i) {
++		p = per_cpu_ptr(team->pcpu_stats, i);
++		do {
++			start = u64_stats_fetch_begin_bh(&p->syncp);
++			rx_packets	= p->rx_packets;
++			rx_bytes	= p->rx_bytes;
++			rx_multicast	= p->rx_multicast;
++			tx_packets	= p->tx_packets;
++			tx_bytes	= p->tx_bytes;
++		} while (u64_stats_fetch_retry_bh(&p->syncp, start));
++
++		stats->rx_packets	+= rx_packets;
++		stats->rx_bytes		+= rx_bytes;
++		stats->multicast	+= rx_multicast;
++		stats->tx_packets	+= tx_packets;
++		stats->tx_bytes		+= tx_bytes;
++		/*
++		 * rx_dropped & tx_dropped are u32, updated
++		 * without syncp protection.
++		 */
++		rx_dropped	+= p->rx_dropped;
++		tx_dropped	+= p->tx_dropped;
++	}
++	stats->rx_dropped	= rx_dropped;
++	stats->tx_dropped	= tx_dropped;
++	return stats;
++}
++
++static void team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		const struct net_device_ops *ops = port->dev->netdev_ops;
++
++		if (ops->ndo_vlan_rx_add_vid)
++			ops->ndo_vlan_rx_add_vid(port->dev, vid);
++	}
++	rcu_read_unlock();
++}
++
++static void team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
++{
++	struct team *team = netdev_priv(dev);
++	struct team_port *port;
++
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		const struct net_device_ops *ops = port->dev->netdev_ops;
++
++		if (ops->ndo_vlan_rx_kill_vid)
++			ops->ndo_vlan_rx_kill_vid(port->dev, vid);
++	}
++	rcu_read_unlock();
++}
++
++static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
++{
++	struct team *team = netdev_priv(dev);
++	int err;
++
++	spin_lock(&team->lock);
++	err = team_port_add(team, port_dev);
++	spin_unlock(&team->lock);
++	return err;
++}
++
++static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
++{
++	struct team *team = netdev_priv(dev);
++	int err;
++
++	spin_lock(&team->lock);
++	err = team_port_del(team, port_dev);
++	spin_unlock(&team->lock);
++	return err;
++}
++
++static const struct net_device_ops team_netdev_ops = {
++	.ndo_init		= team_init,
++	.ndo_uninit		= team_uninit,
++	.ndo_open		= team_open,
++	.ndo_stop		= team_close,
++	.ndo_start_xmit		= team_xmit,
++	.ndo_change_rx_flags	= team_change_rx_flags,
++	.ndo_set_rx_mode	= team_set_rx_mode,
++	.ndo_set_mac_address	= team_set_mac_address,
++	.ndo_change_mtu		= team_change_mtu,
++	.ndo_get_stats64	= team_get_stats64,
++	.ndo_vlan_rx_add_vid	= team_vlan_rx_add_vid,
++	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
++	.ndo_add_slave		= team_add_slave,
++	.ndo_del_slave		= team_del_slave,
++};
++
++
++/***********************
++ * rt netlink interface
++ ***********************/
++
++static void team_setup(struct net_device *dev)
++{
++	ether_setup(dev);
++
++	dev->netdev_ops = &team_netdev_ops;
++	dev->destructor	= team_destructor;
++	dev->tx_queue_len = 0;
++	dev->flags |= IFF_MULTICAST;
++	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
++
++	/*
++	 * Indicate we support unicast address filtering. That way core won't
++	 * bring us to promisc mode in case a unicast addr is added.
++	 * Let this up to underlay drivers.
++	 */
++	dev->priv_flags |= IFF_UNICAST_FLT;
++
++	dev->features |= NETIF_F_LLTX;
++	dev->features |= NETIF_F_GRO;
++	dev->hw_features = NETIF_F_HW_VLAN_TX |
++			   NETIF_F_HW_VLAN_RX |
++			   NETIF_F_HW_VLAN_FILTER;
++
++	dev->features |= dev->hw_features;
++}
++
++static int team_newlink(struct net *src_net, struct net_device *dev,
++			struct nlattr *tb[], struct nlattr *data[])
++{
++	int err;
++
++	if (tb[IFLA_ADDRESS] == NULL)
++		random_ether_addr(dev->dev_addr);
++
++	err = register_netdevice(dev);
++	if (err)
++		return err;
++
++	return 0;
++}
++
++static int team_validate(struct nlattr *tb[], struct nlattr *data[])
++{
++	if (tb[IFLA_ADDRESS]) {
++		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
++			return -EINVAL;
++		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
++			return -EADDRNOTAVAIL;
++	}
++	return 0;
++}
++
++static struct rtnl_link_ops team_link_ops __read_mostly = {
++	.kind		= DRV_NAME,
++	.priv_size	= sizeof(struct team),
++	.setup		= team_setup,
++	.newlink	= team_newlink,
++	.validate	= team_validate,
++};
++
++
++/***********************************
++ * Generic netlink custom interface
++ ***********************************/
++
++static struct genl_family team_nl_family = {
++	.id		= GENL_ID_GENERATE,
++	.name		= TEAM_GENL_NAME,
++	.version	= TEAM_GENL_VERSION,
++	.maxattr	= TEAM_ATTR_MAX,
++	.netnsok	= true,
++};
++
++static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
++	[TEAM_ATTR_UNSPEC]			= { .type = NLA_UNSPEC, },
++	[TEAM_ATTR_TEAM_IFINDEX]		= { .type = NLA_U32 },
++	[TEAM_ATTR_LIST_OPTION]			= { .type = NLA_NESTED },
++	[TEAM_ATTR_LIST_PORT]			= { .type = NLA_NESTED },
++};
++
++static const struct nla_policy
++team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
++	[TEAM_ATTR_OPTION_UNSPEC]		= { .type = NLA_UNSPEC, },
++	[TEAM_ATTR_OPTION_NAME] = {
++		.type = NLA_STRING,
++		.len = TEAM_STRING_MAX_LEN,
++	},
++	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
++	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
++	[TEAM_ATTR_OPTION_DATA] = {
++		.type = NLA_BINARY,
++		.len = TEAM_STRING_MAX_LEN,
++	},
++};
++
++static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
++{
++	struct sk_buff *msg;
++	void *hdr;
++	int err;
++
++	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++	if (!msg)
++		return -ENOMEM;
++
++	hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
++			  &team_nl_family, 0, TEAM_CMD_NOOP);
++	if (IS_ERR(hdr)) {
++		err = PTR_ERR(hdr);
++		goto err_msg_put;
++	}
++
++	genlmsg_end(msg, hdr);
++
++	return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
++
++err_msg_put:
++	nlmsg_free(msg);
++
++	return err;
++}
++
++/*
++ * Netlink cmd functions should be locked by following two functions.
++ * To ensure team_uninit would not be called in between, hold rcu_read_lock
++ * all the time.
++ */
++static struct team *team_nl_team_get(struct genl_info *info)
++{
++	struct net *net = genl_info_net(info);
++	int ifindex;
++	struct net_device *dev;
++	struct team *team;
++
++	if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
++		return NULL;
++
++	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
++	rcu_read_lock();
++	dev = dev_get_by_index_rcu(net, ifindex);
++	if (!dev || dev->netdev_ops != &team_netdev_ops) {
++		rcu_read_unlock();
++		return NULL;
++	}
++
++	team = netdev_priv(dev);
++	spin_lock(&team->lock);
++	return team;
++}
++
++static void team_nl_team_put(struct team *team)
++{
++	spin_unlock(&team->lock);
++	rcu_read_unlock();
++}
++
++static int team_nl_send_generic(struct genl_info *info, struct team *team,
++				int (*fill_func)(struct sk_buff *skb,
++						 struct genl_info *info,
++						 int flags, struct team *team))
++{
++	struct sk_buff *skb;
++	int err;
++
++	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++	if (!skb)
++		return -ENOMEM;
++
++	err = fill_func(skb, info, NLM_F_ACK, team);
++	if (err < 0)
++		goto err_fill;
++
++	err = genlmsg_unicast(genl_info_net(info), skb, info->snd_pid);
++	return err;
++
++err_fill:
++	nlmsg_free(skb);
++	return err;
++}
++
++static int team_nl_fill_options_get_changed(struct sk_buff *skb,
++					    u32 pid, u32 seq, int flags,
++					    struct team *team,
++					    struct team_option *changed_option)
++{
++	struct nlattr *option_list;
++	void *hdr;
++	struct team_option *option;
++
++	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
++			  TEAM_CMD_OPTIONS_GET);
++	if (IS_ERR(hdr))
++		return PTR_ERR(hdr);
++
++	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
++	option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
++	if (!option_list)
++		return -EMSGSIZE;
++
++	list_for_each_entry(option, &team->option_list, list) {
++		struct nlattr *option_item;
++		long arg;
++
++		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
++		if (!option_item)
++			goto nla_put_failure;
++		NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
++		if (option == changed_option)
++			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
++		switch (option->type) {
++		case TEAM_OPTION_TYPE_U32:
++			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
++			team_option_get(team, option, &arg);
++			NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
++			break;
++		case TEAM_OPTION_TYPE_STRING:
++			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
++			team_option_get(team, option, &arg);
++			NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
++				       (char *) arg);
++			break;
++		default:
++			BUG();
++		}
++		nla_nest_end(skb, option_item);
++	}
++
++	nla_nest_end(skb, option_list);
++	return genlmsg_end(skb, hdr);
++
++nla_put_failure:
++	genlmsg_cancel(skb, hdr);
++	return -EMSGSIZE;
++}
++
++static int team_nl_fill_options_get(struct sk_buff *skb,
++				    struct genl_info *info, int flags,
++				    struct team *team)
++{
++	return team_nl_fill_options_get_changed(skb, info->snd_pid,
++						info->snd_seq, NLM_F_ACK,
++						team, NULL);
++}
++
++static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
++{
++	struct team *team;
++	int err;
++
++	team = team_nl_team_get(info);
++	if (!team)
++		return -EINVAL;
++
++	err = team_nl_send_generic(info, team, team_nl_fill_options_get);
++
++	team_nl_team_put(team);
++
++	return err;
++}
++
++static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
++{
++	struct team *team;
++	int err = 0;
++	int i;
++	struct nlattr *nl_option;
++
++	team = team_nl_team_get(info);
++	if (!team)
++		return -EINVAL;
++
++	err = -EINVAL;
++	if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
++		err = -EINVAL;
++		goto team_put;
++	}
++
++	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
++		struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
++		enum team_option_type opt_type;
++		struct team_option *option;
++		char *opt_name;
++		bool opt_found = false;
++
++		if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
++			err = -EINVAL;
++			goto team_put;
++		}
++		err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
++				       nl_option, team_nl_option_policy);
++		if (err)
++			goto team_put;
++		if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
++		    !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
++		    !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
++			err = -EINVAL;
++			goto team_put;
++		}
++		switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
++		case NLA_U32:
++			opt_type = TEAM_OPTION_TYPE_U32;
++			break;
++		case NLA_STRING:
++			opt_type = TEAM_OPTION_TYPE_STRING;
++			break;
++		default:
++			goto team_put;
++		}
++
++		opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
++		list_for_each_entry(option, &team->option_list, list) {
++			long arg;
++			struct nlattr *opt_data_attr;
++
++			if (option->type != opt_type ||
++			    strcmp(option->name, opt_name))
++				continue;
++			opt_found = true;
++			opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
++			switch (opt_type) {
++			case TEAM_OPTION_TYPE_U32:
++				arg = nla_get_u32(opt_data_attr);
++				break;
++			case TEAM_OPTION_TYPE_STRING:
++				arg = (long) nla_data(opt_data_attr);
++				break;
++			default:
++				BUG();
++			}
++			err = team_option_set(team, option, &arg);
++			if (err)
++				goto team_put;
++		}
++		if (!opt_found) {
++			err = -ENOENT;
++			goto team_put;
++		}
++	}
++
++team_put:
++	team_nl_team_put(team);
++
++	return err;
++}
++
++static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
++					      u32 pid, u32 seq, int flags,
++					      struct team *team,
++					      struct team_port *changed_port)
++{
++	struct nlattr *port_list;
++	void *hdr;
++	struct team_port *port;
++
++	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
++			  TEAM_CMD_PORT_LIST_GET);
++	if (IS_ERR(hdr))
++		return PTR_ERR(hdr);
++
++	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
++	port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
++	if (!port_list)
++		return -EMSGSIZE;
++
++	list_for_each_entry(port, &team->port_list, list) {
++		struct nlattr *port_item;
++
++		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
++		if (!port_item)
++			goto nla_put_failure;
++		NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
++		if (port == changed_port)
++			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
++		if (port->linkup)
++			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
++		NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
++		NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
++		nla_nest_end(skb, port_item);
++	}
++
++	nla_nest_end(skb, port_list);
++	return genlmsg_end(skb, hdr);
++
++nla_put_failure:
++	genlmsg_cancel(skb, hdr);
++	return -EMSGSIZE;
++}
++
++static int team_nl_fill_port_list_get(struct sk_buff *skb,
++				      struct genl_info *info, int flags,
++				      struct team *team)
++{
++	return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
++						  info->snd_seq, NLM_F_ACK,
++						  team, NULL);
++}
++
++static int team_nl_cmd_port_list_get(struct sk_buff *skb,
++				     struct genl_info *info)
++{
++	struct team *team;
++	int err;
++
++	team = team_nl_team_get(info);
++	if (!team)
++		return -EINVAL;
++
++	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
++
++	team_nl_team_put(team);
++
++	return err;
++}
++
++static struct genl_ops team_nl_ops[] = {
++	{
++		.cmd = TEAM_CMD_NOOP,
++		.doit = team_nl_cmd_noop,
++		.policy = team_nl_policy,
++	},
++	{
++		.cmd = TEAM_CMD_OPTIONS_SET,
++		.doit = team_nl_cmd_options_set,
++		.policy = team_nl_policy,
++		.flags = GENL_ADMIN_PERM,
++	},
++	{
++		.cmd = TEAM_CMD_OPTIONS_GET,
++		.doit = team_nl_cmd_options_get,
++		.policy = team_nl_policy,
++		.flags = GENL_ADMIN_PERM,
++	},
++	{
++		.cmd = TEAM_CMD_PORT_LIST_GET,
++		.doit = team_nl_cmd_port_list_get,
++		.policy = team_nl_policy,
++		.flags = GENL_ADMIN_PERM,
++	},
++};
++
++static struct genl_multicast_group team_change_event_mcgrp = {
++	.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
++};
++
++static int team_nl_send_event_options_get(struct team *team,
++					  struct team_option *changed_option)
++{
++	struct sk_buff *skb;
++	int err;
++	struct net *net = dev_net(team->dev);
++
++	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++	if (!skb)
++		return -ENOMEM;
++
++	err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
++					       changed_option);
++	if (err < 0)
++		goto err_fill;
++
++	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
++				      GFP_KERNEL);
++	return err;
++
++err_fill:
++	nlmsg_free(skb);
++	return err;
++}
++
++static int team_nl_send_event_port_list_get(struct team_port *port)
++{
++	struct sk_buff *skb;
++	int err;
++	struct net *net = dev_net(port->team->dev);
++
++	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++	if (!skb)
++		return -ENOMEM;
++
++	err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
++						 port->team, port);
++	if (err < 0)
++		goto err_fill;
++
++	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
++				      GFP_KERNEL);
++	return err;
++
++err_fill:
++	nlmsg_free(skb);
++	return err;
++}
++
++static int team_nl_init(void)
++{
++	int err;
++
++	err = genl_register_family_with_ops(&team_nl_family, team_nl_ops,
++					    ARRAY_SIZE(team_nl_ops));
++	if (err)
++		return err;
++
++	err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
++	if (err)
++		goto err_change_event_grp_reg;
++
++	return 0;
++
++err_change_event_grp_reg:
++	genl_unregister_family(&team_nl_family);
++
++	return err;
++}
++
++static void team_nl_fini(void)
++{
++	genl_unregister_family(&team_nl_family);
++}
++
++
++/******************
++ * Change checkers
++ ******************/
++
++static void __team_options_change_check(struct team *team,
++					struct team_option *changed_option)
++{
++	int err;
++
++	err = team_nl_send_event_options_get(team, changed_option);
++	if (err)
++		netdev_warn(team->dev, "Failed to send options change via netlink\n");
++}
++
++/* rtnl lock is held */
++static void __team_port_change_check(struct team_port *port, bool linkup)
++{
++	int err;
++
++	if (port->linkup == linkup)
++		return;
++
++	port->linkup = linkup;
++	if (linkup) {
++		struct ethtool_cmd ecmd;
++
++		err = __ethtool_get_settings(port->dev, &ecmd);
++		if (!err) {
++			port->speed = ethtool_cmd_speed(&ecmd);
++			port->duplex = ecmd.duplex;
++			goto send_event;
++		}
++	}
++	port->speed = 0;
++	port->duplex = 0;
++
++send_event:
++	err = team_nl_send_event_port_list_get(port);
++	if (err)
++		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
++			    port->dev->name);
++
++}
++
++static void team_port_change_check(struct team_port *port, bool linkup)
++{
++	struct team *team = port->team;
++
++	spin_lock(&team->lock);
++	__team_port_change_check(port, linkup);
++	spin_unlock(&team->lock);
++}
++
++/************************************
++ * Net device notifier event handler
++ ************************************/
++
++static int team_device_event(struct notifier_block *unused,
++			     unsigned long event, void *ptr)
++{
++	struct net_device *dev = (struct net_device *) ptr;
++	struct team_port *port;
++
++	port = team_port_get_rtnl(dev);
++	if (!port)
++		return NOTIFY_DONE;
++
++	switch (event) {
++	case NETDEV_UP:
++		if (netif_carrier_ok(dev))
++			team_port_change_check(port, true);
++	case NETDEV_DOWN:
++		team_port_change_check(port, false);
++	case NETDEV_CHANGE:
++		if (netif_running(port->dev))
++			team_port_change_check(port,
++					       !!netif_carrier_ok(port->dev));
++		break;
++	case NETDEV_UNREGISTER:
++		team_del_slave(port->team->dev, dev);
++		break;
++	case NETDEV_FEAT_CHANGE:
++		team_compute_features(port->team);
++		break;
++	case NETDEV_CHANGEMTU:
++		/* Forbid to change mtu of underlaying device */
++		return NOTIFY_BAD;
++	case NETDEV_PRE_TYPE_CHANGE:
++		/* Forbid to change type of underlaying device */
++		return NOTIFY_BAD;
++	}
++	return NOTIFY_DONE;
++}
++
++static struct notifier_block team_notifier_block __read_mostly = {
++	.notifier_call = team_device_event,
++};
++
++
++/***********************
++ * Module init and exit
++ ***********************/
++
++static int __init team_module_init(void)
++{
++	int err;
++
++	register_netdevice_notifier(&team_notifier_block);
++
++	err = rtnl_link_register(&team_link_ops);
++	if (err)
++		goto err_rtnl_reg;
++
++	err = team_nl_init();
++	if (err)
++		goto err_nl_init;
++
++	return 0;
++
++err_nl_init:
++	rtnl_link_unregister(&team_link_ops);
++
++err_rtnl_reg:
++	unregister_netdevice_notifier(&team_notifier_block);
++
++	return err;
++}
++
++static void __exit team_module_exit(void)
++{
++	team_nl_fini();
++	rtnl_link_unregister(&team_link_ops);
++	unregister_netdevice_notifier(&team_notifier_block);
++}
++
++module_init(team_module_init);
++module_exit(team_module_exit);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Jiri Pirko <jpirko at redhat.com>");
++MODULE_DESCRIPTION("Ethernet team device driver");
++MODULE_ALIAS_RTNL_LINK(DRV_NAME);
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+new file mode 100644
+index 0000000..6fe920c
+--- /dev/null
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -0,0 +1,137 @@
++/*
++ * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team
++ * Copyright (c) 2011 Jiri Pirko <jpirko at redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <net/rtnetlink.h>
++#include <linux/if_team.h>
++
++struct ab_priv {
++	struct team_port __rcu *active_port;
++};
++
++static struct ab_priv *ab_priv(struct team *team)
++{
++	return (struct ab_priv *) &team->mode_priv;
++}
++
++static rx_handler_result_t ab_receive(struct team *team, struct team_port *port,
++				      struct sk_buff *skb) {
++	struct team_port *active_port;
++
++	active_port = rcu_dereference(ab_priv(team)->active_port);
++	if (active_port != port)
++		return RX_HANDLER_EXACT;
++	return RX_HANDLER_ANOTHER;
++}
++
++static bool ab_transmit(struct team *team, struct sk_buff *skb)
++{
++	struct team_port *active_port;
++
++	active_port = rcu_dereference(ab_priv(team)->active_port);
++	if (unlikely(!active_port))
++		goto drop;
++	skb->dev = active_port->dev;
++	if (dev_queue_xmit(skb))
++		return false;
++	return true;
++
++drop:
++	dev_kfree_skb_any(skb);
++	return false;
++}
++
++static void ab_port_leave(struct team *team, struct team_port *port)
++{
++	if (ab_priv(team)->active_port == port)
++		rcu_assign_pointer(ab_priv(team)->active_port, NULL);
++}
++
++static int ab_active_port_get(struct team *team, void *arg)
++{
++	u32 *ifindex = arg;
++
++	*ifindex = 0;
++	if (ab_priv(team)->active_port)
++		*ifindex = ab_priv(team)->active_port->dev->ifindex;
++	return 0;
++}
++
++static int ab_active_port_set(struct team *team, void *arg)
++{
++	u32 *ifindex = arg;
++	struct team_port *port;
++
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		if (port->dev->ifindex == *ifindex) {
++			rcu_assign_pointer(ab_priv(team)->active_port, port);
++			return 0;
++		}
++	}
++	return -ENOENT;
++}
++
++static struct team_option ab_options[] = {
++	{
++		.name = "activeport",
++		.type = TEAM_OPTION_TYPE_U32,
++		.getter = ab_active_port_get,
++		.setter = ab_active_port_set,
++	},
++};
++
++int ab_init(struct team *team)
++{
++	team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
++	return 0;
++}
++
++void ab_exit(struct team *team)
++{
++	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
++}
++
++static const struct team_mode_ops ab_mode_ops = {
++	.init			= ab_init,
++	.exit			= ab_exit,
++	.receive		= ab_receive,
++	.transmit		= ab_transmit,
++	.port_leave		= ab_port_leave,
++};
++
++static struct team_mode ab_mode = {
++	.kind		= "activebackup",
++	.owner		= THIS_MODULE,
++	.priv_size	= sizeof(struct ab_priv),
++	.ops		= &ab_mode_ops,
++};
++
++static int __init ab_init_module(void)
++{
++	return team_mode_register(&ab_mode);
++}
++
++static void __exit ab_cleanup_module(void)
++{
++	team_mode_unregister(&ab_mode);
++}
++
++module_init(ab_init_module);
++module_exit(ab_cleanup_module);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Jiri Pirko <jpirko at redhat.com>");
++MODULE_DESCRIPTION("Active-backup mode for team");
++MODULE_ALIAS("team-mode-activebackup");
+diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
+new file mode 100644
+index 0000000..a0e8f80
+--- /dev/null
++++ b/drivers/net/team/team_mode_roundrobin.c
+@@ -0,0 +1,107 @@
++/*
++ * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team
++ * Copyright (c) 2011 Jiri Pirko <jpirko at redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/if_team.h>
++
++struct rr_priv {
++	unsigned int sent_packets;
++};
++
++static struct rr_priv *rr_priv(struct team *team)
++{
++	return (struct rr_priv *) &team->mode_priv;
++}
++
++static struct team_port *__get_first_port_up(struct team *team,
++					     struct team_port *port)
++{
++	struct team_port *cur;
++
++	if (port->linkup)
++		return port;
++	cur = port;
++	list_for_each_entry_continue_rcu(cur, &team->port_list, list)
++		if (cur->linkup)
++			return cur;
++	list_for_each_entry_rcu(cur, &team->port_list, list) {
++		if (cur == port)
++			break;
++		if (cur->linkup)
++			return cur;
++	}
++	return NULL;
++}
++
++static bool rr_transmit(struct team *team, struct sk_buff *skb)
++{
++	struct team_port *port;
++	int port_index;
++
++	port_index = rr_priv(team)->sent_packets++ % team->port_count;
++	port = team_get_port_by_index_rcu(team, port_index);
++	port = __get_first_port_up(team, port);
++	if (unlikely(!port))
++		goto drop;
++	skb->dev = port->dev;
++	if (dev_queue_xmit(skb))
++		return false;
++	return true;
++
++drop:
++	dev_kfree_skb_any(skb);
++	return false;
++}
++
++static int rr_port_enter(struct team *team, struct team_port *port)
++{
++	return team_port_set_team_mac(port);
++}
++
++static void rr_port_change_mac(struct team *team, struct team_port *port)
++{
++	team_port_set_team_mac(port);
++}
++
++static const struct team_mode_ops rr_mode_ops = {
++	.transmit		= rr_transmit,
++	.port_enter		= rr_port_enter,
++	.port_change_mac	= rr_port_change_mac,
++};
++
++static struct team_mode rr_mode = {
++	.kind		= "roundrobin",
++	.owner		= THIS_MODULE,
++	.priv_size	= sizeof(struct rr_priv),
++	.ops		= &rr_mode_ops,
++};
++
++static int __init rr_init_module(void)
++{
++	return team_mode_register(&rr_mode);
++}
++
++static void __exit rr_cleanup_module(void)
++{
++	team_mode_unregister(&rr_mode);
++}
++
++module_init(rr_init_module);
++module_exit(rr_cleanup_module);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Jiri Pirko <jpirko at redhat.com>");
++MODULE_DESCRIPTION("Round-robin mode for team");
++MODULE_ALIAS("team-mode-roundrobin");
+diff --git a/include/linux/Kbuild b/include/linux/Kbuild
+index 619b565..0b091b3 100644
+--- a/include/linux/Kbuild
++++ b/include/linux/Kbuild
+@@ -185,6 +185,7 @@ header-y += if_pppol2tp.h
+ header-y += if_pppox.h
+ header-y += if_slip.h
+ header-y += if_strip.h
++header-y += if_team.h
+ header-y += if_tr.h
+ header-y += if_tun.h
+ header-y += if_tunnel.h
+diff --git a/include/linux/if.h b/include/linux/if.h
+index db20bd4..06b6ef6 100644
+--- a/include/linux/if.h
++++ b/include/linux/if.h
+@@ -79,6 +79,7 @@
+ #define IFF_TX_SKB_SHARING	0x10000	/* The interface supports sharing
+ 					 * skbs on transmit */
+ #define IFF_UNICAST_FLT	0x20000		/* Supports unicast filtering	*/
++#define IFF_TEAM_PORT	0x40000		/* device used as team port */
+ 
+ #define IF_GET_IFACE	0x0001		/* for querying only */
+ #define IF_GET_PROTO	0x0002
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+new file mode 100644
+index 0000000..14f6388
+--- /dev/null
++++ b/include/linux/if_team.h
+@@ -0,0 +1,242 @@
++/*
++ * include/linux/if_team.h - Network team device driver header
++ * Copyright (c) 2011 Jiri Pirko <jpirko at redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifndef _LINUX_IF_TEAM_H_
++#define _LINUX_IF_TEAM_H_
++
++#ifdef __KERNEL__
++
++struct team_pcpu_stats {
++	u64			rx_packets;
++	u64			rx_bytes;
++	u64			rx_multicast;
++	u64			tx_packets;
++	u64			tx_bytes;
++	struct u64_stats_sync	syncp;
++	u32			rx_dropped;
++	u32			tx_dropped;
++};
++
++struct team;
++
++struct team_port {
++	struct net_device *dev;
++	struct hlist_node hlist; /* node in hash list */
++	struct list_head list; /* node in ordinary list */
++	struct team *team;
++	int index;
++
++	/*
++	 * A place for storing original values of the device before it
++	 * become a port.
++	 */
++	struct {
++		unsigned char dev_addr[MAX_ADDR_LEN];
++		unsigned int mtu;
++	} orig;
++
++	bool linkup;
++	u32 speed;
++	u8 duplex;
++
++	struct rcu_head rcu;
++};
++
++struct team_mode_ops {
++	int (*init)(struct team *team);
++	void (*exit)(struct team *team);
++	rx_handler_result_t (*receive)(struct team *team,
++				       struct team_port *port,
++				       struct sk_buff *skb);
++	bool (*transmit)(struct team *team, struct sk_buff *skb);
++	int (*port_enter)(struct team *team, struct team_port *port);
++	void (*port_leave)(struct team *team, struct team_port *port);
++	void (*port_change_mac)(struct team *team, struct team_port *port);
++};
++
++enum team_option_type {
++	TEAM_OPTION_TYPE_U32,
++	TEAM_OPTION_TYPE_STRING,
++};
++
++struct team_option {
++	struct list_head list;
++	const char *name;
++	enum team_option_type type;
++	int (*getter)(struct team *team, void *arg);
++	int (*setter)(struct team *team, void *arg);
++};
++
++struct team_mode {
++	struct list_head list;
++	const char *kind;
++	struct module *owner;
++	size_t priv_size;
++	const struct team_mode_ops *ops;
++};
++
++#define TEAM_PORT_HASHBITS 4
++#define TEAM_PORT_HASHENTRIES (1 << TEAM_PORT_HASHBITS)
++
++#define TEAM_MODE_PRIV_LONGS 4
++#define TEAM_MODE_PRIV_SIZE (sizeof(long) * TEAM_MODE_PRIV_LONGS)
++
++struct team {
++	struct net_device *dev; /* associated netdevice */
++	struct team_pcpu_stats __percpu *pcpu_stats;
++
++	spinlock_t lock; /* used for overall locking, e.g. port lists write */
++
++	/*
++	 * port lists with port count
++	 */
++	int port_count;
++	struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES];
++	struct list_head port_list;
++
++	struct list_head option_list;
++
++	const struct team_mode *mode;
++	struct team_mode_ops ops;
++	long mode_priv[TEAM_MODE_PRIV_LONGS];
++};
++
++static inline struct hlist_head *team_port_index_hash(struct team *team,
++						      int port_index)
++{
++	return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
++}
++
++static inline struct team_port *team_get_port_by_index(struct team *team,
++						       int port_index)
++{
++	struct hlist_node *p;
++	struct team_port *port;
++	struct hlist_head *head = team_port_index_hash(team, port_index);
++
++	hlist_for_each_entry(port, p, head, hlist)
++		if (port->index == port_index)
++			return port;
++	return NULL;
++}
++static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
++							   int port_index)
++{
++	struct hlist_node *p;
++	struct team_port *port;
++	struct hlist_head *head = team_port_index_hash(team, port_index);
++
++	hlist_for_each_entry_rcu(port, p, head, hlist)
++		if (port->index == port_index)
++			return port;
++	return NULL;
++}
++
++extern int team_port_set_team_mac(struct team_port *port);
++extern void team_options_register(struct team *team,
++				  struct team_option *option,
++				  size_t option_count);
++extern void team_options_unregister(struct team *team,
++				    struct team_option *option,
++				    size_t option_count);
++extern int team_mode_register(struct team_mode *mode);
++extern int team_mode_unregister(struct team_mode *mode);
++
++#endif /* __KERNEL__ */
++
++#define TEAM_STRING_MAX_LEN 32
++
++/**********************************
++ * NETLINK_GENERIC netlink family.
++ **********************************/
++
++enum {
++	TEAM_CMD_NOOP,
++	TEAM_CMD_OPTIONS_SET,
++	TEAM_CMD_OPTIONS_GET,
++	TEAM_CMD_PORT_LIST_GET,
++
++	__TEAM_CMD_MAX,
++	TEAM_CMD_MAX = (__TEAM_CMD_MAX - 1),
++};
++
++enum {
++	TEAM_ATTR_UNSPEC,
++	TEAM_ATTR_TEAM_IFINDEX,		/* u32 */
++	TEAM_ATTR_LIST_OPTION,		/* nest */
++	TEAM_ATTR_LIST_PORT,		/* nest */
++
++	__TEAM_ATTR_MAX,
++	TEAM_ATTR_MAX = __TEAM_ATTR_MAX - 1,
++};
++
++/* Nested layout of get/set msg:
++ *
++ *	[TEAM_ATTR_LIST_OPTION]
++ *		[TEAM_ATTR_ITEM_OPTION]
++ *			[TEAM_ATTR_OPTION_*], ...
++ *		[TEAM_ATTR_ITEM_OPTION]
++ *			[TEAM_ATTR_OPTION_*], ...
++ *		...
++ *	[TEAM_ATTR_LIST_PORT]
++ *		[TEAM_ATTR_ITEM_PORT]
++ *			[TEAM_ATTR_PORT_*], ...
++ *		[TEAM_ATTR_ITEM_PORT]
++ *			[TEAM_ATTR_PORT_*], ...
++ *		...
++ */
++
++enum {
++	TEAM_ATTR_ITEM_OPTION_UNSPEC,
++	TEAM_ATTR_ITEM_OPTION,		/* nest */
++
++	__TEAM_ATTR_ITEM_OPTION_MAX,
++	TEAM_ATTR_ITEM_OPTION_MAX = __TEAM_ATTR_ITEM_OPTION_MAX - 1,
++};
++
++enum {
++	TEAM_ATTR_OPTION_UNSPEC,
++	TEAM_ATTR_OPTION_NAME,		/* string */
++	TEAM_ATTR_OPTION_CHANGED,	/* flag */
++	TEAM_ATTR_OPTION_TYPE,		/* u8 */
++	TEAM_ATTR_OPTION_DATA,		/* dynamic */
++
++	__TEAM_ATTR_OPTION_MAX,
++	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
++};
++
++enum {
++	TEAM_ATTR_ITEM_PORT_UNSPEC,
++	TEAM_ATTR_ITEM_PORT,		/* nest */
++
++	__TEAM_ATTR_ITEM_PORT_MAX,
++	TEAM_ATTR_ITEM_PORT_MAX = __TEAM_ATTR_ITEM_PORT_MAX - 1,
++};
++
++enum {
++	TEAM_ATTR_PORT_UNSPEC,
++	TEAM_ATTR_PORT_IFINDEX,		/* u32 */
++	TEAM_ATTR_PORT_CHANGED,		/* flag */
++	TEAM_ATTR_PORT_LINKUP,		/* flag */
++	TEAM_ATTR_PORT_SPEED,		/* u32 */
++	TEAM_ATTR_PORT_DUPLEX,		/* u8 */
++
++	__TEAM_ATTR_PORT_MAX,
++	TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
++};
++
++/*
++ * NETLINK_GENERIC related info
++ */
++#define TEAM_GENL_NAME "team"
++#define TEAM_GENL_VERSION 0x1
++#define TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME "change_event"
++
++#endif /* _LINUX_IF_TEAM_H_ */

Added: dists/sid/linux/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,49 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Wed, 16 Nov 2011 11:55:54 +0000
+Subject: [02/23] team: Do not hold rcu_read_lock when running netlink cmds
+
+commit 8c0713a57482ebfadef197c856a38af3a55444c6 upstream.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 60672bb..e5390c7 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -1043,8 +1043,7 @@ err_msg_put:
+ 
+ /*
+  * Netlink cmd functions should be locked by following two functions.
+- * To ensure team_uninit would not be called in between, hold rcu_read_lock
+- * all the time.
++ * Since dev gets held here, that ensures dev won't disappear in between.
+  */
+ static struct team *team_nl_team_get(struct genl_info *info)
+ {
+@@ -1057,10 +1056,10 @@ static struct team *team_nl_team_get(struct genl_info *info)
+ 		return NULL;
+ 
+ 	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
+-	rcu_read_lock();
+-	dev = dev_get_by_index_rcu(net, ifindex);
++	dev = dev_get_by_index(net, ifindex);
+ 	if (!dev || dev->netdev_ops != &team_netdev_ops) {
+-		rcu_read_unlock();
++		if (dev)
++			dev_put(dev);
+ 		return NULL;
+ 	}
+ 
+@@ -1072,7 +1071,7 @@ static struct team *team_nl_team_get(struct genl_info *info)
+ static void team_nl_team_put(struct team *team)
+ {
+ 	spin_unlock(&team->lock);
+-	rcu_read_unlock();
++	dev_put(team->dev);
+ }
+ 
+ static int team_nl_send_generic(struct genl_info *info, struct team *team,

Added: dists/sid/linux/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,149 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Wed, 16 Nov 2011 11:09:08 +0000
+Subject: [03/23] team: convert overall spinlock to mutex
+
+commit 61dc3461b9549bc10a2f16d254250680cadafcce upstream.
+
+No need to have spinlock for this purpose. So convert this to mutex and
+avoid current schedule while atomic problems in netlink code.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   32 ++++++++++++++++----------------
+ include/linux/if_team.h |    2 +-
+ 2 files changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index e5390c7..7db219c 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -443,9 +443,9 @@ static void __team_compute_features(struct team *team)
+ 
+ static void team_compute_features(struct team *team)
+ {
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	__team_compute_features(team);
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ }
+ 
+ static int team_port_enter(struct team *team, struct team_port *port)
+@@ -647,7 +647,7 @@ static int team_init(struct net_device *dev)
+ 	int i;
+ 
+ 	team->dev = dev;
+-	spin_lock_init(&team->lock);
++	mutex_init(&team->lock);
+ 
+ 	team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
+ 	if (!team->pcpu_stats)
+@@ -672,13 +672,13 @@ static void team_uninit(struct net_device *dev)
+ 	struct team_port *port;
+ 	struct team_port *tmp;
+ 
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	list_for_each_entry_safe(port, tmp, &team->port_list, list)
+ 		team_port_del(team, port->dev);
+ 
+ 	__team_change_mode(team, NULL); /* cleanup */
+ 	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ }
+ 
+ static void team_destructor(struct net_device *dev)
+@@ -784,7 +784,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
+ 	 * Alhough this is reader, it's guarded by team lock. It's not possible
+ 	 * to traverse list in reverse under rcu_read_lock
+ 	 */
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	list_for_each_entry(port, &team->port_list, list) {
+ 		err = dev_set_mtu(port->dev, new_mtu);
+ 		if (err) {
+@@ -793,7 +793,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
+ 			goto unwind;
+ 		}
+ 	}
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ 
+ 	dev->mtu = new_mtu;
+ 
+@@ -802,7 +802,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
+ unwind:
+ 	list_for_each_entry_continue_reverse(port, &team->port_list, list)
+ 		dev_set_mtu(port->dev, dev->mtu);
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ 
+ 	return err;
+ }
+@@ -880,9 +880,9 @@ static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
+ 	struct team *team = netdev_priv(dev);
+ 	int err;
+ 
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	err = team_port_add(team, port_dev);
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ 	return err;
+ }
+ 
+@@ -891,9 +891,9 @@ static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
+ 	struct team *team = netdev_priv(dev);
+ 	int err;
+ 
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	err = team_port_del(team, port_dev);
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ 	return err;
+ }
+ 
+@@ -1064,13 +1064,13 @@ static struct team *team_nl_team_get(struct genl_info *info)
+ 	}
+ 
+ 	team = netdev_priv(dev);
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	return team;
+ }
+ 
+ static void team_nl_team_put(struct team *team)
+ {
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ 	dev_put(team->dev);
+ }
+ 
+@@ -1486,9 +1486,9 @@ static void team_port_change_check(struct team_port *port, bool linkup)
+ {
+ 	struct team *team = port->team;
+ 
+-	spin_lock(&team->lock);
++	mutex_lock(&team->lock);
+ 	__team_port_change_check(port, linkup);
+-	spin_unlock(&team->lock);
++	mutex_unlock(&team->lock);
+ }
+ 
+ /************************************
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 14f6388..a6eac12 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -92,7 +92,7 @@ struct team {
+ 	struct net_device *dev; /* associated netdevice */
+ 	struct team_pcpu_stats __percpu *pcpu_stats;
+ 
+-	spinlock_t lock; /* used for overall locking, e.g. port lists write */
++	struct mutex lock; /* used for overall locking, e.g. port lists write */
+ 
+ 	/*
+ 	 * port lists with port count

Added: dists/sid/linux/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,189 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Wed, 16 Nov 2011 11:09:09 +0000
+Subject: [04/23] team: replicate options on register
+
+commit 358b838291f618278080bbed435b755f9b46748e upstream.
+
+Since multiple team instances are putting defined options into their
+option list, during register each option must be cloned before added
+into list. This resolves uncool memory corruptions when using multiple
+teams.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c                   |   76 +++++++++++++++++++++++++----
+ drivers/net/team/team_mode_activebackup.c |    5 +-
+ include/linux/if_team.h                   |    8 +--
+ 3 files changed, 72 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 7db219c..f309274 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -80,30 +80,78 @@ EXPORT_SYMBOL(team_port_set_team_mac);
+  * Options handling
+  *******************/
+ 
+-void team_options_register(struct team *team, struct team_option *option,
+-			   size_t option_count)
++struct team_option *__team_find_option(struct team *team, const char *opt_name)
++{
++	struct team_option *option;
++
++	list_for_each_entry(option, &team->option_list, list) {
++		if (strcmp(option->name, opt_name) == 0)
++			return option;
++	}
++	return NULL;
++}
++
++int team_options_register(struct team *team,
++			  const struct team_option *option,
++			  size_t option_count)
+ {
+ 	int i;
++	struct team_option *dst_opts[option_count];
++	int err;
++
++	memset(dst_opts, 0, sizeof(dst_opts));
++	for (i = 0; i < option_count; i++, option++) {
++		struct team_option *dst_opt;
++
++		if (__team_find_option(team, option->name)) {
++			err = -EEXIST;
++			goto rollback;
++		}
++		dst_opt = kmalloc(sizeof(*option), GFP_KERNEL);
++		if (!dst_opt) {
++			err = -ENOMEM;
++			goto rollback;
++		}
++		memcpy(dst_opt, option, sizeof(*option));
++		dst_opts[i] = dst_opt;
++	}
++
++	for (i = 0; i < option_count; i++)
++		list_add_tail(&dst_opts[i]->list, &team->option_list);
+ 
+-	for (i = 0; i < option_count; i++, option++)
+-		list_add_tail(&option->list, &team->option_list);
++	return 0;
++
++rollback:
++	for (i = 0; i < option_count; i++)
++		kfree(dst_opts[i]);
++
++	return err;
+ }
++
+ EXPORT_SYMBOL(team_options_register);
+ 
+ static void __team_options_change_check(struct team *team,
+ 					struct team_option *changed_option);
+ 
+ static void __team_options_unregister(struct team *team,
+-				      struct team_option *option,
++				      const struct team_option *option,
+ 				      size_t option_count)
+ {
+ 	int i;
+ 
+-	for (i = 0; i < option_count; i++, option++)
+-		list_del(&option->list);
++	for (i = 0; i < option_count; i++, option++) {
++		struct team_option *del_opt;
++
++		del_opt = __team_find_option(team, option->name);
++		if (del_opt) {
++			list_del(&del_opt->list);
++			kfree(del_opt);
++		}
++	}
+ }
+ 
+-void team_options_unregister(struct team *team, struct team_option *option,
++void team_options_unregister(struct team *team,
++			     const struct team_option *option,
+ 			     size_t option_count)
+ {
+ 	__team_options_unregister(team, option, option_count);
+@@ -632,7 +680,7 @@ static int team_mode_option_set(struct team *team, void *arg)
+ 	return team_change_mode(team, *str);
+ }
+ 
+-static struct team_option team_options[] = {
++static const struct team_option team_options[] = {
+ 	{
+ 		.name = "mode",
+ 		.type = TEAM_OPTION_TYPE_STRING,
+@@ -645,6 +693,7 @@ static int team_init(struct net_device *dev)
+ {
+ 	struct team *team = netdev_priv(dev);
+ 	int i;
++	int err;
+ 
+ 	team->dev = dev;
+ 	mutex_init(&team->lock);
+@@ -660,10 +709,17 @@ static int team_init(struct net_device *dev)
+ 	team_adjust_ops(team);
+ 
+ 	INIT_LIST_HEAD(&team->option_list);
+-	team_options_register(team, team_options, ARRAY_SIZE(team_options));
++	err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
++	if (err)
++		goto err_options_register;
+ 	netif_carrier_off(dev);
+ 
+ 	return 0;
++
++err_options_register:
++	free_percpu(team->pcpu_stats);
++
++	return err;
+ }
+ 
+ static void team_uninit(struct net_device *dev)
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+index 6fe920c..b344275 100644
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -83,7 +83,7 @@ static int ab_active_port_set(struct team *team, void *arg)
+ 	return -ENOENT;
+ }
+ 
+-static struct team_option ab_options[] = {
++static const struct team_option ab_options[] = {
+ 	{
+ 		.name = "activeport",
+ 		.type = TEAM_OPTION_TYPE_U32,
+@@ -94,8 +94,7 @@ static struct team_option ab_options[] = {
+ 
+ int ab_init(struct team *team)
+ {
+-	team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
+-	return 0;
++	return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
+ }
+ 
+ void ab_exit(struct team *team)
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index a6eac12..828181f 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -140,11 +140,11 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
+ }
+ 
+ extern int team_port_set_team_mac(struct team_port *port);
+-extern void team_options_register(struct team *team,
+-				  struct team_option *option,
+-				  size_t option_count);
++extern int team_options_register(struct team *team,
++				 const struct team_option *option,
++				 size_t option_count);
+ extern void team_options_unregister(struct team *team,
+-				    struct team_option *option,
++				    const struct team_option *option,
+ 				    size_t option_count);
+ extern int team_mode_register(struct team_mode *mode);
+ extern int team_mode_unregister(struct team_mode *mode);

Added: dists/sid/linux/debian/patches/features/all/team/0005-team-add-fix_features.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0005-team-add-fix_features.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,54 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 17 Nov 2011 04:16:04 +0000
+Subject: [05/23] team: add fix_features
+
+commit 234a8fd49d086f5a3debb379bfdf4e6b51f0c0e2 upstream.
+
+do fix features in similar way as bonding code does
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index f309274..5b169c1 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -953,6 +953,27 @@ static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
+ 	return err;
+ }
+ 
++static netdev_features_t team_fix_features(struct net_device *dev,
++					   netdev_features_t features)
++{
++	struct team_port *port;
++	struct team *team = netdev_priv(dev);
++	netdev_features_t mask;
++
++	mask = features;
++	features &= ~NETIF_F_ONE_FOR_ALL;
++	features |= NETIF_F_ALL_FOR_ALL;
++
++	rcu_read_lock();
++	list_for_each_entry_rcu(port, &team->port_list, list) {
++		features = netdev_increment_features(features,
++						     port->dev->features,
++						     mask);
++	}
++	rcu_read_unlock();
++	return features;
++}
++
+ static const struct net_device_ops team_netdev_ops = {
+ 	.ndo_init		= team_init,
+ 	.ndo_uninit		= team_uninit,
+@@ -968,6 +989,7 @@ static const struct net_device_ops team_netdev_ops = {
+ 	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
+ 	.ndo_add_slave		= team_add_slave,
+ 	.ndo_del_slave		= team_del_slave,
++	.ndo_fix_features	= team_fix_features,
+ };
+ 
+ 

Added: dists/sid/linux/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,50 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 17 Nov 2011 04:16:05 +0000
+Subject: [06/23] team: avoid using variable-length array
+
+commit 2bba19fff8d09bf19df5d5e2de7188d65de67c3e upstream.
+
+Apparently using variable-length array is not correct
+(https://lkml.org/lkml/2011/10/23/25). So remove it.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |    9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 5b169c1..c48ef19 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -96,10 +96,13 @@ int team_options_register(struct team *team,
+ 			  size_t option_count)
+ {
+ 	int i;
+-	struct team_option *dst_opts[option_count];
++	struct team_option **dst_opts;
+ 	int err;
+ 
+-	memset(dst_opts, 0, sizeof(dst_opts));
++	dst_opts = kzalloc(sizeof(struct team_option *) * option_count,
++			   GFP_KERNEL);
++	if (!dst_opts)
++		return -ENOMEM;
+ 	for (i = 0; i < option_count; i++, option++) {
+ 		struct team_option *dst_opt;
+ 
+@@ -119,12 +122,14 @@ int team_options_register(struct team *team,
+ 	for (i = 0; i < option_count; i++)
+ 		list_add_tail(&dst_opts[i]->list, &team->option_list);
+ 
++	kfree(dst_opts);
+ 	return 0;
+ 
+ rollback:
+ 	for (i = 0; i < option_count; i++)
+ 		kfree(dst_opts[i]);
+ 
++	kfree(dst_opts);
+ 	return err;
+ }
+ 

Added: dists/sid/linux/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,38 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 17 Nov 2011 06:32:37 +0000
+Subject: [07/23] team: replace kmalloc+memcpy by kmemdup
+
+commit f8a15af093b19b86d56933c8757cee298d0f32a8 upstream.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |    8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index c48ef19..064155d 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -104,19 +104,15 @@ int team_options_register(struct team *team,
+ 	if (!dst_opts)
+ 		return -ENOMEM;
+ 	for (i = 0; i < option_count; i++, option++) {
+-		struct team_option *dst_opt;
+-
+ 		if (__team_find_option(team, option->name)) {
+ 			err = -EEXIST;
+ 			goto rollback;
+ 		}
+-		dst_opt = kmalloc(sizeof(*option), GFP_KERNEL);
+-		if (!dst_opt) {
++		dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
++		if (!dst_opts[i]) {
+ 			err = -ENOMEM;
+ 			goto rollback;
+ 		}
+-		memcpy(dst_opt, option, sizeof(*option));
+-		dst_opts[i] = dst_opt;
+ 	}
+ 
+ 	for (i = 0; i < option_count; i++)

Added: dists/sid/linux/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,28 @@
+From: Eric Dumazet <eric.dumazet at gmail.com>
+Date: Wed, 23 Nov 2011 07:09:32 +0000
+Subject: [08/23] net: treewide use of RCU_INIT_POINTER
+
+commit 2cfa5a0471fef43fda0b7bd87e3a5e4dbadb7809 upstream.
+
+rcu_assign_pointer(ptr, NULL) can be safely replaced by
+RCU_INIT_POINTER(ptr, NULL)
+
+(old rcu_assign_pointer() macro was testing the NULL value and could
+omit the smp_wmb(), but this had to be removed because of compiler
+warnings)
+
+Signed-off-by: Eric Dumazet <eric.dumazet at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Restrict to drivers/net/team/]
+---
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -56,7 +56,7 @@ drop:
+ static void ab_port_leave(struct team *team, struct team_port *port)
+ {
+ 	if (ab_priv(team)->active_port == port)
+-		rcu_assign_pointer(ab_priv(team)->active_port, NULL);
++		RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
+ }
+ 
+ static int ab_active_port_get(struct team *team, void *arg)

Added: dists/sid/linux/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,132 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 8 Dec 2011 04:11:17 +0000
+Subject: [09/23] net: introduce vlan_vid_[add/del] and use them instead of
+ direct [add/kill]_vid ndo calls
+
+commit 87002b03baabd2b8f6281ab6411ed88d24958de1 upstream.
+
+This patch adds wrapper for ndo_vlan_rx_add_vid/ndo_vlan_rx_kill_vid
+functions. Check for NETIF_F_HW_VLAN_FILTER feature is done in this
+wrapper.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Drop changes to bonding, macvlan, vlan; assume
+ ndo_vlan_rx_{add,kill}_vid still return void]
+---
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -18,6 +18,7 @@
+ #include <linux/ctype.h>
+ #include <linux/notifier.h>
+ #include <linux/netdevice.h>
++#include <linux/if_vlan.h>
+ #include <linux/if_arp.h>
+ #include <linux/socket.h>
+ #include <linux/etherdevice.h>
+@@ -906,15 +907,26 @@
+ {
+ 	struct team *team = netdev_priv(dev);
+ 	struct team_port *port;
++	int err;
+ 
+-	rcu_read_lock();
+-	list_for_each_entry_rcu(port, &team->port_list, list) {
+-		const struct net_device_ops *ops = port->dev->netdev_ops;
+-
+-		if (ops->ndo_vlan_rx_add_vid)
+-			ops->ndo_vlan_rx_add_vid(port->dev, vid);
++	/*
++	 * Alhough this is reader, it's guarded by team lock. It's not possible
++	 * to traverse list in reverse under rcu_read_lock
++	 */
++	mutex_lock(&team->lock);
++	list_for_each_entry(port, &team->port_list, list) {
++		err = vlan_vid_add(port->dev, vid);
++		if (err)
++			goto unwind;
+ 	}
+-	rcu_read_unlock();
++	mutex_unlock(&team->lock);
++
++	return;
++
++unwind:
++	list_for_each_entry_continue_reverse(port, &team->port_list, list)
++		vlan_vid_del(port->dev, vid);
++	mutex_unlock(&team->lock);
+ }
+ 
+ static void team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
+@@ -923,12 +935,8 @@
+ 	struct team_port *port;
+ 
+ 	rcu_read_lock();
+-	list_for_each_entry_rcu(port, &team->port_list, list) {
+-		const struct net_device_ops *ops = port->dev->netdev_ops;
+-
+-		if (ops->ndo_vlan_rx_kill_vid)
+-			ops->ndo_vlan_rx_kill_vid(port->dev, vid);
+-	}
++	list_for_each_entry_rcu(port, &team->port_list, list)
++		vlan_vid_del(port->dev, vid);
+ 	rcu_read_unlock();
+ }
+ 
+--- a/include/linux/if_vlan.h
++++ b/include/linux/if_vlan.h
+@@ -109,6 +109,9 @@
+ extern bool vlan_do_receive(struct sk_buff **skb, bool last_handler);
+ extern struct sk_buff *vlan_untag(struct sk_buff *skb);
+ 
++extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
++extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
++
+ #else
+ static inline struct net_device *
+ __vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id)
+@@ -139,6 +142,15 @@
+ {
+ 	return skb;
+ }
++
++static inline int vlan_vid_add(struct net_device *dev, unsigned short vid)
++{
++	return 0;
++}
++
++static inline void vlan_vid_del(struct net_device *dev, unsigned short vid)
++{
++}
+ #endif
+ 
+ /**
+--- a/net/8021q/vlan_core.c
++++ b/net/8021q/vlan_core.c
+@@ -179,3 +179,26 @@
+ 	kfree_skb(skb);
+ 	return NULL;
+ }
++
++int vlan_vid_add(struct net_device *dev, unsigned short vid)
++{
++	const struct net_device_ops *ops = dev->netdev_ops;
++
++	if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
++	     ops->ndo_vlan_rx_add_vid) {
++		ops->ndo_vlan_rx_add_vid(dev, vid);
++	}
++	return 0;
++}
++EXPORT_SYMBOL(vlan_vid_add);
++
++void vlan_vid_del(struct net_device *dev, unsigned short vid)
++{
++	const struct net_device_ops *ops = dev->netdev_ops;
++
++	if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
++	     ops->ndo_vlan_rx_kill_vid) {
++		ops->ndo_vlan_rx_kill_vid(dev, vid);
++	}
++}
++EXPORT_SYMBOL(vlan_vid_del);

Added: dists/sid/linux/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,81 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 8 Dec 2011 04:11:19 +0000
+Subject: [10/23] vlan: introduce functions to do mass addition/deletion of vids by
+ another device
+
+commit 348a1443cc4303c72cf1ee3b26e476fec8e7b5fa upstream.
+
+Introduce functions handy to copy vlan ids from one driver's list to
+another.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Assume ndo_vlan_rx_add_id still returns void]
+---
+--- a/include/linux/if_vlan.h
++++ b/include/linux/if_vlan.h
+@@ -112,6 +112,10 @@
+ extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
+ extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
+ 
++extern int vlan_vids_add_by_dev(struct net_device *dev,
++				const struct net_device *by_dev);
++extern void vlan_vids_del_by_dev(struct net_device *dev,
++				 const struct net_device *by_dev);
+ #else
+ static inline struct net_device *
+ __vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id)
+@@ -151,6 +155,16 @@
+ static inline void vlan_vid_del(struct net_device *dev, unsigned short vid)
+ {
+ }
++
++static inline int vlan_vids_add_by_dev(struct net_device *dev,
++				       const struct net_device *by_dev)
++{
++}
++
++static inline void vlan_vids_del_by_dev(struct net_device *dev,
++					const struct net_device *by_dev)
++{
++}
+ #endif
+ 
+ /**
+--- a/net/8021q/vlan_core.c
++++ b/net/8021q/vlan_core.c
+@@ -202,3 +202,34 @@
+ 	}
+ }
+ EXPORT_SYMBOL(vlan_vid_del);
++
++int vlan_vids_add_by_dev(struct net_device *dev,
++			 const struct net_device *by_dev)
++{
++	struct vlan_vid_info *vid_info;
++
++	ASSERT_RTNL();
++
++	if (!by_dev->vlan_info)
++		return;
++
++	list_for_each_entry(vid_info, &by_dev->vlan_info->vid_list, list) {
++		vlan_vid_add(dev, vid_info->vid);
++	}
++	return 0;
++}
++EXPORT_SYMBOL(vlan_vids_add_by_dev);
++
++void vlan_vids_del_by_dev(struct net_device *dev,
++			  const struct net_device *by_dev)
++{
++	struct vlan_vid_info *vid_info;
++
++	ASSERT_RTNL();
++
++	if (!by_dev->vlan_info)
++		return;
++
++	list_for_each_entry(vid_info, &by_dev->vlan_info->vid_list, list)
++		vlan_vid_del(dev, vid_info->vid);
++}

Added: dists/sid/linux/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,53 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Thu, 8 Dec 2011 04:11:20 +0000
+Subject: [11/23] team: use vlan_vids_[addr/del]_by_dev
+
+commit 57459185a19b0246866479522b77cbb9732201d1 upstream.
+
+So far when vlan id was added to team device befor port was added, this
+vid was not added to port's vlan filter. Also after removal, vid stayed
+in port device's vlan filter. Benefit of new vlan functions to handle
+this work.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 79c2d1b..ed2a862 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -588,6 +588,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
+ 		goto err_dev_open;
+ 	}
+ 
++	err = vlan_vids_add_by_dev(port_dev, dev);
++	if (err) {
++		netdev_err(dev, "Failed to add vlan ids to device %s\n",
++				portname);
++		goto err_vids_add;
++	}
++
+ 	err = netdev_set_master(port_dev, dev);
+ 	if (err) {
+ 		netdev_err(dev, "Device %s failed to set master\n", portname);
+@@ -615,6 +622,9 @@ err_handler_register:
+ 	netdev_set_master(port_dev, NULL);
+ 
+ err_set_master:
++	vlan_vids_del_by_dev(port_dev, dev);
++
++err_vids_add:
+ 	dev_close(port_dev);
+ 
+ err_dev_open:
+@@ -648,6 +658,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 	team_adjust_ops(team);
+ 	netdev_rx_handler_unregister(port_dev);
+ 	netdev_set_master(port_dev, NULL);
++	vlan_vids_del_by_dev(port_dev, dev);
+ 	dev_close(port_dev);
+ 	team_port_leave(team, port);
+ 	team_port_set_orig_mac(port);

Added: dists/sid/linux/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,371 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 24 Jan 2012 05:16:00 +0000
+Subject: [12/23] team: send only changed options/ports via netlink
+
+commit b82b9183d4f18f9b8c4bb31f223eb6c79b734eb0 upstream.
+
+This patch changes event message behaviour to send only updated records
+instead of whole list. This fixes bug on which userspace receives non-actual
+data in case multiple events occur in row.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |  136 +++++++++++++++++++++++++++++++----------------
+ include/linux/if_team.h |   10 ++++
+ 2 files changed, 100 insertions(+), 46 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index ed2a862..6b678f3 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
+ 	return NULL;
+ }
+ 
+-int team_options_register(struct team *team,
+-			  const struct team_option *option,
+-			  size_t option_count)
++int __team_options_register(struct team *team,
++			    const struct team_option *option,
++			    size_t option_count)
+ {
+ 	int i;
+ 	struct team_option **dst_opts;
+@@ -116,8 +116,11 @@ int team_options_register(struct team *team,
+ 		}
+ 	}
+ 
+-	for (i = 0; i < option_count; i++)
++	for (i = 0; i < option_count; i++) {
++		dst_opts[i]->changed = true;
++		dst_opts[i]->removed = false;
+ 		list_add_tail(&dst_opts[i]->list, &team->option_list);
++	}
+ 
+ 	kfree(dst_opts);
+ 	return 0;
+@@ -130,10 +133,22 @@ rollback:
+ 	return err;
+ }
+ 
+-EXPORT_SYMBOL(team_options_register);
++static void __team_options_mark_removed(struct team *team,
++					const struct team_option *option,
++					size_t option_count)
++{
++	int i;
++
++	for (i = 0; i < option_count; i++, option++) {
++		struct team_option *del_opt;
+ 
+-static void __team_options_change_check(struct team *team,
+-					struct team_option *changed_option);
++		del_opt = __team_find_option(team, option->name);
++		if (del_opt) {
++			del_opt->changed = true;
++			del_opt->removed = true;
++		}
++	}
++}
+ 
+ static void __team_options_unregister(struct team *team,
+ 				      const struct team_option *option,
+@@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team,
+ 	}
+ }
+ 
++static void __team_options_change_check(struct team *team);
++
++int team_options_register(struct team *team,
++			  const struct team_option *option,
++			  size_t option_count)
++{
++	int err;
++
++	err = __team_options_register(team, option, option_count);
++	if (err)
++		return err;
++	__team_options_change_check(team);
++	return 0;
++}
++EXPORT_SYMBOL(team_options_register);
++
+ void team_options_unregister(struct team *team,
+ 			     const struct team_option *option,
+ 			     size_t option_count)
+ {
++	__team_options_mark_removed(team, option, option_count);
++	__team_options_change_check(team);
+ 	__team_options_unregister(team, option, option_count);
+-	__team_options_change_check(team, NULL);
+ }
+ EXPORT_SYMBOL(team_options_unregister);
+ 
+@@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option,
+ 	if (err)
+ 		return err;
+ 
+-	__team_options_change_check(team, option);
++	option->changed = true;
++	__team_options_change_check(team);
+ 	return err;
+ }
+ 
+@@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 		return -ENOENT;
+ 	}
+ 
++	port->removed = true;
+ 	__team_port_change_check(port, false);
+ 	team_port_list_del_port(team, port);
+ 	team_adjust_ops(team);
+@@ -1200,10 +1234,9 @@ err_fill:
+ 	return err;
+ }
+ 
+-static int team_nl_fill_options_get_changed(struct sk_buff *skb,
+-					    u32 pid, u32 seq, int flags,
+-					    struct team *team,
+-					    struct team_option *changed_option)
++static int team_nl_fill_options_get(struct sk_buff *skb,
++				    u32 pid, u32 seq, int flags,
++				    struct team *team, bool fillall)
+ {
+ 	struct nlattr *option_list;
+ 	void *hdr;
+@@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
+ 		struct nlattr *option_item;
+ 		long arg;
+ 
++		/* Include only changed options if fill all mode is not on */
++		if (!fillall && !option->changed)
++			continue;
+ 		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
+ 		if (!option_item)
+ 			goto nla_put_failure;
+ 		NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
+-		if (option == changed_option)
++		if (option->changed) {
+ 			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
++			option->changed = false;
++		}
++		if (option->removed)
++			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
+ 		switch (option->type) {
+ 		case TEAM_OPTION_TYPE_U32:
+ 			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
+@@ -1255,13 +1295,13 @@ nla_put_failure:
+ 	return -EMSGSIZE;
+ }
+ 
+-static int team_nl_fill_options_get(struct sk_buff *skb,
+-				    struct genl_info *info, int flags,
+-				    struct team *team)
++static int team_nl_fill_options_get_all(struct sk_buff *skb,
++					struct genl_info *info, int flags,
++					struct team *team)
+ {
+-	return team_nl_fill_options_get_changed(skb, info->snd_pid,
+-						info->snd_seq, NLM_F_ACK,
+-						team, NULL);
++	return team_nl_fill_options_get(skb, info->snd_pid,
++					info->snd_seq, NLM_F_ACK,
++					team, true);
+ }
+ 
+ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+@@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+ 	if (!team)
+ 		return -EINVAL;
+ 
+-	err = team_nl_send_generic(info, team, team_nl_fill_options_get);
++	err = team_nl_send_generic(info, team, team_nl_fill_options_get_all);
+ 
+ 	team_nl_team_put(team);
+ 
+@@ -1365,10 +1405,10 @@ team_put:
+ 	return err;
+ }
+ 
+-static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
+-					      u32 pid, u32 seq, int flags,
+-					      struct team *team,
+-					      struct team_port *changed_port)
++static int team_nl_fill_port_list_get(struct sk_buff *skb,
++				      u32 pid, u32 seq, int flags,
++				      struct team *team,
++				      bool fillall)
+ {
+ 	struct nlattr *port_list;
+ 	void *hdr;
+@@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
+ 	list_for_each_entry(port, &team->port_list, list) {
+ 		struct nlattr *port_item;
+ 
++		/* Include only changed ports if fill all mode is not on */
++		if (!fillall && !port->changed)
++			continue;
+ 		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
+ 		if (!port_item)
+ 			goto nla_put_failure;
+ 		NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
+-		if (port == changed_port)
++		if (port->changed) {
+ 			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
++			port->changed = false;
++		}
++		if (port->removed)
++			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
+ 		if (port->linkup)
+ 			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
+ 		NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
+@@ -1408,13 +1455,13 @@ nla_put_failure:
+ 	return -EMSGSIZE;
+ }
+ 
+-static int team_nl_fill_port_list_get(struct sk_buff *skb,
+-				      struct genl_info *info, int flags,
+-				      struct team *team)
++static int team_nl_fill_port_list_get_all(struct sk_buff *skb,
++					  struct genl_info *info, int flags,
++					  struct team *team)
+ {
+-	return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
+-						  info->snd_seq, NLM_F_ACK,
+-						  team, NULL);
++	return team_nl_fill_port_list_get(skb, info->snd_pid,
++					  info->snd_seq, NLM_F_ACK,
++					  team, true);
+ }
+ 
+ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+@@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+ 	if (!team)
+ 		return -EINVAL;
+ 
+-	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
++	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all);
+ 
+ 	team_nl_team_put(team);
+ 
+@@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = {
+ 	.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
+ };
+ 
+-static int team_nl_send_event_options_get(struct team *team,
+-					  struct team_option *changed_option)
++static int team_nl_send_event_options_get(struct team *team)
+ {
+ 	struct sk_buff *skb;
+ 	int err;
+@@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team,
+ 	if (!skb)
+ 		return -ENOMEM;
+ 
+-	err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
+-					       changed_option);
++	err = team_nl_fill_options_get(skb, 0, 0, 0, team, false);
+ 	if (err < 0)
+ 		goto err_fill;
+ 
+@@ -1489,18 +1534,17 @@ err_fill:
+ 	return err;
+ }
+ 
+-static int team_nl_send_event_port_list_get(struct team_port *port)
++static int team_nl_send_event_port_list_get(struct team *team)
+ {
+ 	struct sk_buff *skb;
+ 	int err;
+-	struct net *net = dev_net(port->team->dev);
++	struct net *net = dev_net(team->dev);
+ 
+ 	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ 	if (!skb)
+ 		return -ENOMEM;
+ 
+-	err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
+-						 port->team, port);
++	err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false);
+ 	if (err < 0)
+ 		goto err_fill;
+ 
+@@ -1544,12 +1588,11 @@ static void team_nl_fini(void)
+  * Change checkers
+  ******************/
+ 
+-static void __team_options_change_check(struct team *team,
+-					struct team_option *changed_option)
++static void __team_options_change_check(struct team *team)
+ {
+ 	int err;
+ 
+-	err = team_nl_send_event_options_get(team, changed_option);
++	err = team_nl_send_event_options_get(team);
+ 	if (err)
+ 		netdev_warn(team->dev, "Failed to send options change via netlink\n");
+ }
+@@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
+ {
+ 	int err;
+ 
+-	if (port->linkup == linkup)
++	if (!port->removed && port->linkup == linkup)
+ 		return;
+ 
++	port->changed = true;
+ 	port->linkup = linkup;
+ 	if (linkup) {
+ 		struct ethtool_cmd ecmd;
+@@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
+ 	port->duplex = 0;
+ 
+ send_event:
+-	err = team_nl_send_event_port_list_get(port);
++	err = team_nl_send_event_port_list_get(port->team);
+ 	if (err)
+ 		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
+ 			    port->dev->name);
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 828181f..58404b0 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -46,6 +46,10 @@ struct team_port {
+ 	u32 speed;
+ 	u8 duplex;
+ 
++	/* Custom gennetlink interface related flags */
++	bool changed;
++	bool removed;
++
+ 	struct rcu_head rcu;
+ };
+ 
+@@ -72,6 +76,10 @@ struct team_option {
+ 	enum team_option_type type;
+ 	int (*getter)(struct team *team, void *arg);
+ 	int (*setter)(struct team *team, void *arg);
++
++	/* Custom gennetlink interface related flags */
++	bool changed;
++	bool removed;
+ };
+ 
+ struct team_mode {
+@@ -207,6 +215,7 @@ enum {
+ 	TEAM_ATTR_OPTION_CHANGED,	/* flag */
+ 	TEAM_ATTR_OPTION_TYPE,		/* u8 */
+ 	TEAM_ATTR_OPTION_DATA,		/* dynamic */
++	TEAM_ATTR_OPTION_REMOVED,	/* flag */
+ 
+ 	__TEAM_ATTR_OPTION_MAX,
+ 	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
+@@ -227,6 +236,7 @@ enum {
+ 	TEAM_ATTR_PORT_LINKUP,		/* flag */
+ 	TEAM_ATTR_PORT_SPEED,		/* u32 */
+ 	TEAM_ATTR_PORT_DUPLEX,		/* u8 */
++	TEAM_ATTR_PORT_REMOVED,		/* flag */
+ 
+ 	__TEAM_ATTR_PORT_MAX,
+ 	TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,

Added: dists/sid/linux/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,108 @@
+From: "David S. Miller" <davem at davemloft.net>
+Date: Sun, 1 Apr 2012 20:25:18 -0400
+Subject: [13/23] team: Stop using NLA_PUT*().
+
+commit 86ebb02dc793058ea17ad647c802b507dafff7cb upstream.
+
+These macros contain a hidden goto, and are thus extremely error
+prone and make code hard to audit.
+
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   50 +++++++++++++++++++++++++++++------------------
+ 1 file changed, 31 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 8f81805..0db6e66 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -1248,7 +1248,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 	if (IS_ERR(hdr))
+ 		return PTR_ERR(hdr);
+ 
+-	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
++	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
++		goto nla_put_failure;
+ 	option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
+ 	if (!option_list)
+ 		return -EMSGSIZE;
+@@ -1263,24 +1264,31 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
+ 		if (!option_item)
+ 			goto nla_put_failure;
+-		NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
++		if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
++			goto nla_put_failure;
+ 		if (option->changed) {
+-			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
++			if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
++				goto nla_put_failure;
+ 			option->changed = false;
+ 		}
+-		if (option->removed)
+-			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
++		if (option->removed &&
++		    nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
++			goto nla_put_failure;
+ 		switch (option->type) {
+ 		case TEAM_OPTION_TYPE_U32:
+-			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
++			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
++				goto nla_put_failure;
+ 			team_option_get(team, option, &arg);
+-			NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
++			if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg))
++				goto nla_put_failure;
+ 			break;
+ 		case TEAM_OPTION_TYPE_STRING:
+-			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
++			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
++				goto nla_put_failure;
+ 			team_option_get(team, option, &arg);
+-			NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
+-				       (char *) arg);
++			if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
++					   (char *) arg))
++				goto nla_put_failure;
+ 			break;
+ 		default:
+ 			BUG();
+@@ -1420,7 +1428,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
+ 	if (IS_ERR(hdr))
+ 		return PTR_ERR(hdr);
+ 
+-	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
++	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
++		goto nla_put_failure;
+ 	port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
+ 	if (!port_list)
+ 		return -EMSGSIZE;
+@@ -1434,17 +1443,20 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
+ 		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
+ 		if (!port_item)
+ 			goto nla_put_failure;
+-		NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
++		if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
++			goto nla_put_failure;
+ 		if (port->changed) {
+-			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
++			if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
++				goto nla_put_failure;
+ 			port->changed = false;
+ 		}
+-		if (port->removed)
+-			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
+-		if (port->linkup)
+-			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
+-		NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
+-		NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
++		if ((port->removed &&
++		     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
++		    (port->linkup &&
++		     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
++		    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->speed) ||
++		    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex))
++			goto nla_put_failure;
+ 		nla_nest_end(skb, port_item);
+ 	}
+ 

Added: dists/sid/linux/debian/patches/features/all/team/0014-team-add-binary-option-type.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0014-team-add-binary-option-type.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,124 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Wed, 4 Apr 2012 12:16:26 +0000
+Subject: [14/23] team: add binary option type
+
+commit 2615598fc100451c71b83d06bdf5faead619a40e upstream.
+
+For transfering generic binary data (e.g. BPF code), introduce new
+binary option type.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   30 ++++++++++++++++++++++++++----
+ include/linux/if_team.h |    8 ++++++++
+ 2 files changed, 34 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 0db6e66..ea96f82 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -1145,10 +1145,7 @@ team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
+ 	},
+ 	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
+ 	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
+-	[TEAM_ATTR_OPTION_DATA] = {
+-		.type = NLA_BINARY,
+-		.len = TEAM_STRING_MAX_LEN,
+-	},
++	[TEAM_ATTR_OPTION_DATA]			= { .type = NLA_BINARY },
+ };
+ 
+ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+@@ -1257,6 +1254,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 	list_for_each_entry(option, &team->option_list, list) {
+ 		struct nlattr *option_item;
+ 		long arg;
++		struct team_option_binary tbinary;
+ 
+ 		/* Include only changed options if fill all mode is not on */
+ 		if (!fillall && !option->changed)
+@@ -1290,6 +1288,15 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 					   (char *) arg))
+ 				goto nla_put_failure;
+ 			break;
++		case TEAM_OPTION_TYPE_BINARY:
++			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
++				goto nla_put_failure;
++			arg = (long) &tbinary;
++			team_option_get(team, option, &arg);
++			if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
++				    tbinary.data_len, tbinary.data))
++				goto nla_put_failure;
++			break;
+ 		default:
+ 			BUG();
+ 		}
+@@ -1374,6 +1381,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 		case NLA_STRING:
+ 			opt_type = TEAM_OPTION_TYPE_STRING;
+ 			break;
++		case NLA_BINARY:
++			opt_type = TEAM_OPTION_TYPE_BINARY;
++			break;
+ 		default:
+ 			goto team_put;
+ 		}
+@@ -1382,19 +1392,31 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 		list_for_each_entry(option, &team->option_list, list) {
+ 			long arg;
+ 			struct nlattr *opt_data_attr;
++			struct team_option_binary tbinary;
++			int data_len;
+ 
+ 			if (option->type != opt_type ||
+ 			    strcmp(option->name, opt_name))
+ 				continue;
+ 			opt_found = true;
+ 			opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
++			data_len = nla_len(opt_data_attr);
+ 			switch (opt_type) {
+ 			case TEAM_OPTION_TYPE_U32:
+ 				arg = nla_get_u32(opt_data_attr);
+ 				break;
+ 			case TEAM_OPTION_TYPE_STRING:
++				if (data_len > TEAM_STRING_MAX_LEN) {
++					err = -EINVAL;
++					goto team_put;
++				}
+ 				arg = (long) nla_data(opt_data_attr);
+ 				break;
++			case TEAM_OPTION_TYPE_BINARY:
++				tbinary.data_len = data_len;
++				tbinary.data = nla_data(opt_data_attr);
++				arg = (long) &tbinary;
++				break;
+ 			default:
+ 				BUG();
+ 			}
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 58404b0..41163ac 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -68,6 +68,7 @@ struct team_mode_ops {
+ enum team_option_type {
+ 	TEAM_OPTION_TYPE_U32,
+ 	TEAM_OPTION_TYPE_STRING,
++	TEAM_OPTION_TYPE_BINARY,
+ };
+ 
+ struct team_option {
+@@ -82,6 +83,13 @@ struct team_option {
+ 	bool removed;
+ };
+ 
++struct team_option_binary {
++	u32 data_len;
++	void *data;
++};
++
++#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
++
+ struct team_mode {
+ 	struct list_head list;
+ 	const char *kind;

Added: dists/sid/linux/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,241 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Wed, 4 Apr 2012 12:16:27 +0000
+Subject: [15/23] team: add loadbalance mode
+
+commit 01d7f30a9f962573b6c91ed520c73fb30658d826 upstream.
+
+This patch introduces new team mode. It's TX port is selected by
+user-set BPF hash function.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/Kconfig                 |   11 ++
+ drivers/net/team/Makefile                |    1 +
+ drivers/net/team/team_mode_loadbalance.c |  188 ++++++++++++++++++++++++++++++
+ 3 files changed, 200 insertions(+)
+ create mode 100644 drivers/net/team/team_mode_loadbalance.c
+
+diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
+index 248a144..89024d5 100644
+--- a/drivers/net/team/Kconfig
++++ b/drivers/net/team/Kconfig
+@@ -40,4 +40,15 @@ config NET_TEAM_MODE_ACTIVEBACKUP
+ 	  To compile this team mode as a module, choose M here: the module
+ 	  will be called team_mode_activebackup.
+ 
++config NET_TEAM_MODE_LOADBALANCE
++	tristate "Load-balance mode support"
++	depends on NET_TEAM
++	---help---
++	  This mode provides load balancing functionality. Tx port selection
++	  is done using BPF function set up from userspace (bpf_hash_func
++	  option)
++
++	  To compile this team mode as a module, choose M here: the module
++	  will be called team_mode_loadbalance.
++
+ endif # NET_TEAM
+diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
+index 85f2028..fb9f4c1 100644
+--- a/drivers/net/team/Makefile
++++ b/drivers/net/team/Makefile
+@@ -5,3 +5,4 @@
+ obj-$(CONFIG_NET_TEAM) += team.o
+ obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
+ obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
++obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o
+diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
+new file mode 100644
+index 0000000..ed20f39
+--- /dev/null
++++ b/drivers/net/team/team_mode_loadbalance.c
+@@ -0,0 +1,188 @@
++/*
++ * drivers/net/team/team_mode_loadbalance.c - Load-balancing mode for team
++ * Copyright (c) 2012 Jiri Pirko <jpirko at redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/filter.h>
++#include <linux/if_team.h>
++
++struct lb_priv {
++	struct sk_filter __rcu *fp;
++	struct sock_fprog *orig_fprog;
++};
++
++static struct lb_priv *lb_priv(struct team *team)
++{
++	return (struct lb_priv *) &team->mode_priv;
++}
++
++static bool lb_transmit(struct team *team, struct sk_buff *skb)
++{
++	struct sk_filter *fp;
++	struct team_port *port;
++	unsigned int hash;
++	int port_index;
++
++	fp = rcu_dereference(lb_priv(team)->fp);
++	if (unlikely(!fp))
++		goto drop;
++	hash = SK_RUN_FILTER(fp, skb);
++	port_index = hash % team->port_count;
++	port = team_get_port_by_index_rcu(team, port_index);
++	if (unlikely(!port))
++		goto drop;
++	skb->dev = port->dev;
++	if (dev_queue_xmit(skb))
++		return false;
++	return true;
++
++drop:
++	dev_kfree_skb_any(skb);
++	return false;
++}
++
++static int lb_bpf_func_get(struct team *team, void *arg)
++{
++	struct team_option_binary *tbinary = team_optarg_tbinary(arg);
++
++	memset(tbinary, 0, sizeof(*tbinary));
++	if (!lb_priv(team)->orig_fprog)
++		return 0;
++
++	tbinary->data_len = lb_priv(team)->orig_fprog->len *
++			    sizeof(struct sock_filter);
++	tbinary->data = lb_priv(team)->orig_fprog->filter;
++	return 0;
++}
++
++static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
++			  void *data)
++{
++	struct sock_fprog *fprog;
++	struct sock_filter *filter = (struct sock_filter *) data;
++
++	if (data_len % sizeof(struct sock_filter))
++		return -EINVAL;
++	fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL);
++	if (!fprog)
++		return -ENOMEM;
++	fprog->filter = kmemdup(filter, data_len, GFP_KERNEL);
++	if (!fprog->filter) {
++		kfree(fprog);
++		return -ENOMEM;
++	}
++	fprog->len = data_len / sizeof(struct sock_filter);
++	*pfprog = fprog;
++	return 0;
++}
++
++static void __fprog_destroy(struct sock_fprog *fprog)
++{
++	kfree(fprog->filter);
++	kfree(fprog);
++}
++
++static int lb_bpf_func_set(struct team *team, void *arg)
++{
++	struct team_option_binary *tbinary = team_optarg_tbinary(arg);
++	struct sk_filter *fp = NULL;
++	struct sock_fprog *fprog = NULL;
++	int err;
++
++	if (tbinary->data_len) {
++		err = __fprog_create(&fprog, tbinary->data_len,
++				     tbinary->data);
++		if (err)
++			return err;
++		err = sk_unattached_filter_create(&fp, fprog);
++		if (err) {
++			__fprog_destroy(fprog);
++			return err;
++		}
++	}
++
++	if (lb_priv(team)->orig_fprog) {
++		/* Clear old filter data */
++		__fprog_destroy(lb_priv(team)->orig_fprog);
++		sk_unattached_filter_destroy(lb_priv(team)->fp);
++	}
++
++	rcu_assign_pointer(lb_priv(team)->fp, fp);
++	lb_priv(team)->orig_fprog = fprog;
++	return 0;
++}
++
++static const struct team_option lb_options[] = {
++	{
++		.name = "bpf_hash_func",
++		.type = TEAM_OPTION_TYPE_BINARY,
++		.getter = lb_bpf_func_get,
++		.setter = lb_bpf_func_set,
++	},
++};
++
++int lb_init(struct team *team)
++{
++	return team_options_register(team, lb_options,
++				     ARRAY_SIZE(lb_options));
++}
++
++void lb_exit(struct team *team)
++{
++	team_options_unregister(team, lb_options,
++				ARRAY_SIZE(lb_options));
++}
++
++static int lb_port_enter(struct team *team, struct team_port *port)
++{
++	return team_port_set_team_mac(port);
++}
++
++static void lb_port_change_mac(struct team *team, struct team_port *port)
++{
++	team_port_set_team_mac(port);
++}
++
++static const struct team_mode_ops lb_mode_ops = {
++	.init			= lb_init,
++	.exit			= lb_exit,
++	.transmit		= lb_transmit,
++	.port_enter		= lb_port_enter,
++	.port_change_mac	= lb_port_change_mac,
++};
++
++static struct team_mode lb_mode = {
++	.kind		= "loadbalance",
++	.owner		= THIS_MODULE,
++	.priv_size	= sizeof(struct lb_priv),
++	.ops		= &lb_mode_ops,
++};
++
++static int __init lb_init_module(void)
++{
++	return team_mode_register(&lb_mode);
++}
++
++static void __exit lb_cleanup_module(void)
++{
++	team_mode_unregister(&lb_mode);
++}
++
++module_init(lb_init_module);
++module_exit(lb_cleanup_module);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Jiri Pirko <jpirko at redhat.com>");
++MODULE_DESCRIPTION("Load-balancing mode for team");
++MODULE_ALIAS("team-mode-loadbalance");

Added: dists/sid/linux/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,687 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 10 Apr 2012 05:15:42 +0000
+Subject: [16/23] team: add support for per-port options
+
+commit 80f7c6683fe0e891ef1db7c967d538b5fdddd22c upstream.
+
+This patch allows to create per-port options. That becomes handy for all
+sorts of stuff, for example for userspace driven link-state, 802.3ad
+implementation and so on.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c                   |  306 +++++++++++++++++++++++------
+ drivers/net/team/team_mode_activebackup.c |   14 +-
+ drivers/net/team/team_mode_loadbalance.c  |   28 ++-
+ include/linux/if_team.h                   |   30 +--
+ 4 files changed, 278 insertions(+), 100 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index ea96f82..eaf8441 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac);
+  * Options handling
+  *******************/
+ 
+-struct team_option *__team_find_option(struct team *team, const char *opt_name)
++struct team_option_inst { /* One for each option instance */
++	struct list_head list;
++	struct team_option *option;
++	struct team_port *port; /* != NULL if per-port */
++	bool changed;
++	bool removed;
++};
++
++static struct team_option *__team_find_option(struct team *team,
++					      const char *opt_name)
+ {
+ 	struct team_option *option;
+ 
+@@ -92,9 +101,121 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
+ 	return NULL;
+ }
+ 
+-int __team_options_register(struct team *team,
+-			    const struct team_option *option,
+-			    size_t option_count)
++static int __team_option_inst_add(struct team *team, struct team_option *option,
++				  struct team_port *port)
++{
++	struct team_option_inst *opt_inst;
++
++	opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
++	if (!opt_inst)
++		return -ENOMEM;
++	opt_inst->option = option;
++	opt_inst->port = port;
++	opt_inst->changed = true;
++	opt_inst->removed = false;
++	list_add_tail(&opt_inst->list, &team->option_inst_list);
++	return 0;
++}
++
++static void __team_option_inst_del(struct team_option_inst *opt_inst)
++{
++	list_del(&opt_inst->list);
++	kfree(opt_inst);
++}
++
++static void __team_option_inst_del_option(struct team *team,
++					  struct team_option *option)
++{
++	struct team_option_inst *opt_inst, *tmp;
++
++	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
++		if (opt_inst->option == option)
++			__team_option_inst_del(opt_inst);
++	}
++}
++
++static int __team_option_inst_add_option(struct team *team,
++					 struct team_option *option)
++{
++	struct team_port *port;
++	int err;
++
++	if (!option->per_port)
++		return __team_option_inst_add(team, option, 0);
++
++	list_for_each_entry(port, &team->port_list, list) {
++		err = __team_option_inst_add(team, option, port);
++		if (err)
++			goto inst_del_option;
++	}
++	return 0;
++
++inst_del_option:
++	__team_option_inst_del_option(team, option);
++	return err;
++}
++
++static void __team_option_inst_mark_removed_option(struct team *team,
++						   struct team_option *option)
++{
++	struct team_option_inst *opt_inst;
++
++	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
++		if (opt_inst->option == option) {
++			opt_inst->changed = true;
++			opt_inst->removed = true;
++		}
++	}
++}
++
++static void __team_option_inst_del_port(struct team *team,
++					struct team_port *port)
++{
++	struct team_option_inst *opt_inst, *tmp;
++
++	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
++		if (opt_inst->option->per_port &&
++		    opt_inst->port == port)
++			__team_option_inst_del(opt_inst);
++	}
++}
++
++static int __team_option_inst_add_port(struct team *team,
++				       struct team_port *port)
++{
++	struct team_option *option;
++	int err;
++
++	list_for_each_entry(option, &team->option_list, list) {
++		if (!option->per_port)
++			continue;
++		err = __team_option_inst_add(team, option, port);
++		if (err)
++			goto inst_del_port;
++	}
++	return 0;
++
++inst_del_port:
++	__team_option_inst_del_port(team, port);
++	return err;
++}
++
++static void __team_option_inst_mark_removed_port(struct team *team,
++						 struct team_port *port)
++{
++	struct team_option_inst *opt_inst;
++
++	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
++		if (opt_inst->port == port) {
++			opt_inst->changed = true;
++			opt_inst->removed = true;
++		}
++	}
++}
++
++static int __team_options_register(struct team *team,
++				   const struct team_option *option,
++				   size_t option_count)
+ {
+ 	int i;
+ 	struct team_option **dst_opts;
+@@ -107,26 +228,32 @@ int __team_options_register(struct team *team,
+ 	for (i = 0; i < option_count; i++, option++) {
+ 		if (__team_find_option(team, option->name)) {
+ 			err = -EEXIST;
+-			goto rollback;
++			goto alloc_rollback;
+ 		}
+ 		dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
+ 		if (!dst_opts[i]) {
+ 			err = -ENOMEM;
+-			goto rollback;
++			goto alloc_rollback;
+ 		}
+ 	}
+ 
+ 	for (i = 0; i < option_count; i++) {
+-		dst_opts[i]->changed = true;
+-		dst_opts[i]->removed = false;
++		err = __team_option_inst_add_option(team, dst_opts[i]);
++		if (err)
++			goto inst_rollback;
+ 		list_add_tail(&dst_opts[i]->list, &team->option_list);
+ 	}
+ 
+ 	kfree(dst_opts);
+ 	return 0;
+ 
+-rollback:
+-	for (i = 0; i < option_count; i++)
++inst_rollback:
++	for (i--; i >= 0; i--)
++		__team_option_inst_del_option(team, dst_opts[i]);
++
++	i = option_count - 1;
++alloc_rollback:
++	for (i--; i >= 0; i--)
+ 		kfree(dst_opts[i]);
+ 
+ 	kfree(dst_opts);
+@@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team,
+ 		struct team_option *del_opt;
+ 
+ 		del_opt = __team_find_option(team, option->name);
+-		if (del_opt) {
+-			del_opt->changed = true;
+-			del_opt->removed = true;
+-		}
++		if (del_opt)
++			__team_option_inst_mark_removed_option(team, del_opt);
+ 	}
+ }
+ 
+@@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team,
+ 
+ 		del_opt = __team_find_option(team, option->name);
+ 		if (del_opt) {
++			__team_option_inst_del_option(team, del_opt);
+ 			list_del(&del_opt->list);
+ 			kfree(del_opt);
+ 		}
+@@ -193,22 +319,42 @@ void team_options_unregister(struct team *team,
+ }
+ EXPORT_SYMBOL(team_options_unregister);
+ 
+-static int team_option_get(struct team *team, struct team_option *option,
+-			   void *arg)
++static int team_option_port_add(struct team *team, struct team_port *port)
+ {
+-	return option->getter(team, arg);
++	int err;
++
++	err = __team_option_inst_add_port(team, port);
++	if (err)
++		return err;
++	__team_options_change_check(team);
++	return 0;
+ }
+ 
+-static int team_option_set(struct team *team, struct team_option *option,
+-			   void *arg)
++static void team_option_port_del(struct team *team, struct team_port *port)
++{
++	__team_option_inst_mark_removed_port(team, port);
++	__team_options_change_check(team);
++	__team_option_inst_del_port(team, port);
++}
++
++static int team_option_get(struct team *team,
++			   struct team_option_inst *opt_inst,
++			   struct team_gsetter_ctx *ctx)
++{
++	return opt_inst->option->getter(team, ctx);
++}
++
++static int team_option_set(struct team *team,
++			   struct team_option_inst *opt_inst,
++			   struct team_gsetter_ctx *ctx)
+ {
+ 	int err;
+ 
+-	err = option->setter(team, arg);
++	err = opt_inst->option->setter(team, ctx);
+ 	if (err)
+ 		return err;
+ 
+-	option->changed = true;
++	opt_inst->changed = true;
+ 	__team_options_change_check(team);
+ 	return err;
+ }
+@@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
+ 		goto err_handler_register;
+ 	}
+ 
++	err = team_option_port_add(team, port);
++	if (err) {
++		netdev_err(dev, "Device %s failed to add per-port options\n",
++			   portname);
++		goto err_option_port_add;
++	}
++
+ 	team_port_list_add_port(team, port);
+ 	team_adjust_ops(team);
+ 	__team_compute_features(team);
+@@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
+ 
+ 	return 0;
+ 
++err_option_port_add:
++	netdev_rx_handler_unregister(port_dev);
++
+ err_handler_register:
+ 	netdev_set_master(port_dev, NULL);
+ 
+@@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 	__team_port_change_check(port, false);
+ 	team_port_list_del_port(team, port);
+ 	team_adjust_ops(team);
++	team_option_port_del(team, port);
+ 	netdev_rx_handler_unregister(port_dev);
+ 	netdev_set_master(port_dev, NULL);
+ 	vlan_vids_del_by_dev(port_dev, dev);
+@@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 
+ static const char team_no_mode_kind[] = "*NOMODE*";
+ 
+-static int team_mode_option_get(struct team *team, void *arg)
++static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	const char **str = arg;
+-
+-	*str = team->mode ? team->mode->kind : team_no_mode_kind;
++	ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind;
+ 	return 0;
+ }
+ 
+-static int team_mode_option_set(struct team *team, void *arg)
++static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	const char **str = arg;
+-
+-	return team_change_mode(team, *str);
++	return team_change_mode(team, ctx->data.str_val);
+ }
+ 
+ static const struct team_option team_options[] = {
+@@ -756,6 +909,7 @@ static int team_init(struct net_device *dev)
+ 	team_adjust_ops(team);
+ 
+ 	INIT_LIST_HEAD(&team->option_list);
++	INIT_LIST_HEAD(&team->option_inst_list);
+ 	err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
+ 	if (err)
+ 		goto err_options_register;
+@@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ {
+ 	struct nlattr *option_list;
+ 	void *hdr;
+-	struct team_option *option;
++	struct team_option_inst *opt_inst;
++	int err;
+ 
+ 	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+ 			  TEAM_CMD_OPTIONS_GET);
+@@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 	if (!option_list)
+ 		return -EMSGSIZE;
+ 
+-	list_for_each_entry(option, &team->option_list, list) {
++	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ 		struct nlattr *option_item;
+-		long arg;
+-		struct team_option_binary tbinary;
++		struct team_option *option = opt_inst->option;
++		struct team_gsetter_ctx ctx;
+ 
+ 		/* Include only changed options if fill all mode is not on */
+-		if (!fillall && !option->changed)
++		if (!fillall && !opt_inst->changed)
+ 			continue;
+ 		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
+ 		if (!option_item)
+ 			goto nla_put_failure;
+ 		if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
+ 			goto nla_put_failure;
+-		if (option->changed) {
++		if (opt_inst->changed) {
+ 			if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
+ 				goto nla_put_failure;
+-			option->changed = false;
++			opt_inst->changed = false;
+ 		}
+-		if (option->removed &&
++		if (opt_inst->removed &&
+ 		    nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
+ 			goto nla_put_failure;
++		if (opt_inst->port &&
++		    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
++				opt_inst->port->dev->ifindex))
++			goto nla_put_failure;
++		ctx.port = opt_inst->port;
+ 		switch (option->type) {
+ 		case TEAM_OPTION_TYPE_U32:
+ 			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
+ 				goto nla_put_failure;
+-			team_option_get(team, option, &arg);
+-			if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg))
++			err = team_option_get(team, opt_inst, &ctx);
++			if (err)
++				goto errout;
++			if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA,
++					ctx.data.u32_val))
+ 				goto nla_put_failure;
+ 			break;
+ 		case TEAM_OPTION_TYPE_STRING:
+ 			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
+ 				goto nla_put_failure;
+-			team_option_get(team, option, &arg);
++			err = team_option_get(team, opt_inst, &ctx);
++			if (err)
++				goto errout;
+ 			if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
+-					   (char *) arg))
++					   ctx.data.str_val))
+ 				goto nla_put_failure;
+ 			break;
+ 		case TEAM_OPTION_TYPE_BINARY:
+ 			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
+ 				goto nla_put_failure;
+-			arg = (long) &tbinary;
+-			team_option_get(team, option, &arg);
++			err = team_option_get(team, opt_inst, &ctx);
++			if (err)
++				goto errout;
+ 			if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
+-				    tbinary.data_len, tbinary.data))
++				    ctx.data.bin_val.len, ctx.data.bin_val.ptr))
+ 				goto nla_put_failure;
+ 			break;
+ 		default:
+@@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 	return genlmsg_end(skb, hdr);
+ 
+ nla_put_failure:
++	err = -EMSGSIZE;
++errout:
+ 	genlmsg_cancel(skb, hdr);
+-	return -EMSGSIZE;
++	return err;
+ }
+ 
+ static int team_nl_fill_options_get_all(struct sk_buff *skb,
+@@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 	}
+ 
+ 	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
+-		struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
++		struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
++		struct nlattr *attr_port_ifindex;
+ 		enum team_option_type opt_type;
+-		struct team_option *option;
++		int opt_port_ifindex = 0; /* != 0 for per-port options */
++		struct team_option_inst *opt_inst;
+ 		char *opt_name;
+ 		bool opt_found = false;
+ 
+@@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 			err = -EINVAL;
+ 			goto team_put;
+ 		}
+-		err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
++		err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX,
+ 				       nl_option, team_nl_option_policy);
+ 		if (err)
+ 			goto team_put;
+-		if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
+-		    !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
+-		    !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
++		if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
++		    !opt_attrs[TEAM_ATTR_OPTION_TYPE] ||
++		    !opt_attrs[TEAM_ATTR_OPTION_DATA]) {
+ 			err = -EINVAL;
+ 			goto team_put;
+ 		}
+-		switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
++		switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
+ 		case NLA_U32:
+ 			opt_type = TEAM_OPTION_TYPE_U32;
+ 			break;
+@@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 			goto team_put;
+ 		}
+ 
+-		opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
+-		list_for_each_entry(option, &team->option_list, list) {
+-			long arg;
++		opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
++		attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
++		if (attr_port_ifindex)
++			opt_port_ifindex = nla_get_u32(attr_port_ifindex);
++
++		list_for_each_entry(opt_inst, &team->option_inst_list, list) {
++			struct team_option *option = opt_inst->option;
+ 			struct nlattr *opt_data_attr;
+-			struct team_option_binary tbinary;
++			struct team_gsetter_ctx ctx;
+ 			int data_len;
++			int tmp_ifindex;
+ 
++			tmp_ifindex = opt_inst->port ?
++				      opt_inst->port->dev->ifindex : 0;
+ 			if (option->type != opt_type ||
+-			    strcmp(option->name, opt_name))
++			    strcmp(option->name, opt_name) ||
++			    tmp_ifindex != opt_port_ifindex)
+ 				continue;
+ 			opt_found = true;
+-			opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
++			opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA];
+ 			data_len = nla_len(opt_data_attr);
++			ctx.port = opt_inst->port;
+ 			switch (opt_type) {
+ 			case TEAM_OPTION_TYPE_U32:
+-				arg = nla_get_u32(opt_data_attr);
++				ctx.data.u32_val = nla_get_u32(opt_data_attr);
+ 				break;
+ 			case TEAM_OPTION_TYPE_STRING:
+ 				if (data_len > TEAM_STRING_MAX_LEN) {
+ 					err = -EINVAL;
+ 					goto team_put;
+ 				}
+-				arg = (long) nla_data(opt_data_attr);
++				ctx.data.str_val = nla_data(opt_data_attr);
+ 				break;
+ 			case TEAM_OPTION_TYPE_BINARY:
+-				tbinary.data_len = data_len;
+-				tbinary.data = nla_data(opt_data_attr);
+-				arg = (long) &tbinary;
++				ctx.data.bin_val.len = data_len;
++				ctx.data.bin_val.ptr = nla_data(opt_data_attr);
+ 				break;
+ 			default:
+ 				BUG();
+ 			}
+-			err = team_option_set(team, option, &arg);
++			err = team_option_set(team, opt_inst, &ctx);
+ 			if (err)
+ 				goto team_put;
+ 		}
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+index f4d960e..6cde1ab 100644
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port)
+ 		RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
+ }
+ 
+-static int ab_active_port_get(struct team *team, void *arg)
++static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	u32 *ifindex = arg;
+-
+-	*ifindex = 0;
+ 	if (ab_priv(team)->active_port)
+-		*ifindex = ab_priv(team)->active_port->dev->ifindex;
++		ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex;
++	else
++		ctx->data.u32_val = 0;
+ 	return 0;
+ }
+ 
+-static int ab_active_port_set(struct team *team, void *arg)
++static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	u32 *ifindex = arg;
+ 	struct team_port *port;
+ 
+ 	list_for_each_entry_rcu(port, &team->port_list, list) {
+-		if (port->dev->ifindex == *ifindex) {
++		if (port->dev->ifindex == ctx->data.u32_val) {
+ 			rcu_assign_pointer(ab_priv(team)->active_port, port);
+ 			return 0;
+ 		}
+diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
+index ed20f39..167cdb4 100644
+--- a/drivers/net/team/team_mode_loadbalance.c
++++ b/drivers/net/team/team_mode_loadbalance.c
+@@ -52,22 +52,21 @@ drop:
+ 	return false;
+ }
+ 
+-static int lb_bpf_func_get(struct team *team, void *arg)
++static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	struct team_option_binary *tbinary = team_optarg_tbinary(arg);
+-
+-	memset(tbinary, 0, sizeof(*tbinary));
+-	if (!lb_priv(team)->orig_fprog)
++	if (!lb_priv(team)->orig_fprog) {
++		ctx->data.bin_val.len = 0;
++		ctx->data.bin_val.ptr = NULL;
+ 		return 0;
+-
+-	tbinary->data_len = lb_priv(team)->orig_fprog->len *
+-			    sizeof(struct sock_filter);
+-	tbinary->data = lb_priv(team)->orig_fprog->filter;
++	}
++	ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len *
++				sizeof(struct sock_filter);
++	ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter;
+ 	return 0;
+ }
+ 
+ static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
+-			  void *data)
++			  const void *data)
+ {
+ 	struct sock_fprog *fprog;
+ 	struct sock_filter *filter = (struct sock_filter *) data;
+@@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog)
+ 	kfree(fprog);
+ }
+ 
+-static int lb_bpf_func_set(struct team *team, void *arg)
++static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+-	struct team_option_binary *tbinary = team_optarg_tbinary(arg);
+ 	struct sk_filter *fp = NULL;
+ 	struct sock_fprog *fprog = NULL;
+ 	int err;
+ 
+-	if (tbinary->data_len) {
+-		err = __fprog_create(&fprog, tbinary->data_len,
+-				     tbinary->data);
++	if (ctx->data.bin_val.len) {
++		err = __fprog_create(&fprog, ctx->data.bin_val.len,
++				     ctx->data.bin_val.ptr);
+ 		if (err)
+ 			return err;
+ 		err = sk_unattached_filter_create(&fp, fprog);
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 41163ac..6f27c84 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -71,25 +71,27 @@ enum team_option_type {
+ 	TEAM_OPTION_TYPE_BINARY,
+ };
+ 
++struct team_gsetter_ctx {
++	union {
++		u32 u32_val;
++		const char *str_val;
++		struct {
++			const void *ptr;
++			u32 len;
++		} bin_val;
++	} data;
++	struct team_port *port;
++};
++
+ struct team_option {
+ 	struct list_head list;
+ 	const char *name;
++	bool per_port;
+ 	enum team_option_type type;
+-	int (*getter)(struct team *team, void *arg);
+-	int (*setter)(struct team *team, void *arg);
+-
+-	/* Custom gennetlink interface related flags */
+-	bool changed;
+-	bool removed;
++	int (*getter)(struct team *team, struct team_gsetter_ctx *ctx);
++	int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
+ };
+ 
+-struct team_option_binary {
+-	u32 data_len;
+-	void *data;
+-};
+-
+-#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
+-
+ struct team_mode {
+ 	struct list_head list;
+ 	const char *kind;
+@@ -118,6 +120,7 @@ struct team {
+ 	struct list_head port_list;
+ 
+ 	struct list_head option_list;
++	struct list_head option_inst_list; /* list of option instances */
+ 
+ 	const struct team_mode *mode;
+ 	struct team_mode_ops ops;
+@@ -224,6 +227,7 @@ enum {
+ 	TEAM_ATTR_OPTION_TYPE,		/* u8 */
+ 	TEAM_ATTR_OPTION_DATA,		/* dynamic */
+ 	TEAM_ATTR_OPTION_REMOVED,	/* flag */
++	TEAM_ATTR_OPTION_PORT_IFINDEX,	/* u32 */ /* for per-port options */
+ 
+ 	__TEAM_ATTR_OPTION_MAX,
+ 	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,

Added: dists/sid/linux/debian/patches/features/all/team/0017-team-add-bool-option-type.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0017-team-add-bool-option-type.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,137 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 10 Apr 2012 05:15:43 +0000
+Subject: [17/23] team: add bool option type
+
+commit 14f066bab19946545130a7379f420af860a02ae8 upstream.
+
+Add another (hopefully last) option type. Use NLA_FLAG to implement
+that.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   40 +++++++++++++++++++++++++++++-----------
+ include/linux/if_team.h |    2 ++
+ 2 files changed, 31 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index eaf8441..2645fae 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -1463,6 +1463,16 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
+ 				    ctx.data.bin_val.len, ctx.data.bin_val.ptr))
+ 				goto nla_put_failure;
+ 			break;
++		case TEAM_OPTION_TYPE_BOOL:
++			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
++				goto nla_put_failure;
++			err = team_option_get(team, opt_inst, &ctx);
++			if (err)
++				goto errout;
++			if (ctx.data.bool_val &&
++			    nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
++				goto nla_put_failure;
++			break;
+ 		default:
+ 			BUG();
+ 		}
+@@ -1524,6 +1534,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
+ 		struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
+ 		struct nlattr *attr_port_ifindex;
++		struct nlattr *attr_data;
+ 		enum team_option_type opt_type;
+ 		int opt_port_ifindex = 0; /* != 0 for per-port options */
+ 		struct team_option_inst *opt_inst;
+@@ -1539,8 +1550,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 		if (err)
+ 			goto team_put;
+ 		if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
+-		    !opt_attrs[TEAM_ATTR_OPTION_TYPE] ||
+-		    !opt_attrs[TEAM_ATTR_OPTION_DATA]) {
++		    !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
+ 			err = -EINVAL;
+ 			goto team_put;
+ 		}
+@@ -1554,10 +1564,19 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 		case NLA_BINARY:
+ 			opt_type = TEAM_OPTION_TYPE_BINARY;
+ 			break;
++		case NLA_FLAG:
++			opt_type = TEAM_OPTION_TYPE_BOOL;
++			break;
+ 		default:
+ 			goto team_put;
+ 		}
+ 
++		attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
++		if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
++			err = -EINVAL;
++			goto team_put;
++		}
++
+ 		opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
+ 		attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
+ 		if (attr_port_ifindex)
+@@ -1565,9 +1584,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 
+ 		list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ 			struct team_option *option = opt_inst->option;
+-			struct nlattr *opt_data_attr;
+ 			struct team_gsetter_ctx ctx;
+-			int data_len;
+ 			int tmp_ifindex;
+ 
+ 			tmp_ifindex = opt_inst->port ?
+@@ -1577,23 +1594,24 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+ 			    tmp_ifindex != opt_port_ifindex)
+ 				continue;
+ 			opt_found = true;
+-			opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA];
+-			data_len = nla_len(opt_data_attr);
+ 			ctx.port = opt_inst->port;
+ 			switch (opt_type) {
+ 			case TEAM_OPTION_TYPE_U32:
+-				ctx.data.u32_val = nla_get_u32(opt_data_attr);
++				ctx.data.u32_val = nla_get_u32(attr_data);
+ 				break;
+ 			case TEAM_OPTION_TYPE_STRING:
+-				if (data_len > TEAM_STRING_MAX_LEN) {
++				if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
+ 					err = -EINVAL;
+ 					goto team_put;
+ 				}
+-				ctx.data.str_val = nla_data(opt_data_attr);
++				ctx.data.str_val = nla_data(attr_data);
+ 				break;
+ 			case TEAM_OPTION_TYPE_BINARY:
+-				ctx.data.bin_val.len = data_len;
+-				ctx.data.bin_val.ptr = nla_data(opt_data_attr);
++				ctx.data.bin_val.len = nla_len(attr_data);
++				ctx.data.bin_val.ptr = nla_data(attr_data);
++				break;
++			case TEAM_OPTION_TYPE_BOOL:
++				ctx.data.bool_val = attr_data ? true : false;
+ 				break;
+ 			default:
+ 				BUG();
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 6f27c84..78c84fd 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -69,6 +69,7 @@ enum team_option_type {
+ 	TEAM_OPTION_TYPE_U32,
+ 	TEAM_OPTION_TYPE_STRING,
+ 	TEAM_OPTION_TYPE_BINARY,
++	TEAM_OPTION_TYPE_BOOL,
+ };
+ 
+ struct team_gsetter_ctx {
+@@ -79,6 +80,7 @@ struct team_gsetter_ctx {
+ 			const void *ptr;
+ 			u32 len;
+ 		} bin_val;
++		bool bool_val;
+ 	} data;
+ 	struct team_port *port;
+ };

Added: dists/sid/linux/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,184 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 10 Apr 2012 05:15:44 +0000
+Subject: [18/23] team: add user_linkup and user_linkup_enabled per-port
+ option
+
+commit 71472ec12c61dd305ab4d11822af7ecc4f9717f9 upstream.
+
+Allows userspace to setup linkup for ports. Default is to take linkup
+directly from ethtool state.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   72 +++++++++++++++++++++++++++++++++++++++++------
+ include/linux/if_team.h |   26 +++++++++++------
+ 2 files changed, 81 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 2645fae..e639abe 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -76,6 +76,11 @@ int team_port_set_team_mac(struct team_port *port)
+ }
+ EXPORT_SYMBOL(team_port_set_team_mac);
+ 
++static void team_refresh_port_linkup(struct team_port *port)
++{
++	port->linkup = port->user.linkup_enabled ? port->user.linkup :
++						   port->state.linkup;
++}
+ 
+ /*******************
+  * Options handling
+@@ -880,6 +885,40 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
+ 	return team_change_mode(team, ctx->data.str_val);
+ }
+ 
++static int team_user_linkup_option_get(struct team *team,
++				       struct team_gsetter_ctx *ctx)
++{
++	ctx->data.bool_val = ctx->port->user.linkup;
++	return 0;
++}
++
++static int team_user_linkup_option_set(struct team *team,
++				       struct team_gsetter_ctx *ctx)
++{
++	ctx->port->user.linkup = ctx->data.bool_val;
++	team_refresh_port_linkup(ctx->port);
++	return 0;
++}
++
++static int team_user_linkup_en_option_get(struct team *team,
++					  struct team_gsetter_ctx *ctx)
++{
++	struct team_port *port = ctx->port;
++
++	ctx->data.bool_val = port->user.linkup_enabled;
++	return 0;
++}
++
++static int team_user_linkup_en_option_set(struct team *team,
++					  struct team_gsetter_ctx *ctx)
++{
++	struct team_port *port = ctx->port;
++
++	port->user.linkup_enabled = ctx->data.bool_val;
++	team_refresh_port_linkup(ctx->port);
++	return 0;
++}
++
+ static const struct team_option team_options[] = {
+ 	{
+ 		.name = "mode",
+@@ -887,6 +926,20 @@ static const struct team_option team_options[] = {
+ 		.getter = team_mode_option_get,
+ 		.setter = team_mode_option_set,
+ 	},
++	{
++		.name = "user_linkup",
++		.type = TEAM_OPTION_TYPE_BOOL,
++		.per_port = true,
++		.getter = team_user_linkup_option_get,
++		.setter = team_user_linkup_option_set,
++	},
++	{
++		.name = "user_linkup_enabled",
++		.type = TEAM_OPTION_TYPE_BOOL,
++		.per_port = true,
++		.getter = team_user_linkup_en_option_get,
++		.setter = team_user_linkup_en_option_set,
++	},
+ };
+ 
+ static int team_init(struct net_device *dev)
+@@ -1670,10 +1723,10 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
+ 		}
+ 		if ((port->removed &&
+ 		     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
+-		    (port->linkup &&
++		    (port->state.linkup &&
+ 		     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
+-		    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->speed) ||
+-		    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex))
++		    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
++		    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
+ 			goto nla_put_failure;
+ 		nla_nest_end(skb, port_item);
+ 	}
+@@ -1833,23 +1886,24 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
+ {
+ 	int err;
+ 
+-	if (!port->removed && port->linkup == linkup)
++	if (!port->removed && port->state.linkup == linkup)
+ 		return;
+ 
+ 	port->changed = true;
+-	port->linkup = linkup;
++	port->state.linkup = linkup;
++	team_refresh_port_linkup(port);
+ 	if (linkup) {
+ 		struct ethtool_cmd ecmd;
+ 
+ 		err = __ethtool_get_settings(port->dev, &ecmd);
+ 		if (!err) {
+-			port->speed = ethtool_cmd_speed(&ecmd);
+-			port->duplex = ecmd.duplex;
++			port->state.speed = ethtool_cmd_speed(&ecmd);
++			port->state.duplex = ecmd.duplex;
+ 			goto send_event;
+ 		}
+ 	}
+-	port->speed = 0;
+-	port->duplex = 0;
++	port->state.speed = 0;
++	port->state.duplex = 0;
+ 
+ send_event:
+ 	err = team_nl_send_event_port_list_get(port->team);
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 78c84fd..5fd5ab1 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -33,6 +33,24 @@ struct team_port {
+ 	struct team *team;
+ 	int index;
+ 
++	bool linkup; /* either state.linkup or user.linkup */
++
++	struct {
++		bool linkup;
++		u32 speed;
++		u8 duplex;
++	} state;
++
++	/* Values set by userspace */
++	struct {
++		bool linkup;
++		bool linkup_enabled;
++	} user;
++
++	/* Custom gennetlink interface related flags */
++	bool changed;
++	bool removed;
++
+ 	/*
+ 	 * A place for storing original values of the device before it
+ 	 * become a port.
+@@ -42,14 +60,6 @@ struct team_port {
+ 		unsigned int mtu;
+ 	} orig;
+ 
+-	bool linkup;
+-	u32 speed;
+-	u8 duplex;
+-
+-	/* Custom gennetlink interface related flags */
+-	bool changed;
+-	bool removed;
+-
+ 	struct rcu_head rcu;
+ };
+ 

Added: dists/sid/linux/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,27 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 10 Apr 2012 05:15:45 +0000
+Subject: [19/23] team: ab: walk through port list non-rcu
+
+commit 679b16073008cc536e85e2773e67234b596fb62e upstream.
+
+Since team->lock is being held, _rcu variant make no sense.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team_mode_activebackup.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+index 6cde1ab..a715c40 100644
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -72,7 +72,7 @@ static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
+ {
+ 	struct team_port *port;
+ 
+-	list_for_each_entry_rcu(port, &team->port_list, list) {
++	list_for_each_entry(port, &team->port_list, list) {
+ 		if (port->dev->ifindex == ctx->data.u32_val) {
+ 			rcu_assign_pointer(ab_priv(team)->active_port, port);
+ 			return 0;

Added: dists/sid/linux/debian/patches/features/all/team/0020-team-add-missed-statics.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0020-team-add-missed-statics.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,66 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Tue, 10 Apr 2012 05:15:46 +0000
+Subject: [20/23] team: add missed "statics"
+
+commit cade455596504fae8e134a27189713ddf7c6d04d upstream.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c                   |    2 +-
+ drivers/net/team/team_mode_activebackup.c |    4 ++--
+ drivers/net/team/team_mode_loadbalance.c  |    4 ++--
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index e639abe..153a62d 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -65,7 +65,7 @@ static int __set_port_mac(struct net_device *port_dev,
+ 	return dev_set_mac_address(port_dev, &addr);
+ }
+ 
+-int team_port_set_orig_mac(struct team_port *port)
++static int team_port_set_orig_mac(struct team_port *port)
+ {
+ 	return __set_port_mac(port->dev, port->orig.dev_addr);
+ }
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+index a715c40..fd6bd03 100644
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -90,12 +90,12 @@ static const struct team_option ab_options[] = {
+ 	},
+ };
+ 
+-int ab_init(struct team *team)
++static int ab_init(struct team *team)
+ {
+ 	return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
+ }
+ 
+-void ab_exit(struct team *team)
++static void ab_exit(struct team *team)
+ {
+ 	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
+ }
+diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
+index 167cdb4..2b506b2 100644
+--- a/drivers/net/team/team_mode_loadbalance.c
++++ b/drivers/net/team/team_mode_loadbalance.c
+@@ -130,13 +130,13 @@ static const struct team_option lb_options[] = {
+ 	},
+ };
+ 
+-int lb_init(struct team *team)
++static int lb_init(struct team *team)
+ {
+ 	return team_options_register(team, lb_options,
+ 				     ARRAY_SIZE(lb_options));
+ }
+ 
+-void lb_exit(struct team *team)
++static void lb_exit(struct team *team)
+ {
+ 	team_options_unregister(team, lb_options,
+ 				ARRAY_SIZE(lb_options));

Added: dists/sid/linux/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,41 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Fri, 20 Apr 2012 04:42:04 +0000
+Subject: [21/23] team: lb: let userspace care about port macs
+
+commit 4c78bb845bd2aaf1f7136e75314c7d034cfd120f upstream.
+
+Better to leave this for userspace
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team_mode_loadbalance.c |   12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
+index 2b506b2..438d5b8 100644
+--- a/drivers/net/team/team_mode_loadbalance.c
++++ b/drivers/net/team/team_mode_loadbalance.c
+@@ -142,22 +142,10 @@ static void lb_exit(struct team *team)
+ 				ARRAY_SIZE(lb_options));
+ }
+ 
+-static int lb_port_enter(struct team *team, struct team_port *port)
+-{
+-	return team_port_set_team_mac(port);
+-}
+-
+-static void lb_port_change_mac(struct team *team, struct team_port *port)
+-{
+-	team_port_set_team_mac(port);
+-}
+-
+ static const struct team_mode_ops lb_mode_ops = {
+ 	.init			= lb_init,
+ 	.exit			= lb_exit,
+ 	.transmit		= lb_transmit,
+-	.port_enter		= lb_port_enter,
+-	.port_change_mac	= lb_port_change_mac,
+ };
+ 
+ static struct team_mode lb_mode = {

Added: dists/sid/linux/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,212 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Fri, 20 Apr 2012 04:42:05 +0000
+Subject: [22/23] team: allow to enable/disable ports
+
+commit 19a0b58e506b06fd41659d8734bba6a3e87980f4 upstream.
+
+This patch changes content of hashlist (used to get port struct by
+computed index (0...en_port_count-1)). Now the hash list contains only
+enabled ports so userspace will be able to say what ports can be used
+for tx/rx. This becomes handy when userspace will need to disable ports
+which does not belong to active aggregator. By default, newly added port
+is enabled.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c                  |   51 ++++++++++++++++++++----------
+ drivers/net/team/team_mode_loadbalance.c |    2 +-
+ drivers/net/team/team_mode_roundrobin.c  |    2 +-
+ include/linux/if_team.h                  |   15 +++++----
+ 4 files changed, 45 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index 153a62d..fe7ca40 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -559,6 +559,8 @@ static int team_change_mode(struct team *team, const char *kind)
+  * Rx path frame handler
+  ************************/
+ 
++static bool team_port_enabled(struct team_port *port);
++
+ /* note: already called with rcu_read_lock */
+ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
+ {
+@@ -575,8 +577,12 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
+ 
+ 	port = team_port_get_rcu(skb->dev);
+ 	team = port->team;
+-
+-	res = team->ops.receive(team, port, skb);
++	if (!team_port_enabled(port)) {
++		/* allow exact match delivery for disabled ports */
++		res = RX_HANDLER_EXACT;
++	} else {
++		res = team->ops.receive(team, port, skb);
++	}
+ 	if (res == RX_HANDLER_ANOTHER) {
+ 		struct team_pcpu_stats *pcpu_stats;
+ 
+@@ -612,17 +618,25 @@ static bool team_port_find(const struct team *team,
+ 	return false;
+ }
+ 
++static bool team_port_enabled(struct team_port *port)
++{
++	return port->index != -1;
++}
++
+ /*
+- * Add/delete port to the team port list. Write guarded by rtnl_lock.
+- * Takes care of correct port->index setup (might be racy).
++ * Enable/disable port by adding to enabled port hashlist and setting
++ * port->index (Might be racy so reader could see incorrect ifindex when
++ * processing a flying packet, but that is not a problem). Write guarded
++ * by team->lock.
+  */
+-static void team_port_list_add_port(struct team *team,
+-				    struct team_port *port)
++static void team_port_enable(struct team *team,
++			     struct team_port *port)
+ {
+-	port->index = team->port_count++;
++	if (team_port_enabled(port))
++		return;
++	port->index = team->en_port_count++;
+ 	hlist_add_head_rcu(&port->hlist,
+ 			   team_port_index_hash(team, port->index));
+-	list_add_tail_rcu(&port->list, &team->port_list);
+ }
+ 
+ static void __reconstruct_port_hlist(struct team *team, int rm_index)
+@@ -630,7 +644,7 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index)
+ 	int i;
+ 	struct team_port *port;
+ 
+-	for (i = rm_index + 1; i < team->port_count; i++) {
++	for (i = rm_index + 1; i < team->en_port_count; i++) {
+ 		port = team_get_port_by_index(team, i);
+ 		hlist_del_rcu(&port->hlist);
+ 		port->index--;
+@@ -639,15 +653,17 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index)
+ 	}
+ }
+ 
+-static void team_port_list_del_port(struct team *team,
+-				   struct team_port *port)
++static void team_port_disable(struct team *team,
++			      struct team_port *port)
+ {
+ 	int rm_index = port->index;
+ 
++	if (!team_port_enabled(port))
++		return;
+ 	hlist_del_rcu(&port->hlist);
+-	list_del_rcu(&port->list);
+ 	__reconstruct_port_hlist(team, rm_index);
+-	team->port_count--;
++	team->en_port_count--;
++	port->index = -1;
+ }
+ 
+ #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+@@ -800,7 +816,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
+ 		goto err_option_port_add;
+ 	}
+ 
+-	team_port_list_add_port(team, port);
++	port->index = -1;
++	team_port_enable(team, port);
++	list_add_tail_rcu(&port->list, &team->port_list);
+ 	team_adjust_ops(team);
+ 	__team_compute_features(team);
+ 	__team_port_change_check(port, !!netif_carrier_ok(port_dev));
+@@ -849,7 +867,8 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 
+ 	port->removed = true;
+ 	__team_port_change_check(port, false);
+-	team_port_list_del_port(team, port);
++	team_port_disable(team, port);
++	list_del_rcu(&port->list);
+ 	team_adjust_ops(team);
+ 	team_option_port_del(team, port);
+ 	netdev_rx_handler_unregister(port_dev);
+@@ -956,7 +975,7 @@ static int team_init(struct net_device *dev)
+ 		return -ENOMEM;
+ 
+ 	for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
+-		INIT_HLIST_HEAD(&team->port_hlist[i]);
++		INIT_HLIST_HEAD(&team->en_port_hlist[i]);
+ 	INIT_LIST_HEAD(&team->port_list);
+ 
+ 	team_adjust_ops(team);
+diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
+index 438d5b8..86e8183 100644
+--- a/drivers/net/team/team_mode_loadbalance.c
++++ b/drivers/net/team/team_mode_loadbalance.c
+@@ -38,7 +38,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb)
+ 	if (unlikely(!fp))
+ 		goto drop;
+ 	hash = SK_RUN_FILTER(fp, skb);
+-	port_index = hash % team->port_count;
++	port_index = hash % team->en_port_count;
+ 	port = team_get_port_by_index_rcu(team, port_index);
+ 	if (unlikely(!port))
+ 		goto drop;
+diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
+index a0e8f80..6abfbdc 100644
+--- a/drivers/net/team/team_mode_roundrobin.c
++++ b/drivers/net/team/team_mode_roundrobin.c
+@@ -50,7 +50,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb)
+ 	struct team_port *port;
+ 	int port_index;
+ 
+-	port_index = rr_priv(team)->sent_packets++ % team->port_count;
++	port_index = rr_priv(team)->sent_packets++ % team->en_port_count;
+ 	port = team_get_port_by_index_rcu(team, port_index);
+ 	port = __get_first_port_up(team, port);
+ 	if (unlikely(!port))
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 5fd5ab1..8185f57 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -28,10 +28,10 @@ struct team;
+ 
+ struct team_port {
+ 	struct net_device *dev;
+-	struct hlist_node hlist; /* node in hash list */
++	struct hlist_node hlist; /* node in enabled ports hash list */
+ 	struct list_head list; /* node in ordinary list */
+ 	struct team *team;
+-	int index;
++	int index; /* index of enabled port. If disabled, it's set to -1 */
+ 
+ 	bool linkup; /* either state.linkup or user.linkup */
+ 
+@@ -125,11 +125,12 @@ struct team {
+ 	struct mutex lock; /* used for overall locking, e.g. port lists write */
+ 
+ 	/*
+-	 * port lists with port count
++	 * List of enabled ports and their count
+ 	 */
+-	int port_count;
+-	struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES];
+-	struct list_head port_list;
++	int en_port_count;
++	struct hlist_head en_port_hlist[TEAM_PORT_HASHENTRIES];
++
++	struct list_head port_list; /* list of all ports */
+ 
+ 	struct list_head option_list;
+ 	struct list_head option_inst_list; /* list of option instances */
+@@ -142,7 +143,7 @@ struct team {
+ static inline struct hlist_head *team_port_index_hash(struct team *team,
+ 						      int port_index)
+ {
+-	return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
++	return &team->en_port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
+ }
+ 
+ static inline struct team_port *team_get_port_by_index(struct team *team,

Added: dists/sid/linux/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch	Thu Jun  7 06:09:25 2012	(r19094)
@@ -0,0 +1,54 @@
+From: Jiri Pirko <jpirko at redhat.com>
+Date: Fri, 20 Apr 2012 04:42:06 +0000
+Subject: [23/23] team: add per-port option for enabling/disabling ports
+
+commit acd69962341a956b5bcc5b4178b70fa527d7ce11 upstream.
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/team/team.c |   24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index fe7ca40..c61ae35 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -904,6 +904,23 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
+ 	return team_change_mode(team, ctx->data.str_val);
+ }
+ 
++static int team_port_en_option_get(struct team *team,
++				   struct team_gsetter_ctx *ctx)
++{
++	ctx->data.bool_val = team_port_enabled(ctx->port);
++	return 0;
++}
++
++static int team_port_en_option_set(struct team *team,
++				   struct team_gsetter_ctx *ctx)
++{
++	if (ctx->data.bool_val)
++		team_port_enable(team, ctx->port);
++	else
++		team_port_disable(team, ctx->port);
++	return 0;
++}
++
+ static int team_user_linkup_option_get(struct team *team,
+ 				       struct team_gsetter_ctx *ctx)
+ {
+@@ -946,6 +963,13 @@ static const struct team_option team_options[] = {
+ 		.setter = team_mode_option_set,
+ 	},
+ 	{
++		.name = "enabled",
++		.type = TEAM_OPTION_TYPE_BOOL,
++		.per_port = true,
++		.getter = team_port_en_option_get,
++		.setter = team_port_en_option_set,
++	},
++	{
+ 		.name = "user_linkup",
+ 		.type = TEAM_OPTION_TYPE_BOOL,
+ 		.per_port = true,

Modified: dists/sid/linux/debian/patches/series-all
==============================================================================
--- dists/sid/linux/debian/patches/series-all	Thu Jun  7 05:45:57 2012	(r19093)
+++ dists/sid/linux/debian/patches/series-all	Thu Jun  7 06:09:25 2012	(r19094)
@@ -309,3 +309,31 @@
 bugfix/all/hugepages-fix-use-after-free-bug-in-quota-handling.patch
 
 bugfix/all/fix-scsi_wait_scan.patch
+
+features/all/define-netdev_features_t.patch
+features/all/filter-Allow-to-create-sk-unattached-filters.patch
+
+# team driver from 3.5ish
+features/all/team/0001-net-introduce-ethernet-teaming-device.patch
+features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
+features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
+features/all/team/0004-team-replicate-options-on-register.patch
+features/all/team/0005-team-add-fix_features.patch
+features/all/team/0006-team-avoid-using-variable-length-array.patch
+features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
+features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
+features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
+features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
+features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
+features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
+features/all/team/0013-team-Stop-using-NLA_PUT.patch
+features/all/team/0014-team-add-binary-option-type.patch
+features/all/team/0015-team-add-loadbalance-mode.patch
+features/all/team/0016-team-add-support-for-per-port-options.patch
+features/all/team/0017-team-add-bool-option-type.patch
+features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
+features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
+features/all/team/0020-team-add-missed-statics.patch
+features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
+features/all/team/0022-team-allow-to-enable-disable-ports.patch
+features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch



More information about the Kernel-svn-changes mailing list