[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