diff --git a/debian/changelog b/debian/changelog index 6cdd3c6b4..10fe0a60f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -23,7 +23,6 @@ linux (3.2.19-2) UNRELEASED; urgency=low * 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 * proc: Backport hidepid mount option from Linux 3.4 (Closes: #669028) * NFSv4: Reduce the footprint of the idmapper (Closes: #657078) * [i386] thp: avoid atomic64_read in pmd_read_atomic for 32bit PAE diff --git a/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch b/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch deleted file mode 100644 index bfc901069..000000000 --- a/debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch +++ /dev/null @@ -1,2322 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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 -+L: netdev@vger.kernel.org -+S: Supported -+F: drivers/net/team/ -+F: include/linux/if_team.h -+ - TEGRA SUPPORT - M: Colin Cross - M: Olof Johansson -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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 "); -+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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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 "); -+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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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 "); -+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 -+ * -+ * 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_ */ diff --git a/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch b/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch deleted file mode 100644 index 3aea3caeb..000000000 --- a/debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch +++ /dev/null @@ -1,49 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, diff --git a/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch b/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch deleted file mode 100644 index 67f740f4f..000000000 --- a/debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch +++ /dev/null @@ -1,149 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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 diff --git a/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch b/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch deleted file mode 100644 index 24b65c675..000000000 --- a/debian/patches/features/all/team/0004-team-replicate-options-on-register.patch +++ /dev/null @@ -1,189 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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); diff --git a/debian/patches/features/all/team/0005-team-add-fix_features.patch b/debian/patches/features/all/team/0005-team-add-fix_features.patch deleted file mode 100644 index 85a504ce5..000000000 --- a/debian/patches/features/all/team/0005-team-add-fix_features.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, - }; - - diff --git a/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch b/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch deleted file mode 100644 index 2abd0fe1c..000000000 --- a/debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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; - } - diff --git a/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch b/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch deleted file mode 100644 index 7680fbdfd..000000000 --- a/debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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++) diff --git a/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch b/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch deleted file mode 100644 index 9bd5f39de..000000000 --- a/debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: Eric Dumazet -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 -Signed-off-by: David S. Miller -[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) diff --git a/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch b/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch deleted file mode 100644 index f30b01ec2..000000000 --- a/debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch +++ /dev/null @@ -1,132 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller -[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 - #include - #include -+#include - #include - #include - #include -@@ -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); diff --git a/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch b/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch deleted file mode 100644 index f6ebb7e8c..000000000 --- a/debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller -[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); -+} diff --git a/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch b/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch deleted file mode 100644 index 9cbbde450..000000000 --- a/debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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); diff --git a/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch b/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch deleted file mode 100644 index 1e8d7bb6b..000000000 --- a/debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch +++ /dev/null @@ -1,371 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, diff --git a/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch b/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch deleted file mode 100644 index 5f8f3a28c..000000000 --- a/debian/patches/features/all/team/0013-team-Stop-using-NLA_PUT.patch +++ /dev/null @@ -1,108 +0,0 @@ -From: "David S. Miller" -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 ---- - 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); - } - diff --git a/debian/patches/features/all/team/0014-team-add-binary-option-type.patch b/debian/patches/features/all/team/0014-team-add-binary-option-type.patch deleted file mode 100644 index b1fadec16..000000000 --- a/debian/patches/features/all/team/0014-team-add-binary-option-type.patch +++ /dev/null @@ -1,124 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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; diff --git a/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch b/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch deleted file mode 100644 index 3158c3010..000000000 --- a/debian/patches/features/all/team/0015-team-add-loadbalance-mode.patch +++ /dev/null @@ -1,241 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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 "); -+MODULE_DESCRIPTION("Load-balancing mode for team"); -+MODULE_ALIAS("team-mode-loadbalance"); diff --git a/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch b/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch deleted file mode 100644 index 204877e6a..000000000 --- a/debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch +++ /dev/null @@ -1,687 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, diff --git a/debian/patches/features/all/team/0017-team-add-bool-option-type.patch b/debian/patches/features/all/team/0017-team-add-bool-option-type.patch deleted file mode 100644 index 077e46621..000000000 --- a/debian/patches/features/all/team/0017-team-add-bool-option-type.patch +++ /dev/null @@ -1,137 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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; - }; diff --git a/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch b/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch deleted file mode 100644 index 1dadd07c6..000000000 --- a/debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch +++ /dev/null @@ -1,184 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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; - }; - diff --git a/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch b/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch deleted file mode 100644 index 13fde09c1..000000000 --- a/debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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; diff --git a/debian/patches/features/all/team/0020-team-add-missed-statics.patch b/debian/patches/features/all/team/0020-team-add-missed-statics.patch deleted file mode 100644 index da5f23a29..000000000 --- a/debian/patches/features/all/team/0020-team-add-missed-statics.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: Jiri Pirko -Date: Tue, 10 Apr 2012 05:15:46 +0000 -Subject: [20/23] team: add missed "statics" - -commit cade455596504fae8e134a27189713ddf7c6d04d upstream. - -Signed-off-by: Jiri Pirko -Signed-off-by: David S. Miller ---- - 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)); diff --git a/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch b/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch deleted file mode 100644 index e90a68c34..000000000 --- a/debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch +++ /dev/null @@ -1,41 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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 = { diff --git a/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch b/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch deleted file mode 100644 index 8df50f7d6..000000000 --- a/debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch +++ /dev/null @@ -1,212 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, diff --git a/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch b/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch deleted file mode 100644 index 09ef3e098..000000000 --- a/debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Jiri Pirko -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 -Signed-off-by: David S. Miller ---- - 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, diff --git a/debian/patches/series b/debian/patches/series index 89b40675a..f5416fe04 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -311,33 +311,9 @@ 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 +# netdev features, probably useful for other backports but not needed yet +#features/all/define-netdev_features_t.patch +#features/all/filter-Allow-to-create-sk-unattached-filters.patch # procfs hidepid from 3.4 features/all/hidepid/0001-procfs-parse-mount-options.patch