Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH 2/3] stmmac: fix NULL pointer dereference in capabilities fixup
From: Ben Hutchings @ 2011-10-26 22:26 UTC (permalink / raw)
  To: Giuseppe CAVALLARO; +Cc: netdev, keguang.zhang, Angus Clark
In-Reply-To: <1319637339-14866-2-git-send-email-peppe.cavallaro@st.com>

On Wed, 2011-10-26 at 15:55 +0200, Giuseppe CAVALLARO wrote:
> From: Angus Clark <angus.clark@st.com>
> 
> Signed-off-by: Angus Clark <angus.clark@st.com>
> Acked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
> ---
>  drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index fcdd5a2..f77eaa6 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -325,7 +325,7 @@ static int stmmac_init_phy(struct net_device *dev)
>  	    (interface == PHY_INTERFACE_MODE_RMII))) {
>  		phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
>  				      SUPPORTED_Asym_Pause);
> -		priv->phydev->advertising = priv->phydev->supported;
> +		priv->phydev->advertising = phydev->supported;

How can this fix the bug?  You mean:

		phydev->advertising = phydev->supported;

Ben.

>  	}
>  
>  	/*

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH 3/3] stmmac: update normal descriptor structure
From: David Miller @ 2011-10-26 21:23 UTC (permalink / raw)
  To: peppe.cavallaro; +Cc: netdev, keguang.zhang
In-Reply-To: <1319637339-14866-3-git-send-email-peppe.cavallaro@st.com>

From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
Date: Wed, 26 Oct 2011 15:55:39 +0200

> This patch updates the normal descriptor structure
> to work fine on new GMAC Synopsys chips.
> 
> Normal descriptors were designed on the old MAC10/100
> databook 1.91 where some bits were reserved: for example
> the tx checksum insertion and rx checksum offload.
> 
> The patch maintains the back-compatibility with old
> MAC devices (tested on STx7109 MAC10/100) and adds new
> fields that actually new GMAC devices can use.
> 
> For example, STx7109 (MAC10/100) will pass from the platform
>   tx_coe = 0, enh_desc = 0, has_gmac = 0.
> A platform like Loongson1B (GMAC) will pass:
>   tx_coe = 1, enh_desc = 0, has_gmac = 1.
> 
> Thanks to Kelvin, he enhanced the normal descriptors for
> GMAC (on MIPS Loongson1B platform).
> 
> Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
> Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>

Applied.

^ permalink raw reply

* Re: [PATCH 2/3] stmmac: fix NULL pointer dereference in capabilities fixup
From: David Miller @ 2011-10-26 21:23 UTC (permalink / raw)
  To: peppe.cavallaro; +Cc: netdev, keguang.zhang, angus.clark
In-Reply-To: <1319637339-14866-2-git-send-email-peppe.cavallaro@st.com>

From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
Date: Wed, 26 Oct 2011 15:55:38 +0200

> From: Angus Clark <angus.clark@st.com>
> 
> Signed-off-by: Angus Clark <angus.clark@st.com>
> Acked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>

Applied.

^ permalink raw reply

* Re: [PATCH 1/3] stmmac: fix a bug while checking the HW cap reg
From: David Miller @ 2011-10-26 21:23 UTC (permalink / raw)
  To: peppe.cavallaro; +Cc: netdev, keguang.zhang
In-Reply-To: <1319637339-14866-1-git-send-email-peppe.cavallaro@st.com>

From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
Date: Wed, 26 Oct 2011 15:55:37 +0200

> The patch fixes a bug while checking the HW cap reg
> on old MAC10/100 where this feature is not available.
> 
> Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>

Applied.

^ permalink raw reply

* [patch net-next V6] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-26 20:27 UTC (permalink / raw)
  To: netdev
  Cc: davem, eric.dumazet, bhutchings, shemminger, fubar, andy, tgraf,
	ebiederm, mirqus, kaber, greearb, jesse, fbl, benjamin.poirier,
	jzupka

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 basis for libteam was recently introduced (some need
still need to be done on it though). Daemon providing arpmon/miimon
active-backup functionality will be introduced shortly.
All what's necessary is already implemented in kernel team driver.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>

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.
---
 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                   | 1555 +++++++++++++++++++++++++++++
 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, 2105 insertions(+), 0 deletions(-)
 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 bb4a83a..a20c84e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6372,6 +6372,13 @@ W:	http://tcp-lp-mod.sourceforge.net/
 S:	Maintained
 F:	net/ipv4/tcp_lp.c
 
+TEAM DRIVER
+M:	Jiri Pirko <jpirko@redhat.com>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	drivers/net/team/
+F:	include/linux/if_team.h
+
 TEGRA SUPPORT
 M:	Colin Cross <ccross@android.com>
 M:	Olof Johansson <olof@lixom.net>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 583f66c..b3020be 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -125,6 +125,8 @@ config IFB
 	  'ifb1' etc.
 	  Look at the iproute2 documentation directory for usage etc
 
+source "drivers/net/team/Kconfig"
+
 config MACVLAN
 	tristate "MAC-VLAN support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index fa877cd..4e4ebfe 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_NET) += Space.o loopback.o
 obj-$(CONFIG_NETCONSOLE) += netconsole.o
 obj-$(CONFIG_PHYLIB) += phy/
 obj-$(CONFIG_RIONET) += rionet.o
+obj-$(CONFIG_NET_TEAM) += team/
 obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
new file mode 100644
index 0000000..248a144
--- /dev/null
+++ b/drivers/net/team/Kconfig
@@ -0,0 +1,43 @@
+menuconfig NET_TEAM
+	tristate "Ethernet team driver support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	---help---
+	  This allows one to create virtual interfaces that teams together
+	  multiple ethernet devices.
+
+	  Team devices can be added using the "ip" command from the
+	  iproute2 package:
+
+	  "ip link add link [ address MAC ] [ NAME ] type team"
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called team.
+
+if NET_TEAM
+
+config NET_TEAM_MODE_ROUNDROBIN
+	tristate "Round-robin mode support"
+	depends on NET_TEAM
+	---help---
+	  Basic mode where port used for transmitting packets is selected in
+	  round-robin fashion using packet counter.
+
+	  All added ports are setup to have bond's mac address.
+
+	  To compile this team mode as a module, choose M here: the module
+	  will be called team_mode_roundrobin.
+
+config NET_TEAM_MODE_ACTIVEBACKUP
+	tristate "Active-backup mode support"
+	depends on NET_TEAM
+	---help---
+	  Only one port is active at a time and the rest of ports are used
+	  for backup.
+
+	  Mac addresses of ports are not modified. Userspace is responsible
+	  to do so.
+
+	  To compile this team mode as a module, choose M here: the module
+	  will be called team_mode_activebackup.
+
+endif # NET_TEAM
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
new file mode 100644
index 0000000..85f2028
--- /dev/null
+++ b/drivers/net/team/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the network team driver
+#
+
+obj-$(CONFIG_NET_TEAM) += team.o
+obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
+obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
new file mode 100644
index 0000000..0180250
--- /dev/null
+++ b/drivers/net/team/team.c
@@ -0,0 +1,1555 @@
+/*
+ * net/drivers/team/team.c - Network team device driver
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/rtnetlink.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
+#include <linux/if_team.h>
+
+#define DRV_NAME "team"
+
+
+/**********
+ * Helpers
+ **********/
+
+#define team_port_exists(dev) (dev->priv_flags & IFF_TEAM_PORT)
+
+static struct team_port *team_port_get_rcu(const struct net_device *dev)
+{
+	struct team_port *port = rcu_dereference(dev->rx_handler_data);
+
+	return team_port_exists(dev) ? port : NULL;
+}
+
+static struct team_port *team_port_get_rtnl(const struct net_device *dev)
+{
+	struct team_port *port = rtnl_dereference(dev->rx_handler_data);
+
+	return team_port_exists(dev) ? port : NULL;
+}
+
+/*
+ * Since the ability to change mac address for open port device is tested in
+ * team_port_add, this function can be called without control of return value
+ */
+static int __set_port_mac(struct net_device *port_dev,
+			  const unsigned char *dev_addr)
+{
+	struct sockaddr addr;
+
+	memcpy(addr.sa_data, dev_addr, ETH_ALEN);
+	addr.sa_family = ARPHRD_ETHER;
+	return dev_set_mac_address(port_dev, &addr);
+}
+
+int team_port_set_orig_mac(struct team_port *port)
+{
+	return __set_port_mac(port->dev, port->orig.dev_addr);
+}
+
+int team_port_set_team_mac(struct team_port *port)
+{
+	return __set_port_mac(port->dev, port->team->dev->dev_addr);
+}
+EXPORT_SYMBOL(team_port_set_team_mac);
+
+
+/*******************
+ * Options handling
+ *******************/
+
+void team_options_register(struct team *team, struct team_option *option,
+			   size_t option_count)
+{
+	int i;
+
+	for (i = 0; i < option_count; i++, option++)
+		list_add_tail(&option->list, &team->option_list);
+}
+EXPORT_SYMBOL(team_options_register);
+
+static void __team_options_change_check(struct team *team,
+					struct team_option *changed_option);
+
+static void __team_options_unregister(struct team *team,
+				      struct team_option *option,
+				      size_t option_count)
+{
+	int i;
+
+	for (i = 0; i < option_count; i++, option++)
+		list_del(&option->list);
+}
+
+void team_options_unregister(struct team *team, struct team_option *option,
+			     size_t option_count)
+{
+	__team_options_unregister(team, option, option_count);
+	__team_options_change_check(team, NULL);
+}
+EXPORT_SYMBOL(team_options_unregister);
+
+static int team_option_get(struct team *team, struct team_option *option,
+			   void *arg)
+{
+	return option->getter(team, arg);
+}
+
+static int team_option_set(struct team *team, struct team_option *option,
+			   void *arg)
+{
+	int err;
+
+	err = option->setter(team, arg);
+	if (err)
+		return err;
+
+	__team_options_change_check(team, option);
+	return err;
+}
+
+/****************
+ * Mode handling
+ ****************/
+
+static LIST_HEAD(mode_list);
+static DEFINE_SPINLOCK(mode_list_lock);
+
+static struct team_mode *__find_mode(const char *kind)
+{
+	struct team_mode *mode;
+
+	list_for_each_entry(mode, &mode_list, list) {
+		if (strcmp(mode->kind, kind) == 0)
+			return mode;
+	}
+	return NULL;
+}
+
+static bool is_good_mode_name(const char *name)
+{
+	while (*name != '\0') {
+		if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+			return false;
+		name++;
+	}
+	return true;
+}
+
+int team_mode_register(struct team_mode *mode)
+{
+	int err = 0;
+
+	if (!is_good_mode_name(mode->kind) ||
+	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
+		return -EINVAL;
+	spin_lock(&mode_list_lock);
+	if (__find_mode(mode->kind)) {
+		err = -EEXIST;
+		goto unlock;
+	}
+	list_add_tail(&mode->list, &mode_list);
+unlock:
+	spin_unlock(&mode_list_lock);
+	return err;
+}
+EXPORT_SYMBOL(team_mode_register);
+
+int team_mode_unregister(struct team_mode *mode)
+{
+	spin_lock(&mode_list_lock);
+	list_del_init(&mode->list);
+	spin_unlock(&mode_list_lock);
+	return 0;
+}
+EXPORT_SYMBOL(team_mode_unregister);
+
+static struct team_mode *team_mode_get(const char *kind)
+{
+	struct team_mode *mode;
+
+	spin_lock(&mode_list_lock);
+	mode = __find_mode(kind);
+	if (!mode) {
+		spin_unlock(&mode_list_lock);
+		request_module("team-mode-%s", kind);
+		spin_lock(&mode_list_lock);
+		mode = __find_mode(kind);
+	}
+	if (mode)
+		if (!try_module_get(mode->owner))
+			mode = NULL;
+
+	spin_unlock(&mode_list_lock);
+	return mode;
+}
+
+static void team_mode_put(const char *kind)
+{
+	struct team_mode *mode;
+
+	spin_lock(&mode_list_lock);
+	mode = __find_mode(kind);
+	BUG_ON(!mode);
+	module_put(mode->owner);
+	spin_unlock(&mode_list_lock);
+}
+
+/*
+ * 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_kind) {
+		void (*exit_op)(struct team *team) = team->mode_ops.exit;
+
+		/* Clear ops area so no callback is called any longer */
+		memset(&team->mode_ops, 0, sizeof(struct team_mode_ops));
+
+		if (exit_op)
+			exit_op(team);
+		team_mode_put(team->mode_kind);
+		team->mode_kind = 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_kind = new_mode->kind;
+	memcpy(&team->mode_ops, new_mode->ops, sizeof(struct team_mode_ops));
+
+	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_kind && 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(kind);
+		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 = RX_HANDLER_ANOTHER;
+
+	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;
+
+	if (team->mode_ops.receive)
+		res = team->mode_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->mode_ops.port_enter) {
+		err = team->mode_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->mode_ops.port_leave)
+		team->mode_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_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);
+	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_kind ? 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);
+
+	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;
+
+	/*
+	 * Ensure transmit function is called only in case there is at least
+	 * one port present.
+	 */
+	if (likely(!list_empty(&team->port_list) && team->mode_ops.transmit))
+		tx_success = team->mode_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->mode_ops.port_change_mac)
+			team->mode_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;
+
+		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;
+
+		ops->ndo_vlan_rx_kill_vid(port->dev, vid);
+	}
+	rcu_read_unlock();
+}
+
+static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
+{
+	struct team *team = netdev_priv(dev);
+	int err;
+
+	spin_lock(&team->lock);
+	err = team_port_add(team, port_dev);
+	spin_unlock(&team->lock);
+	return err;
+}
+
+static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
+{
+	struct team *team = netdev_priv(dev);
+	int err;
+
+	spin_lock(&team->lock);
+	err = team_port_del(team, port_dev);
+	spin_unlock(&team->lock);
+	return err;
+}
+
+static const struct net_device_ops team_netdev_ops = {
+	.ndo_init		= team_init,
+	.ndo_uninit		= team_uninit,
+	.ndo_open		= team_open,
+	.ndo_stop		= team_close,
+	.ndo_start_xmit		= team_xmit,
+	.ndo_change_rx_flags	= team_change_rx_flags,
+	.ndo_set_rx_mode	= team_set_rx_mode,
+	.ndo_set_mac_address	= team_set_mac_address,
+	.ndo_change_mtu		= team_change_mtu,
+	.ndo_get_stats64	= team_get_stats64,
+	.ndo_vlan_rx_add_vid	= team_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
+	.ndo_add_slave		= team_add_slave,
+	.ndo_del_slave		= team_del_slave,
+};
+
+
+/***********************
+ * rt netlink interface
+ ***********************/
+
+static void team_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->netdev_ops = &team_netdev_ops;
+	dev->destructor	= team_destructor;
+	dev->tx_queue_len = 0;
+	dev->flags |= IFF_MULTICAST;
+	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+
+	/*
+	 * Indicate we support unicast address filtering. That way core won't
+	 * bring us to promisc mode in case a unicast addr is added.
+	 * Let this up to underlay drivers.
+	 */
+	dev->priv_flags |= IFF_UNICAST_FLT;
+
+	dev->features |= NETIF_F_LLTX;
+	dev->features |= NETIF_F_GRO;
+	dev->hw_features = NETIF_F_HW_VLAN_TX |
+			   NETIF_F_HW_VLAN_RX |
+			   NETIF_F_HW_VLAN_FILTER;
+
+	dev->features |= dev->hw_features;
+}
+
+static int team_newlink(struct net *src_net, struct net_device *dev,
+			struct nlattr *tb[], struct nlattr *data[])
+{
+	int err;
+
+	if (tb[IFLA_ADDRESS] == NULL)
+		random_ether_addr(dev->dev_addr);
+
+	err = register_netdevice(dev);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int team_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	if (tb[IFLA_ADDRESS]) {
+		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+			return -EINVAL;
+		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+			return -EADDRNOTAVAIL;
+	}
+	return 0;
+}
+
+static struct rtnl_link_ops team_link_ops __read_mostly = {
+	.kind		= DRV_NAME,
+	.priv_size	= sizeof(struct team),
+	.setup		= team_setup,
+	.newlink	= team_newlink,
+	.validate	= team_validate,
+};
+
+
+/***********************************
+ * Generic netlink custom interface
+ ***********************************/
+
+static struct genl_family team_nl_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= TEAM_GENL_NAME,
+	.version	= TEAM_GENL_VERSION,
+	.maxattr	= TEAM_ATTR_MAX,
+	.netnsok	= true,
+};
+
+static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
+	[TEAM_ATTR_UNSPEC]			= { .type = NLA_UNSPEC, },
+	[TEAM_ATTR_TEAM_IFINDEX]		= { .type = NLA_U32 },
+	[TEAM_ATTR_LIST_OPTION]			= { .type = NLA_NESTED },
+	[TEAM_ATTR_LIST_PORT]			= { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
+	[TEAM_ATTR_OPTION_UNSPEC]		= { .type = NLA_UNSPEC, },
+	[TEAM_ATTR_OPTION_NAME] = {
+		.type = NLA_STRING,
+		.len = TEAM_STRING_MAX_LEN,
+	},
+	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
+	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
+	[TEAM_ATTR_OPTION_DATA] = {
+		.type = NLA_BINARY,
+		.len = TEAM_STRING_MAX_LEN,
+	},
+};
+
+static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
+			  &team_nl_family, 0, TEAM_CMD_NOOP);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto err_msg_put;
+	}
+
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
+
+err_msg_put:
+	nlmsg_free(msg);
+
+	return err;
+}
+
+/*
+ * Netlink cmd functions should be locked by following two functions.
+ * To ensure team_uninit would not be called in between, hold rcu_read_lock
+ * all the time.
+ */
+static struct team *team_nl_team_get(struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	int ifindex;
+	struct net_device *dev;
+	struct team *team;
+
+	if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
+		return NULL;
+
+	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
+	rcu_read_lock();
+	dev = dev_get_by_index_rcu(net, ifindex);
+	if (!dev || dev->netdev_ops != &team_netdev_ops) {
+		rcu_read_unlock();
+		return NULL;
+	}
+
+	team = netdev_priv(dev);
+	spin_lock(&team->lock);
+	return team;
+}
+
+static void team_nl_team_put(struct team *team)
+{
+	spin_unlock(&team->lock);
+	rcu_read_unlock();
+}
+
+static int team_nl_send_generic(struct genl_info *info, struct team *team,
+				int (*fill_func)(struct sk_buff *skb,
+						 struct genl_info *info,
+						 int flags, struct team *team))
+{
+	struct sk_buff *skb;
+	int err;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = fill_func(skb, info, NLM_F_ACK, team);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_unicast(genl_info_net(info), skb, info->snd_pid);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_fill_options_get_changed(struct sk_buff *skb,
+					    u32 pid, u32 seq, int flags,
+					    struct team *team,
+					    struct team_option *changed_option)
+{
+	struct nlattr *option_list;
+	void *hdr;
+	struct team_option *option;
+
+	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+			  TEAM_CMD_OPTIONS_GET);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
+	option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
+	if (!option_list)
+		return -EMSGSIZE;
+
+	list_for_each_entry(option, &team->option_list, list) {
+		struct nlattr *option_item;
+		long arg;
+
+		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
+		if (!option_item)
+			goto nla_put_failure;
+		NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
+		if (option == changed_option)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
+		switch (option->type) {
+		case TEAM_OPTION_TYPE_U32:
+			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
+			team_option_get(team, option, &arg);
+			NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
+			break;
+		case TEAM_OPTION_TYPE_STRING:
+			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
+			team_option_get(team, option, &arg);
+			NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
+				       (char *) arg);
+			break;
+		default:
+			BUG();
+		}
+		nla_nest_end(skb, option_item);
+	}
+
+	nla_nest_end(skb, option_list);
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+static int team_nl_fill_options_get(struct sk_buff *skb,
+				    struct genl_info *info, int flags,
+				    struct team *team)
+{
+	return team_nl_fill_options_get_changed(skb, info->snd_pid,
+						info->snd_seq, NLM_F_ACK,
+						team, NULL);
+}
+
+static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+{
+	struct team *team;
+	int err;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = team_nl_send_generic(info, team, team_nl_fill_options_get);
+
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct team *team;
+	int err = 0;
+	int i;
+	struct nlattr *nl_option;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = -EINVAL;
+	if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
+		err = -EINVAL;
+		goto team_put;
+	}
+
+	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
+		struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
+		enum team_option_type opt_type;
+		struct team_option *option;
+		char *opt_name;
+		bool opt_found = false;
+
+		if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
+			err = -EINVAL;
+			goto team_put;
+		}
+		err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
+				       nl_option, team_nl_option_policy);
+		if (err)
+			goto team_put;
+		if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
+		    !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
+		    !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
+			err = -EINVAL;
+			goto team_put;
+		}
+		switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
+		case NLA_U32:
+			opt_type = TEAM_OPTION_TYPE_U32;
+			break;
+		case NLA_STRING:
+			opt_type = TEAM_OPTION_TYPE_STRING;
+			break;
+		default:
+			goto team_put;
+		}
+
+		opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
+		list_for_each_entry(option, &team->option_list, list) {
+			long arg;
+			struct nlattr *opt_data_attr;
+
+			if (option->type != opt_type ||
+			    strcmp(option->name, opt_name))
+				continue;
+			opt_found = true;
+			opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
+			switch (opt_type) {
+			case TEAM_OPTION_TYPE_U32:
+				arg = nla_get_u32(opt_data_attr);
+				break;
+			case TEAM_OPTION_TYPE_STRING:
+				arg = (long) nla_data(opt_data_attr);
+				break;
+			default:
+				BUG();
+			}
+			err = team_option_set(team, option, &arg);
+			if (err)
+				goto team_put;
+		}
+		if (!opt_found) {
+			err = -ENOENT;
+			goto team_put;
+		}
+	}
+
+team_put:
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
+					      u32 pid, u32 seq, int flags,
+					      struct team *team,
+					      struct team_port *changed_port)
+{
+	struct nlattr *port_list;
+	void *hdr;
+	struct team_port *port;
+
+	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+			  TEAM_CMD_PORT_LIST_GET);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
+	port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
+	if (!port_list)
+		return -EMSGSIZE;
+
+	list_for_each_entry(port, &team->port_list, list) {
+		struct nlattr *port_item;
+
+		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
+		if (!port_item)
+			goto nla_put_failure;
+		NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
+		if (port == changed_port)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
+		if (port->linkup)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
+		NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
+		NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
+		nla_nest_end(skb, port_item);
+	}
+
+	nla_nest_end(skb, port_list);
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+static int team_nl_fill_port_list_get(struct sk_buff *skb,
+				      struct genl_info *info, int flags,
+				      struct team *team)
+{
+	return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
+						  info->snd_seq, NLM_F_ACK,
+						  team, NULL);
+}
+
+static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+				     struct genl_info *info)
+{
+	struct team *team;
+	int err;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
+
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static struct genl_ops team_nl_ops[] = {
+	{
+		.cmd = TEAM_CMD_NOOP,
+		.doit = team_nl_cmd_noop,
+		.policy = team_nl_policy,
+	},
+	{
+		.cmd = TEAM_CMD_OPTIONS_SET,
+		.doit = team_nl_cmd_options_set,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = TEAM_CMD_OPTIONS_GET,
+		.doit = team_nl_cmd_options_get,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = TEAM_CMD_PORT_LIST_GET,
+		.doit = team_nl_cmd_port_list_get,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+static struct genl_multicast_group team_change_event_mcgrp = {
+	.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
+};
+
+static int team_nl_send_event_options_get(struct team *team,
+					  struct team_option *changed_option)
+{
+	struct sk_buff *skb;
+	int err;
+	struct net *net = dev_net(team->dev);
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
+					       changed_option);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
+				      GFP_KERNEL);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_send_event_port_list_get(struct team_port *port)
+{
+	struct sk_buff *skb;
+	int err;
+	struct net *net = dev_net(port->team->dev);
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
+						 port->team, port);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
+				      GFP_KERNEL);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_init(void)
+{
+	int err;
+
+	err = genl_register_family_with_ops(&team_nl_family, team_nl_ops,
+					    ARRAY_SIZE(team_nl_ops));
+	if (err)
+		return err;
+
+	err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
+	if (err)
+		goto err_change_event_grp_reg;
+
+	return 0;
+
+err_change_event_grp_reg:
+	genl_unregister_family(&team_nl_family);
+
+	return err;
+}
+
+static void team_nl_fini(void)
+{
+	genl_unregister_family(&team_nl_family);
+}
+
+
+/******************
+ * Change checkers
+ ******************/
+
+static void __team_options_change_check(struct team *team,
+					struct team_option *changed_option)
+{
+	int err;
+
+	err = team_nl_send_event_options_get(team, changed_option);
+	if (err)
+		netdev_warn(team->dev, "Failed to send options change via netlink\n");
+}
+
+/* rtnl lock is held */
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+	int err;
+
+	if (port->linkup == linkup)
+		return;
+
+	port->linkup = linkup;
+	if (linkup) {
+		struct ethtool_cmd ecmd;
+
+		err = __ethtool_get_settings(port->dev, &ecmd);
+		if (!err) {
+			port->speed = ethtool_cmd_speed(&ecmd);
+			port->duplex = ecmd.duplex;
+			goto send_event;
+		}
+	}
+	port->speed = 0;
+	port->duplex = 0;
+
+send_event:
+	err = team_nl_send_event_port_list_get(port);
+	if (err)
+		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
+			    port->dev->name);
+
+}
+
+static void team_port_change_check(struct team_port *port, bool linkup)
+{
+	struct team *team = port->team;
+
+	spin_lock(&team->lock);
+	__team_port_change_check(port, linkup);
+	spin_unlock(&team->lock);
+}
+
+/************************************
+ * Net device notifier event handler
+ ************************************/
+
+static int team_device_event(struct notifier_block *unused,
+			     unsigned long event, void *ptr)
+{
+	struct net_device *dev = (struct net_device *) ptr;
+	struct team_port *port;
+
+	port = team_port_get_rtnl(dev);
+	if (!port)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UP:
+		if (netif_carrier_ok(dev))
+			team_port_change_check(port, true);
+	case NETDEV_DOWN:
+		team_port_change_check(port, false);
+	case NETDEV_CHANGE:
+		if (netif_running(port->dev))
+			team_port_change_check(port,
+					       !!netif_carrier_ok(port->dev));
+		break;
+	case NETDEV_UNREGISTER:
+		team_del_slave(port->team->dev, dev);
+		break;
+	case NETDEV_FEAT_CHANGE:
+		team_compute_features(port->team);
+		break;
+	case NETDEV_CHANGEMTU:
+		/* Forbid to change mtu of underlaying device */
+		return NOTIFY_BAD;
+	case NETDEV_PRE_TYPE_CHANGE:
+		/* Forbid to change type of underlaying device */
+		return NOTIFY_BAD;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block team_notifier_block __read_mostly = {
+	.notifier_call = team_device_event,
+};
+
+
+/***********************
+ * Module init and exit
+ ***********************/
+
+static int __init team_module_init(void)
+{
+	int err;
+
+	register_netdevice_notifier(&team_notifier_block);
+
+	err = rtnl_link_register(&team_link_ops);
+	if (err)
+		goto err_rtnl_reg;
+
+	err = team_nl_init();
+	if (err)
+		goto err_nl_init;
+
+	return 0;
+
+err_nl_init:
+	rtnl_link_unregister(&team_link_ops);
+
+err_rtnl_reg:
+	unregister_netdevice_notifier(&team_notifier_block);
+
+	return err;
+}
+
+static void __exit team_module_exit(void)
+{
+	team_nl_fini();
+	rtnl_link_unregister(&team_link_ops);
+	unregister_netdevice_notifier(&team_notifier_block);
+}
+
+module_init(team_module_init);
+module_exit(team_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Ethernet team device driver");
+MODULE_ALIAS_RTNL_LINK(DRV_NAME);
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
new file mode 100644
index 0000000..b939a2c
--- /dev/null
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -0,0 +1,137 @@
+/*
+ * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
+#include <linux/if_team.h>
+
+struct ab_priv {
+	struct team_port __rcu *active_port;
+};
+
+static struct ab_priv *ab_priv(struct team *team)
+{
+	return (struct ab_priv *) &team->mode_priv;
+}
+
+static rx_handler_result_t ab_receive(struct team *team, struct team_port *port,
+				      struct sk_buff *skb) {
+	struct team_port *active_port;
+
+	active_port = rcu_dereference(ab_priv(team)->active_port);
+	if (active_port != port)
+		return RX_HANDLER_EXACT;
+	return RX_HANDLER_ANOTHER;
+}
+
+static bool ab_transmit(struct team *team, struct sk_buff *skb)
+{
+	struct team_port *active_port;
+
+	active_port = rcu_dereference(ab_priv(team)->active_port);
+	if (unlikely(!active_port))
+		goto drop;
+	skb->dev = active_port->dev;
+	if (dev_queue_xmit(skb))
+		return false;
+	return true;
+
+drop:
+	dev_kfree_skb(skb);
+	return false;
+}
+
+static void ab_port_leave(struct team *team, struct team_port *port)
+{
+	if (ab_priv(team)->active_port == port)
+		rcu_assign_pointer(ab_priv(team)->active_port, NULL);
+}
+
+static int ab_active_port_get(struct team *team, void *arg)
+{
+	u32 *ifindex = arg;
+
+	*ifindex = 0;
+	if (ab_priv(team)->active_port)
+		*ifindex = ab_priv(team)->active_port->dev->ifindex;
+	return 0;
+}
+
+static int ab_active_port_set(struct team *team, void *arg)
+{
+	u32 *ifindex = arg;
+	struct team_port *port;
+
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		if (port->dev->ifindex == *ifindex) {
+			rcu_assign_pointer(ab_priv(team)->active_port, port);
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+static struct team_option ab_options[] = {
+	{
+		.name = "activeport",
+		.type = TEAM_OPTION_TYPE_U32,
+		.getter = ab_active_port_get,
+		.setter = ab_active_port_set,
+	},
+};
+
+int ab_init(struct team *team)
+{
+	team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
+	return 0;
+}
+
+void ab_exit(struct team *team)
+{
+	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
+}
+
+static const struct team_mode_ops ab_mode_ops = {
+	.init			= ab_init,
+	.exit			= ab_exit,
+	.receive		= ab_receive,
+	.transmit		= ab_transmit,
+	.port_leave		= ab_port_leave,
+};
+
+static struct team_mode ab_mode = {
+	.kind		= "activebackup",
+	.owner		= THIS_MODULE,
+	.priv_size	= sizeof(struct ab_priv),
+	.ops		= &ab_mode_ops,
+};
+
+static int __init ab_init_module(void)
+{
+	return team_mode_register(&ab_mode);
+}
+
+static void __exit ab_cleanup_module(void)
+{
+	team_mode_unregister(&ab_mode);
+}
+
+module_init(ab_init_module);
+module_exit(ab_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Active-backup mode for team");
+MODULE_ALIAS("team-mode-activebackup");
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
new file mode 100644
index 0000000..0374052
--- /dev/null
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -0,0 +1,107 @@
+/*
+ * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_team.h>
+
+struct rr_priv {
+	unsigned int sent_packets;
+};
+
+static struct rr_priv *rr_priv(struct team *team)
+{
+	return (struct rr_priv *) &team->mode_priv;
+}
+
+static struct team_port *__get_first_port_up(struct team *team,
+					     struct team_port *port)
+{
+	struct team_port *cur;
+
+	if (port->linkup)
+		return port;
+	cur = port;
+	list_for_each_entry_continue_rcu(cur, &team->port_list, list)
+		if (cur->linkup)
+			return cur;
+	list_for_each_entry_rcu(cur, &team->port_list, list) {
+		if (cur == port)
+			break;
+		if (cur->linkup)
+			return cur;
+	}
+	return NULL;
+}
+
+static bool rr_transmit(struct team *team, struct sk_buff *skb)
+{
+	struct team_port *port;
+	int port_index;
+
+	port_index = rr_priv(team)->sent_packets++ % team->port_count;
+	port = team_get_port_by_index_rcu(team, port_index);
+	port = __get_first_port_up(team, port);
+	if (unlikely(!port))
+		goto drop;
+	skb->dev = port->dev;
+	if (dev_queue_xmit(skb))
+		return false;
+	return true;
+
+drop:
+	dev_kfree_skb(skb);
+	return false;
+}
+
+static int rr_port_enter(struct team *team, struct team_port *port)
+{
+	return team_port_set_team_mac(port);
+}
+
+static void rr_port_change_mac(struct team *team, struct team_port *port)
+{
+	team_port_set_team_mac(port);
+}
+
+static const struct team_mode_ops rr_mode_ops = {
+	.transmit		= rr_transmit,
+	.port_enter		= rr_port_enter,
+	.port_change_mac	= rr_port_change_mac,
+};
+
+static struct team_mode rr_mode = {
+	.kind		= "roundrobin",
+	.owner		= THIS_MODULE,
+	.priv_size	= sizeof(struct rr_priv),
+	.ops		= &rr_mode_ops,
+};
+
+static int __init rr_init_module(void)
+{
+	return team_mode_register(&rr_mode);
+}
+
+static void __exit rr_cleanup_module(void)
+{
+	team_mode_unregister(&rr_mode);
+}
+
+module_init(rr_init_module);
+module_exit(rr_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Round-robin mode for team");
+MODULE_ALIAS("team-mode-roundrobin");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 619b565..0b091b3 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -185,6 +185,7 @@ header-y += if_pppol2tp.h
 header-y += if_pppox.h
 header-y += if_slip.h
 header-y += if_strip.h
+header-y += if_team.h
 header-y += if_tr.h
 header-y += if_tun.h
 header-y += if_tunnel.h
diff --git a/include/linux/if.h b/include/linux/if.h
index db20bd4..06b6ef6 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -79,6 +79,7 @@
 #define IFF_TX_SKB_SHARING	0x10000	/* The interface supports sharing
 					 * skbs on transmit */
 #define IFF_UNICAST_FLT	0x20000		/* Supports unicast filtering	*/
+#define IFF_TEAM_PORT	0x40000		/* device used as team port */
 
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
new file mode 100644
index 0000000..df28f8a
--- /dev/null
+++ b/include/linux/if_team.h
@@ -0,0 +1,242 @@
+/*
+ * include/linux/if_team.h - Network team device driver header
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_TEAM_H_
+#define _LINUX_IF_TEAM_H_
+
+#ifdef __KERNEL__
+
+struct team_pcpu_stats {
+	u64			rx_packets;
+	u64			rx_bytes;
+	u64			rx_multicast;
+	u64			tx_packets;
+	u64			tx_bytes;
+	struct u64_stats_sync	syncp;
+	u32			rx_dropped;
+	u32			tx_dropped;
+};
+
+struct team;
+
+struct team_port {
+	struct net_device *dev;
+	struct hlist_node hlist; /* node in hash list */
+	struct list_head list; /* node in ordinary list */
+	struct team *team;
+	int index;
+
+	/*
+	 * A place for storing original values of the device before it
+	 * become a port.
+	 */
+	struct {
+		unsigned char dev_addr[MAX_ADDR_LEN];
+		unsigned int mtu;
+	} orig;
+
+	bool linkup;
+	u32 speed;
+	u8 duplex;
+
+	struct rcu_head rcu;
+};
+
+struct team_mode_ops {
+	int (*init)(struct team *team);
+	void (*exit)(struct team *team);
+	rx_handler_result_t (*receive)(struct team *team,
+				       struct team_port *port,
+				       struct sk_buff *skb);
+	bool (*transmit)(struct team *team, struct sk_buff *skb);
+	int (*port_enter)(struct team *team, struct team_port *port);
+	void (*port_leave)(struct team *team, struct team_port *port);
+	void (*port_change_mac)(struct team *team, struct team_port *port);
+};
+
+enum team_option_type {
+	TEAM_OPTION_TYPE_U32,
+	TEAM_OPTION_TYPE_STRING,
+};
+
+struct team_option {
+	struct list_head list;
+	const char *name;
+	enum team_option_type type;
+	int (*getter)(struct team *team, void *arg);
+	int (*setter)(struct team *team, void *arg);
+};
+
+struct team_mode {
+	struct list_head list;
+	const char *kind;
+	struct module *owner;
+	size_t priv_size;
+	const struct team_mode_ops *ops;
+};
+
+#define TEAM_PORT_HASHBITS 4
+#define TEAM_PORT_HASHENTRIES (1 << TEAM_PORT_HASHBITS)
+
+#define TEAM_MODE_PRIV_LONGS 4
+#define TEAM_MODE_PRIV_SIZE (sizeof(long) * TEAM_MODE_PRIV_LONGS)
+
+struct team {
+	struct net_device *dev; /* associated netdevice */
+	struct team_pcpu_stats __percpu *pcpu_stats;
+
+	spinlock_t lock; /* used for overall locking, e.g. port lists write */
+
+	/*
+	 * port lists with port count
+	 */
+	int port_count;
+	struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES];
+	struct list_head port_list;
+
+	struct list_head option_list;
+
+	const char *mode_kind;
+	struct team_mode_ops mode_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_ */
-- 
1.7.6

^ permalink raw reply related

* Re: [PATCH] Add TCP_NO_DELAYED_ACK socket option
From: Rick Jones @ 2011-10-26 20:06 UTC (permalink / raw)
  To: Andy Lutomirski; +Cc: netdev
In-Reply-To: <CALCETrVcG=F6FrcQ3PhGz27t9+6jGh0TGACfVQ6CeUX=EjNsAw@mail.gmail.com>

>> If the networks where this happens are indeed truly private, can they run a
>> private kernel?  Or use an LD_PRELOAD hack to wedge-in a
>> setsockopt(TCP_NODELAY) call into the application?  Or set something like
>> tcp_naglim_def on the application system(s)?  Or have the server application
>> make a setsockopt(TCP_MAXSEG) call before listen() to a value one byte below
>> that of what the application is sending?
>
> We control our server.  We don't control the server at the other end.
> We've tried to get them to do any of the above, but they seem
> unwilling or unable to do it.  I suspect that they're using various
> pieces from various third-party vendors that just don't care.

Making the setsockopt(TCP_MAXSEG) would be at your end :)  Presumably 
based on the minimum message size.  That would cause the connection to 
have an MSS == the request size so every request send should take the 
"is this send plus any queued unsent data >= MSS" path.

Another "at your end" possibility would be setting a rather small 
SO_RCVBUF size at your end before calling listen(), in hopes of 
triggering the window update.

>> Is the application actually "virtuous" in sending logically associated data
>> in one "send" call, and simply running afoul of Nagle+DelayedACK in having
>> multiple distinct requests outstanding at once, or is it actually quite evil
>> in that it is sending logically associated data in separate send calls?
>>
>
> The remote application generates messages meant for us, and they
> appear to send each message in its own segment.  I don't have the
> source, so I don't know whether they're really using one send call per
> message or whether they're using MSG_MORE, TCP_CORK, so some other
> mechanism.  Each message is time-sensitive and should be received as
> soon as possible afterq its sent (i.e. one-half rtt).  Unfortunately,
> when they send two messages and we don't ack the first one, the second
> gets delayed.  Turning off delayed acks helps but does not completely
> solve the problem.

If it is write,write,read  (multiple sends per logical message) in a 
packet trace you should see a partial request in the first segment, 
followed by the rest of the request  (and perhaps the second through 
Nth) in the second segment.  Or, I suppose your server application would 
have a receive complete with the first part of the first request, 
getting the second part of the request in a subsequent receive call.

If it is multiple requests at a time each sent in one send call, you 
should see a first segment arriving with a complete request within it, 
followed by a second segment with the next request(s).

rick jones

^ permalink raw reply

* Re: [net 0/7] bnx2x: driver and firmware fixes
From: David Miller @ 2011-10-26 20:02 UTC (permalink / raw)
  To: yanivr; +Cc: netdev
In-Reply-To: <1319622039-10553-1-git-send-email-yanivr@broadcom.com>

From: "Yaniv Rosner" <yanivr@broadcom.com>
Date: Wed, 26 Oct 2011 11:40:32 +0200

> The following patch series describe few link fixes and firmware update.
>  
> Please consider applying it to net.

The bnx2x driver has moved to a different location in the source tree,
therefore none of your patches apply.

^ permalink raw reply

* Re: [PATCH] ipv6: tcp: fix TCLASS value in ACK messages sent from TIME_WAIT
From: David Miller @ 2011-10-26 20:01 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <20111026.155908.1133603039272144196.davem@davemloft.net>

From: David Miller <davem@davemloft.net>
Date: Wed, 26 Oct 2011 15:59:08 -0400 (EDT)

> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Wed, 26 Oct 2011 08:42:11 +0200
> 
>> commit 66b13d99d96a (ipv4: tcp: fix TOS value in ACK messages sent from
>> TIME_WAIT) fixed IPv4 only.
>> 
>> This part is for the IPv6 side.
>> 
>> We alias tw_tclass and tw_tos, if socket family is INET6.
>> 
>> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> 
> Applied.

Nevermind...  reverted.

net/dccp/ipv6.c: In function ‘dccp_v6_send_response’:
net/dccp/ipv6.c:274:3: error: too few arguments to function ‘ip6_xmit’
include/net/ipv6.h:486:14: note: declared here
net/dccp/ipv6.c: In function ‘dccp_v6_ctl_send_reset’:
net/dccp/ipv6.c:329:3: error: too few arguments to function ‘ip6_xmit’
include/net/ipv6.h:486:14: note: declared here
net/dccp/probe.c: In function ‘dccpprobe_init’:

^ permalink raw reply

* Re: [PATCH] ipv6: tcp: fix TCLASS value in ACK messages sent from TIME_WAIT
From: David Miller @ 2011-10-26 19:59 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1319611331.18883.13.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 26 Oct 2011 08:42:11 +0200

> commit 66b13d99d96a (ipv4: tcp: fix TOS value in ACK messages sent from
> TIME_WAIT) fixed IPv4 only.
> 
> This part is for the IPv6 side.
> 
> We alias tw_tclass and tw_tos, if socket family is INET6.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

Did you happen to check to see that this does the right thing for
V4 mapped ipv6 sockets?

Thanks.

^ permalink raw reply

* Re: [PATCH net-next] ipv4: use IS_ENABLED() macro to cleanup code
From: David Miller @ 2011-10-26 19:56 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1319610874.18883.8.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 26 Oct 2011 08:34:34 +0200

> Le mardi 25 octobre 2011 à 19:30 -0400, David Miller a écrit :
> 
>> net/ipv4/ip_sockglue.c: In function ‘do_ip_setsockopt’:
>> net/ipv4/ip_sockglue.c:523:29: error: ‘LOOPBACK4_IPV6’ undeclared (first use in this function)
>> net/ipv4/ip_sockglue.c:523:29: note: each undeclared identifier is reported only once for each function it appears in
>> 
>> This fails because ip_sockglue.c guards the net/transp_v6.h header
>> inclusion with a real CPP guard.
> 
> Yep, it seems compiler was not able to perform optimisation of dead
> code.

It's not the issue of optimizing the code out, in the end the compiler
will optimize this stuff out.

Rather, the issue is that the code seen by the compiler must be parsable.

^ permalink raw reply

* Re: [PATCH] Add TCP_NO_DELAYED_ACK socket option
From: Andy Lutomirski @ 2011-10-26 19:35 UTC (permalink / raw)
  To: Rick Jones; +Cc: netdev
In-Reply-To: <4EA849E7.5030503@hp.com>

On Wed, Oct 26, 2011 at 10:56 AM, Rick Jones <rick.jones2@hp.com> wrote:
> On 10/25/2011 07:25 PM, Andy Lutomirski wrote:
>>
>> When talking to an unfixable interactive peer that fails to set
>> TCP_NODELAY, disabling delayed ACKs can help mitigate the problem.
>> This is an evil thing to do, but if the entire network is private,
>> it's not that evil.
>>
>> This works around a problem with the remote *application*, so make
>> it a socket option instead of a sysctl or a per-route option.
>>
>> Signed-off-by: Andy Lutomirski<luto@amacapital.net>
>> ---
>>
>> This patch is a bit embarrassing.  We talk to remote applications over
>> TCP that are very much interactive but don't set TCP_NODELAY.  These
>> applications apparently cannot be fixed.  As a partial workaround, if we
>> ACK every incoming segment, then as long as they don't transmit two
>> segments per rtt, we do pretty well.
>
> Embarrassing/evil indeed - is it really something to go into the kernel?

That's a good question.  It's in our kernel -- I don't know whether it
should go upstream.

>
> If the networks where this happens are indeed truly private, can they run a
> private kernel?  Or use an LD_PRELOAD hack to wedge-in a
> setsockopt(TCP_NODELAY) call into the application?  Or set something like
> tcp_naglim_def on the application system(s)?  Or have the server application
> make a setsockopt(TCP_MAXSEG) call before listen() to a value one byte below
> that of what the application is sending?

We control our server.  We don't control the server at the other end.
We've tried to get them to do any of the above, but they seem
unwilling or unable to do it.  I suspect that they're using various
pieces from various third-party vendors that just don't care.

>
> Is the application actually "virtuous" in sending logically associated data
> in one "send" call, and simply running afoul of Nagle+DelayedACK in having
> multiple distinct requests outstanding at once, or is it actually quite evil
> in that it is sending logically associated data in separate send calls?
>

The remote application generates messages meant for us, and they
appear to send each message in its own segment.  I don't have the
source, so I don't know whether they're really using one send call per
message or whether they're using MSG_MORE, TCP_CORK, so some other
mechanism.  Each message is time-sensitive and should be received as
soon as possible afterq its sent (i.e. one-half rtt).  Unfortunately,
when they send two messages and we don't ack the first one, the second
gets delayed.  Turning off delayed acks helps but does not completely
solve the problem.

> rick jones
>
> choir preaching follows:

:)  I agree.  Unfortunately I didn't write all this stuff.

^ permalink raw reply

* Re: [PATCH] Add TCP_NO_DELAYED_ACK socket option
From: Rick Jones @ 2011-10-26 17:56 UTC (permalink / raw)
  To: Andy Lutomirski; +Cc: netdev
In-Reply-To: <fc8b00b9978b4f956fa705badfaa138854abf919.1319595687.git.luto@amacapital.net>

On 10/25/2011 07:25 PM, Andy Lutomirski wrote:
> When talking to an unfixable interactive peer that fails to set
> TCP_NODELAY, disabling delayed ACKs can help mitigate the problem.
> This is an evil thing to do, but if the entire network is private,
> it's not that evil.
>
> This works around a problem with the remote *application*, so make
> it a socket option instead of a sysctl or a per-route option.
>
> Signed-off-by: Andy Lutomirski<luto@amacapital.net>
> ---
>
> This patch is a bit embarrassing.  We talk to remote applications over
> TCP that are very much interactive but don't set TCP_NODELAY.  These
> applications apparently cannot be fixed.  As a partial workaround, if we
> ACK every incoming segment, then as long as they don't transmit two
> segments per rtt, we do pretty well.

Embarrassing/evil indeed - is it really something to go into the kernel?

If the networks where this happens are indeed truly private, can they 
run a private kernel?  Or use an LD_PRELOAD hack to wedge-in a 
setsockopt(TCP_NODELAY) call into the application?  Or set something 
like tcp_naglim_def on the application system(s)?  Or have the server 
application make a setsockopt(TCP_MAXSEG) call before listen() to a 
value one byte below that of what the application is sending?

Is the application actually "virtuous" in sending logically associated 
data in one "send" call, and simply running afoul of Nagle+DelayedACK in 
having multiple distinct requests outstanding at once, or is it actually 
quite evil in that it is sending logically associated data in separate 
send calls?

rick jones

choir preaching follows:

raj@tardy:~$ cat usenet_replies/nagle_algorithm

 > I'm not familiar with this issue, and I'm mostly ignorant about what
 > tcp does below the sockets interface. Can anybody briefly explain what
 > "nagle" is, and how and when to turn it off? Or point me to the
 > appropriate manual.

In broad terms, whenever an application does a send() call, the logic
of the Nagle algorithm is supposed to go something like this:

1) Is the quantity of data in this send, plus any queued, unsent data,
    greater than the MSS (Maximum Segment Size) for this connection? If
    yes, send the data in the user's send now (modulo any other
    constraints such as receiver's advertised window and the TCP
    congestion window). If no, go to 2.

2) Is the connection to the remote otherwise idle? That is, is there
    no unACKed data outstanding on the network. If yes, send the data
    in the user's send now. If no, queue the data and wait. Either the
    application will continue to call send() with enough data to get to
    a full MSS-worth of data, or the remote will ACK all the currently
    sent, unACKed data, or our retransmission timer will expire.

Now, where applications run into trouble is when they have what might
be described as "write, write, read" behaviour, where they present
logically associated data to the transport in separate 'send' calls
and those sends are typically less than the MSS for the connection.
It isn't so much that they run afoul of Nagle as they run into issues
with the interaction of Nagle and the other heuristics operating on
the remote. In particular, the delayed ACK heuristics.

When a receiving TCP is deciding whether or not to send an ACK back to
the sender, in broad handwaving terms it goes through logic similar to
this:

a) is there data being sent back to the sender? if yes, piggy-back the
    ACK on the data segment.

b) is there a window update being sent back to the sender? if yes,
    piggy-back the ACK on the window update.

c) has the standalone ACK timer expired.

Window updates are generally triggered by the following heuristics:

i) would the window update be for a non-trivial fraction of the window
    - typically somewhere at or above 1/4 the window, that is, has the
    application "consumed" at least that much data? if yes, send a
    window update. if no, check ii.

ii) would the window update be for, the application "consumed," at
     least 2*MSS worth of data? if yes, send a window update, if no
     wait.

Now, going back to that write, write, read application, on the sending
side, the first write will be transmitted by TCP via nagle rule 2 -
the connection is otherwise idle.  However, the second small send will
be delayed as there is at that point unACKnowledged data outstanding
on the connection.

At the receiver, that small TCP segment will arrive and will be passed
to the application. The application does not have the entire app-level
message, so it will not send a reply (data to TCP) back. The typical
TCP window is much much larger than the MSS, so no window update would
be triggered by heuristic i. The data just arrived and consumed by the
application is < 2*MSS, so no window update from heuristic ii.  Since
there is no window update, no ACK is sent by heuristic b.

So, that leaves heuristic c - the standalone ACK timer. That ranges
anywhere between 50 and 200 milliseconds depending on the TCP stack in
use.

If you've read this far :) now we can take a look at the effect of
various things touted as "fixes" to applications experiencing this
interaction.  We take as our example a client-server application where
both the client and the server are implemented with a write of a small
application header, followed by application data.  First, the
"default" case which is with Nagle enabled (TCP_NODELAY _NOT_ set) and
with standard ACK behaviour:

               Client                     Server
              Req Header        ->
                                <-        Standalone ACK after Nms
              Req Data          ->
                                <-        Possible standalone ACK
                                <-        Rsp Header
              Standalone ACK    ->
                                <-        Rsp Data
     Possible standalone ACK    ->


For two "messages" we end-up with at least six segments on the wire.
The possible standalone ACKs will depend on whether the server's
response time, or client's think time is longer than the standalone
ACK interval on their respective sides. Now, if TCP_NODELAY is set we
see:


               Client                     Server
              Req Header        ->
              Req Data          ->
                                <-        Possible Standalone ACK after Nms
                                <-        Rsp Header
                                <-        Rsp Data
      Possible Standalone ACK   ->

In theory, we are down two four segments on the wire which seems good,
but frankly we can do better.  First though, consider what happens
when someone disables delayed ACKs

               Client                     Server
              Req Header        ->
                                <-        Immediate Standalone ACK
              Req Data          ->
                                <-        Immediate Standalone ACK
                                <-        Rsp Header
    Immediate Standalone ACK    ->
                                <-        Rsp Data
    Immediate Standalone ACK    ->

Now we definitly see 8 segments on the wire.  It will also be that way
if both TCP_NODELAY is set and delayed ACKs are disabled.

How about if the application did the "right" think in the first place?
That is sent the logically associated data at the same time:


               Client                     Server
              Request        ->
                             <-           Possible Standalone ACK
                                <-        Response
    Possible Standalone ACK    ->

We are down to two segments on the wire.

For "small" packets, the CPU cost is about the same regardless of data
or ACK.  This means that the application which is making the propper
gathering send call will spend far fewer CPU cycles in the networking
stack.

^ permalink raw reply

* Small glitch in neptune ethernet driver
From: Thomas Jarosch @ 2011-10-26 17:39 UTC (permalink / raw)
  To: davem; +Cc: netdev

Hi Dave,

consider this piece of code in net/ethernet/sun/niu.c:

static int __devinit phy_record(struct niu_parent *parent,
				struct phy_probe_info *p,
				int dev_id_1, int dev_id_2, u8 phy_port,
				int type)
{
...
	if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) {
		if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) &&
		    ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011) &&
		    ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8706))
			return 0;
	}
...
}

Here are the defines from sun/niu.h:

#define NIU_PHY_ID_MASK         0xfffff0f0
#define NIU_PHY_ID_BCM8704      0x00206030
#define NIU_PHY_ID_BCM8706      0x00206035

There's a zero at the end of the ID_MASK,
so the NIU_PHY_ID_BCM8706 will never match (ends on 5).

Report from cppcheck:
[sun/niu.c:8594]: (style) Mismatching comparison, the result is always true

The code will probably still work as the id for BCM8706
should match on NIU_PHY_ID_BCM8704, too.

Keep it or leave it?

Cheers,
Thomas

^ permalink raw reply

* Re: [PATCH] libertas: ensure we clean up a scan request properly
From: Dan Williams @ 2011-10-26 17:35 UTC (permalink / raw)
  To: Andres Salomon
  Cc: dsd, libertas-dev, netdev, linux-wireless, linville, linux-kernel
In-Reply-To: <20111026101926.48109373@queued.net>

On Wed, 2011-10-26 at 10:19 -0700, Andres Salomon wrote:
> Commit 2e30168b ("libertas: terminate scan when stopping interface")
> adds cleanup code to lbs_eth_stop to call cfg80211_scan_done if there's
> an outstanding cfg80211_scan_request.  However, it assumes that the
> scan request was allocated via the cfg80211 stack.  Libertas has
> its own internal allocation method, kept track of with
> priv->internal_scan.  This doesn't set scan_req->wiphy, amongst other
> things, which results in hitting a BUG() when we call cfg80211_scan_done
> on the request.
> 
> This provides a function to take care of the low-level scan_req cleanup
> details.  We simply call that to deal with finishing up scan requests.

Acked-by: Dan Williams <dcbw@redhat.com>

> The bug we were hitting was:
> 
> [  964.321495] kernel BUG at net/wireless/core.h:87!
> [  964.329970] Unable to handle kernel NULL pointer dereference at virtual address 00000000
> [  964.341963] pgd = dcf80000
> ...
> [  964.849998] 9fe0: 00000000 beb417b8 4018e280 401e822c 60000010 00000004 00000000 00000000
> [  964.865007] [<c003104c>] (__bug+0x1c/0x28) from [<c0384ffc>] (cfg80211_scan_done+0x54/0x6c)
> [  964.895324] [<c0384ffc>] (cfg80211_scan_done+0x54/0x6c) from [<bf028bac>] (lbs_eth_stop+0x10c/0x188 [libertas])
> [  964.895324] [<bf028bac>] (lbs_eth_stop+0x10c/0x188 [libertas]) from [<c03002a0>] (__dev_close_many+0x94/0xc4)
> [  964.918995] [<c03002a0>] (__dev_close_many+0x94/0xc4) from [<c030037c>] (dev_close_many+0x78/0xe0)
> 
> Signed-off-by: Andres Salomon <dilinger@queued.net>
> ---
>  drivers/net/wireless/libertas/cfg.c  |   25 +++++++++++++++++--------
>  drivers/net/wireless/libertas/cfg.h  |    1 +
>  drivers/net/wireless/libertas/main.c |    6 ++----
>  3 files changed, 20 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
> index ff63782..4fcd653 100644
> --- a/drivers/net/wireless/libertas/cfg.c
> +++ b/drivers/net/wireless/libertas/cfg.c
> @@ -728,15 +728,9 @@ static void lbs_scan_worker(struct work_struct *work)
>  		le16_to_cpu(scan_cmd->hdr.size),
>  		lbs_ret_scan, 0);
>  
> -	if (priv->scan_channel >= priv->scan_req->n_channels) {
> +	if (priv->scan_channel >= priv->scan_req->n_channels)
>  		/* Mark scan done */
> -		if (priv->internal_scan)
> -			kfree(priv->scan_req);
> -		else
> -			cfg80211_scan_done(priv->scan_req, false);
> -
> -		priv->scan_req = NULL;
> -	}
> +		lbs_scan_done(priv);
>  
>  	/* Restart network */
>  	if (carrier)
> @@ -774,6 +768,21 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal,
>  	lbs_deb_leave(LBS_DEB_CFG80211);
>  }
>  
> +/*
> + * Clean up priv->scan_req.  Should be used to handle the allocation details.
> + */
> +void lbs_scan_done(struct lbs_private *priv)
> +{
> +	WARN_ON(!priv->scan_req);
> +
> +	if (priv->internal_scan)
> +		kfree(priv->scan_req);
> +	else
> +		cfg80211_scan_done(priv->scan_req, false);
> +
> +	priv->scan_req = NULL;
> +}
> +
>  static int lbs_cfg_scan(struct wiphy *wiphy,
>  	struct net_device *dev,
>  	struct cfg80211_scan_request *request)
> diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
> index a02ee15..558168c 100644
> --- a/drivers/net/wireless/libertas/cfg.h
> +++ b/drivers/net/wireless/libertas/cfg.h
> @@ -16,6 +16,7 @@ int lbs_reg_notifier(struct wiphy *wiphy,
>  void lbs_send_disconnect_notification(struct lbs_private *priv);
>  void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
>  
> +void lbs_scan_done(struct lbs_private *priv);
>  void lbs_scan_deinit(struct lbs_private *priv);
>  int lbs_disconnect(struct lbs_private *priv, u16 reason);
>  
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index b03779b..39a6a7a 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -255,10 +255,8 @@ static int lbs_eth_stop(struct net_device *dev)
>  
>  	lbs_update_mcast(priv);
>  	cancel_delayed_work_sync(&priv->scan_work);
> -	if (priv->scan_req) {
> -		cfg80211_scan_done(priv->scan_req, false);
> -		priv->scan_req = NULL;
> -	}
> +	if (priv->scan_req)
> +		lbs_scan_done(priv);
>  
>  	netif_carrier_off(priv->dev);
>  

^ permalink raw reply

* Re: [PATCH net -v2] [BUGFIX] bonding: use flush_delayed_work_sync in bond_close
From: Jay Vosburgh @ 2011-10-26 17:31 UTC (permalink / raw)
  To: HAYASAKA Mitsuo
  Cc: netdev, Américo Wang, Stephen Hemminger, Andy Gospodarek,
	linux-kernel, yrl.pp-manager.tt
In-Reply-To: <4EA4E2EE.4050201@hitachi.com>

HAYASAKA Mitsuo <mitsuo.hayasaka.hu@hitachi.com> wrote:
[...]
>The interval of mii_mon was set to 1 to reproduce this bug easily and 
>the 802.3ad mode was used. Then, I executed the following command.
>
># while true; do ifconfig bond0 down; done &
># while true; do ifconfig bond0 up; done &
>
>This bug rarely occurs since it is the severe timing problem.
>I found that it is more easily to reproduce this bug when using guest OS.
>
>For example, it took one to three days for me to reproduce it on host OS, 
>but some hours on guest OS.

	Could you test this patch and see if it resolves the problem?

	This patch does a few things:

	All of the monitor functions that run on work queues are
modified to never unconditionally acquire RTNL; all will reschedule the
work and return if rtnl_trylock fails.  This covers bond_mii_monitor,
bond_activebackup_arp_mon, and bond_alb_monitor.

	The "clear out the work queues" calls in bond_close and
bond_uninit now call cancel_delayed_work_sync, which should either
delete a pending work item, or wait for an executing item to complete.
I chose cancel_ over the original patch's flush_ because we just want
the work queue stopped.  We don't need to have any pending items execute
if they're not already running.

	Also in reference to the previous, I'm not sure if we still need
to check for delayed_work_pending, but I've left those checks in place.

	Remove the "kill_timers" field and all references to it.  If
cancel_delayed_work_sync is safe to use, we do not need an extra
sentinel.

	Lastly, for testing purposes only, the bond_alb_monitor in this
patch includes an unconditional call to rtnl_trylock(); this is an
artifical way to make the race in that function easier to test for,
because the real race is very difficult to hit.

	This patch is against net-next as of yesterday.

	Comments?

	-J


diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index b33c099..0ae0d7c 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2110,9 +2110,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
 
 	read_lock(&bond->lock);
 
-	if (bond->kill_timers)
-		goto out;
-
 	//check if there are any slaves
 	if (bond->slave_cnt == 0)
 		goto re_arm;
@@ -2161,9 +2158,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
 	}
 
 re_arm:
-	if (!bond->kill_timers)
-		queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
-out:
+	queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
+
 	read_unlock(&bond->lock);
 }
 
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index d4fbd2e..13d1bf9 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1343,10 +1343,6 @@ void bond_alb_monitor(struct work_struct *work)
 
 	read_lock(&bond->lock);
 
-	if (bond->kill_timers) {
-		goto out;
-	}
-
 	if (bond->slave_cnt == 0) {
 		bond_info->tx_rebalance_counter = 0;
 		bond_info->lp_counter = 0;
@@ -1394,6 +1390,14 @@ void bond_alb_monitor(struct work_struct *work)
 		bond_info->tx_rebalance_counter = 0;
 	}
 
+	/* XXX - unconditional attempt at RTNL for testing purposes, as
+	 * normal case, below, is difficult to induce.
+	 */
+	read_unlock(&bond->lock);
+	if (rtnl_trylock())
+		rtnl_unlock();
+	read_lock(&bond->lock);
+
 	/* handle rlb stuff */
 	if (bond_info->rlb_enabled) {
 		if (bond_info->primary_is_promisc &&
@@ -1404,7 +1408,10 @@ void bond_alb_monitor(struct work_struct *work)
 			 * nothing else.
 			 */
 			read_unlock(&bond->lock);
-			rtnl_lock();
+			if (!rtnl_trylock()) {
+				read_lock(&bond->lock);
+				goto re_arm;
+			}
 
 			bond_info->rlb_promisc_timeout_counter = 0;
 
@@ -1440,9 +1447,8 @@ void bond_alb_monitor(struct work_struct *work)
 	}
 
 re_arm:
-	if (!bond->kill_timers)
-		queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
-out:
+	queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
+
 	read_unlock(&bond->lock);
 }
 
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 71efff3..e6fefff 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -774,9 +774,6 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
 
 	read_lock(&bond->lock);
 
-	if (bond->kill_timers)
-		goto out;
-
 	/* rejoin all groups on bond device */
 	__bond_resend_igmp_join_requests(bond->dev);
 
@@ -790,9 +787,9 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
 			__bond_resend_igmp_join_requests(vlan_dev);
 	}
 
-	if ((--bond->igmp_retrans > 0) && !bond->kill_timers)
+	if (--bond->igmp_retrans > 0)
 		queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5);
-out:
+
 	read_unlock(&bond->lock);
 }
 
@@ -2518,19 +2515,26 @@ void bond_mii_monitor(struct work_struct *work)
 	struct bonding *bond = container_of(work, struct bonding,
 					    mii_work.work);
 	bool should_notify_peers = false;
+	unsigned long delay;
 
 	read_lock(&bond->lock);
-	if (bond->kill_timers)
-		goto out;
+
+	delay = msecs_to_jiffies(bond->params.miimon);
 
 	if (bond->slave_cnt == 0)
 		goto re_arm;
 
-	should_notify_peers = bond_should_notify_peers(bond);
-
 	if (bond_miimon_inspect(bond)) {
 		read_unlock(&bond->lock);
-		rtnl_lock();
+
+		/* Race avoidance with bond_close flush of workqueue */
+		if (!rtnl_trylock()) {
+			read_lock(&bond->lock);
+			delay = 1;
+			should_notify_peers = false;
+			goto re_arm;
+		}
+
 		read_lock(&bond->lock);
 
 		bond_miimon_commit(bond);
@@ -2540,15 +2544,21 @@ void bond_mii_monitor(struct work_struct *work)
 		read_lock(&bond->lock);
 	}
 
+	should_notify_peers = bond_should_notify_peers(bond);
+
 re_arm:
-	if (bond->params.miimon && !bond->kill_timers)
-		queue_delayed_work(bond->wq, &bond->mii_work,
-				   msecs_to_jiffies(bond->params.miimon));
-out:
+	if (bond->params.miimon)
+		queue_delayed_work(bond->wq, &bond->mii_work, delay);
+
 	read_unlock(&bond->lock);
 
 	if (should_notify_peers) {
-		rtnl_lock();
+		if (!rtnl_trylock()) {
+			read_lock(&bond->lock);
+			bond->send_peer_notif++;
+			read_unlock(&bond->lock);
+			return;
+		}
 		netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
 		rtnl_unlock();
 	}
@@ -2790,9 +2800,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
 
 	delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
 
-	if (bond->kill_timers)
-		goto out;
-
 	if (bond->slave_cnt == 0)
 		goto re_arm;
 
@@ -2889,9 +2896,9 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
 	}
 
 re_arm:
-	if (bond->params.arp_interval && !bond->kill_timers)
+	if (bond->params.arp_interval)
 		queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
 	read_unlock(&bond->lock);
 }
 
@@ -3132,9 +3139,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
 	read_lock(&bond->lock);
 
-	if (bond->kill_timers)
-		goto out;
-
 	delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
 
 	if (bond->slave_cnt == 0)
@@ -3144,7 +3148,15 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
 	if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
 		read_unlock(&bond->lock);
-		rtnl_lock();
+
+		/* Race avoidance with bond_close flush of workqueue */
+		if (!rtnl_trylock()) {
+			read_lock(&bond->lock);
+			delta_in_ticks = 1;
+			should_notify_peers = false;
+			goto re_arm;
+		}
+
 		read_lock(&bond->lock);
 
 		bond_ab_arp_commit(bond, delta_in_ticks);
@@ -3157,13 +3169,18 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 	bond_ab_arp_probe(bond);
 
 re_arm:
-	if (bond->params.arp_interval && !bond->kill_timers)
+	if (bond->params.arp_interval)
 		queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
-out:
+
 	read_unlock(&bond->lock);
 
 	if (should_notify_peers) {
-		rtnl_lock();
+		if (!rtnl_trylock()) {
+			read_lock(&bond->lock);
+			bond->send_peer_notif++;
+			read_unlock(&bond->lock);
+			return;
+		}
 		netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
 		rtnl_unlock();
 	}
@@ -3425,8 +3442,6 @@ static int bond_open(struct net_device *bond_dev)
 	struct slave *slave;
 	int i;
 
-	bond->kill_timers = 0;
-
 	/* reset slave->backup and slave->inactive */
 	read_lock(&bond->lock);
 	if (bond->slave_cnt > 0) {
@@ -3495,33 +3510,30 @@ static int bond_close(struct net_device *bond_dev)
 
 	bond->send_peer_notif = 0;
 
-	/* signal timers not to re-arm */
-	bond->kill_timers = 1;
-
 	write_unlock_bh(&bond->lock);
 
 	if (bond->params.miimon) {  /* link check interval, in milliseconds. */
-		cancel_delayed_work(&bond->mii_work);
+		cancel_delayed_work_sync(&bond->mii_work);
 	}
 
 	if (bond->params.arp_interval) {  /* arp interval, in milliseconds. */
-		cancel_delayed_work(&bond->arp_work);
+		cancel_delayed_work_sync(&bond->arp_work);
 	}
 
 	switch (bond->params.mode) {
 	case BOND_MODE_8023AD:
-		cancel_delayed_work(&bond->ad_work);
+		cancel_delayed_work_sync(&bond->ad_work);
 		break;
 	case BOND_MODE_TLB:
 	case BOND_MODE_ALB:
-		cancel_delayed_work(&bond->alb_work);
+		cancel_delayed_work_sync(&bond->alb_work);
 		break;
 	default:
 		break;
 	}
 
 	if (delayed_work_pending(&bond->mcast_work))
-		cancel_delayed_work(&bond->mcast_work);
+		cancel_delayed_work_sync(&bond->mcast_work);
 
 	if (bond_is_lb(bond)) {
 		/* Must be called only after all
@@ -4368,26 +4380,22 @@ static void bond_setup(struct net_device *bond_dev)
 
 static void bond_work_cancel_all(struct bonding *bond)
 {
-	write_lock_bh(&bond->lock);
-	bond->kill_timers = 1;
-	write_unlock_bh(&bond->lock);
-
 	if (bond->params.miimon && delayed_work_pending(&bond->mii_work))
-		cancel_delayed_work(&bond->mii_work);
+		cancel_delayed_work_sync(&bond->mii_work);
 
 	if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work))
-		cancel_delayed_work(&bond->arp_work);
+		cancel_delayed_work_sync(&bond->arp_work);
 
 	if (bond->params.mode == BOND_MODE_ALB &&
 	    delayed_work_pending(&bond->alb_work))
-		cancel_delayed_work(&bond->alb_work);
+		cancel_delayed_work_sync(&bond->alb_work);
 
 	if (bond->params.mode == BOND_MODE_8023AD &&
 	    delayed_work_pending(&bond->ad_work))
-		cancel_delayed_work(&bond->ad_work);
+		cancel_delayed_work_sync(&bond->ad_work);
 
 	if (delayed_work_pending(&bond->mcast_work))
-		cancel_delayed_work(&bond->mcast_work);
+		cancel_delayed_work_sync(&bond->mcast_work);
 }
 
 /*
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 82fec5f..1aecc37 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -222,7 +222,6 @@ struct bonding {
 			       struct slave *);
 	rwlock_t lock;
 	rwlock_t curr_slave_lock;
-	s8       kill_timers;
 	u8	 send_peer_notif;
 	s8	 setup_by_slave;
 	s8       igmp_retrans;


---
	-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com

^ permalink raw reply related

* [PATCH] libertas: ensure we clean up a scan request properly
From: Andres Salomon @ 2011-10-26 17:19 UTC (permalink / raw)
  To: Dan Williams
  Cc: linville, libertas-dev, linux-wireless, netdev, linux-kernel, dsd

Commit 2e30168b ("libertas: terminate scan when stopping interface")
adds cleanup code to lbs_eth_stop to call cfg80211_scan_done if there's
an outstanding cfg80211_scan_request.  However, it assumes that the
scan request was allocated via the cfg80211 stack.  Libertas has
its own internal allocation method, kept track of with
priv->internal_scan.  This doesn't set scan_req->wiphy, amongst other
things, which results in hitting a BUG() when we call cfg80211_scan_done
on the request.

This provides a function to take care of the low-level scan_req cleanup
details.  We simply call that to deal with finishing up scan requests.

The bug we were hitting was:

[  964.321495] kernel BUG at net/wireless/core.h:87!
[  964.329970] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[  964.341963] pgd = dcf80000
...
[  964.849998] 9fe0: 00000000 beb417b8 4018e280 401e822c 60000010 00000004 00000000 00000000
[  964.865007] [<c003104c>] (__bug+0x1c/0x28) from [<c0384ffc>] (cfg80211_scan_done+0x54/0x6c)
[  964.895324] [<c0384ffc>] (cfg80211_scan_done+0x54/0x6c) from [<bf028bac>] (lbs_eth_stop+0x10c/0x188 [libertas])
[  964.895324] [<bf028bac>] (lbs_eth_stop+0x10c/0x188 [libertas]) from [<c03002a0>] (__dev_close_many+0x94/0xc4)
[  964.918995] [<c03002a0>] (__dev_close_many+0x94/0xc4) from [<c030037c>] (dev_close_many+0x78/0xe0)

Signed-off-by: Andres Salomon <dilinger@queued.net>
---
 drivers/net/wireless/libertas/cfg.c  |   25 +++++++++++++++++--------
 drivers/net/wireless/libertas/cfg.h  |    1 +
 drivers/net/wireless/libertas/main.c |    6 ++----
 3 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index ff63782..4fcd653 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -728,15 +728,9 @@ static void lbs_scan_worker(struct work_struct *work)
 		le16_to_cpu(scan_cmd->hdr.size),
 		lbs_ret_scan, 0);
 
-	if (priv->scan_channel >= priv->scan_req->n_channels) {
+	if (priv->scan_channel >= priv->scan_req->n_channels)
 		/* Mark scan done */
-		if (priv->internal_scan)
-			kfree(priv->scan_req);
-		else
-			cfg80211_scan_done(priv->scan_req, false);
-
-		priv->scan_req = NULL;
-	}
+		lbs_scan_done(priv);
 
 	/* Restart network */
 	if (carrier)
@@ -774,6 +768,21 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal,
 	lbs_deb_leave(LBS_DEB_CFG80211);
 }
 
+/*
+ * Clean up priv->scan_req.  Should be used to handle the allocation details.
+ */
+void lbs_scan_done(struct lbs_private *priv)
+{
+	WARN_ON(!priv->scan_req);
+
+	if (priv->internal_scan)
+		kfree(priv->scan_req);
+	else
+		cfg80211_scan_done(priv->scan_req, false);
+
+	priv->scan_req = NULL;
+}
+
 static int lbs_cfg_scan(struct wiphy *wiphy,
 	struct net_device *dev,
 	struct cfg80211_scan_request *request)
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
index a02ee15..558168c 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -16,6 +16,7 @@ int lbs_reg_notifier(struct wiphy *wiphy,
 void lbs_send_disconnect_notification(struct lbs_private *priv);
 void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
 
+void lbs_scan_done(struct lbs_private *priv);
 void lbs_scan_deinit(struct lbs_private *priv);
 int lbs_disconnect(struct lbs_private *priv, u16 reason);
 
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index b03779b..39a6a7a 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -255,10 +255,8 @@ static int lbs_eth_stop(struct net_device *dev)
 
 	lbs_update_mcast(priv);
 	cancel_delayed_work_sync(&priv->scan_work);
-	if (priv->scan_req) {
-		cfg80211_scan_done(priv->scan_req, false);
-		priv->scan_req = NULL;
-	}
+	if (priv->scan_req)
+		lbs_scan_done(priv);
 
 	netif_carrier_off(priv->dev);
 
-- 
1.7.2.5

^ permalink raw reply related

* Re: tg3: BMC stops responding in 3.0
From: Arkadiusz Miśkiewicz @ 2011-10-26 15:49 UTC (permalink / raw)
  To: Matt Carlson; +Cc: Michael Chan, netdev@vger.kernel.org
In-Reply-To: <201110010737.59434.a.miskiewicz@gmail.com>

On Saturday 01 of October 2011, Arkadiusz Miśkiewicz wrote:
> On Saturday 01 of October 2011, Matt Carlson wrote:
> > On Fri, Sep 30, 2011 at 01:06:25AM -0700, Arkadiusz Mi??kiewicz wrote:
> > > On Friday 30 of September 2011, Matt Carlson wrote:
> > > > On Mon, Sep 26, 2011 at 11:31:33AM -0700, Arkadiusz Mi??kiewicz wrote:
> > > > > On Monday 26 of September 2011, Matt Carlson wrote:
> > > > > > On Fri, Sep 23, 2011 at 12:45:50PM -0700, Arkadiusz Mi??kiewicz
> 
> wrote:
> > > > > > > Hi,
> > > > > > > 
> > > > > > > I was using 2.6.38.8 and recently tried to switch to 3.0.4 on
> > > > > > > Tyan S2891 platform.
> > > > > > > 
> > > > > > > This platform uses tg3:
> > > > > > > tg3 0000:0a:09.1: eth1: Tigon3 [partno(BCM95704) rev 2003]
> > > > > > > (PCIX:133MHz:64- bit) MAC address 00:e0:81:33:5e:af
> > > > > > > tg3 0000:0a:09.1: eth1: attached PHY is 5704 (10/100/1000Base-T
> > > > > > > Ethernet) (WireSpeed[1], EEE[0])
> > > > > > > tg3 0000:0a:09.1: eth1: RXcsums[1] LinkChgREG[0] MIirq[0]
> > > > > > > ASF[0] TSOcap[1] tg3 0000:0a:09.1: eth1: dma_rwctrl[769f4000]
> > > > > > > dma_mask[64-bit]
> > > > > > > 
> > > > > > > With 2.6.38.8 everything was working fine. With 3.0.4 there is
> > > > > > > a problem. As soon as tg3 module is loaded or eth0 configured
> > > > > > > (can't tell which one since the machine is 400km away from me
> > > > > > > and I have no way to play with it other than ipmi or ssh) BMC
> > > > > > > stops responding (so all ipmitool commands over LAN stop
> > > > > > > working). Normal tg3 activity is not affected - I can ssh-in
> > > > > > > without a problem etc but ipmi over lan doesn't work.
> > > > > > > 
> > > > > > > From ssh console "ipmitool lan print" works, shows data but for
> > > > > > > example after "ipmitool mc reset cold" it doesn't recover -
> > > > > > > ipmitool returns "Invalid channel: 255". I have to reboot to
> > > > > > > 2.6.38.8 and then issue "ipmitool mc reset cold" to recover.
> > > > > > > 
> > > > > > > Any idea which tg3 change could break this? Can't bisect this
> > > > > > > due remote access only.
> > > > > > > 
> > > > > > > I was hoping that maybe
> > > > > > > 9e975cc291d80d5e4562d6bed15ec171e896d69b "tg3: Fix io failures
> > > > > > > after chip reset" will fix things for me but no - this doesn't
> > > > > > > help.
> > > > > > 
> > > > > > What version of the tg3 driver are you working with?
> > > > > 
> > > > > The one in 3.0.4 kernel. I think it's 3.119 (at least modinfo says
> > > > > so).
> > > > 
> > > > Unfortunately there were a lot of changes between 3.117 and 3.119(+).
> > > > Is there any way you can narrow down the gap?
> > > 
> > > The machines are 400km away from me and it's hard to debug that way
> > > then ipmi/network conectivity is in stake :-/ I could try some form of
> > > bisecting but need to know if all git versions between 3.117 and 3.119
> > > were known to be safe and working? I don't want to loose any
> > > conectivity to this machine.
> > > 
> > > I was going to try 2.6.39 but it looks like it also uses 3.117 driver.
> > 
> > O.K.  Can you give me the details of your machine?  Maybe we have the
> > exact machine or a machine similar enough to reproduce the problem with.

Did you have any chance to find and test on a similar system?

> 
> It's 1U server, with Tyan S2891 mainboard and some chenbro chassis. ipmi
> and eth0 share physical rj45 port in the server.
> 
> lspci & dmidecode below:
> 
> 00:00.0 Memory controller: nVidia Corporation CK804 Memory Controller (rev
> a3) Subsystem: Tyan Computer Thunder K8SRE Mainboard
> 	Flags: bus master, 66MHz, fast devsel, latency 0
> 	Capabilities: [44] HyperTransport: Slave or Primary Interface
> 	Capabilities: [e0] HyperTransport: MSI Mapping Enable+ Fixed-
> 
> 00:01.0 ISA bridge: nVidia Corporation CK804 ISA Bridge (rev a3)
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0
> 	I/O ports at 8c00 [size=1K]
> 
> 00:01.1 SMBus: nVidia Corporation CK804 SMBus (rev a2)
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: 66MHz, fast devsel
> 	I/O ports at 1000 [size=32]
> 	I/O ports at 5000 [size=64]
> 	I/O ports at 5040 [size=64]
> 	Capabilities: [44] Power Management version 2
> 
> 00:02.0 USB Controller: nVidia Corporation CK804 USB Controller (rev a2)
> (prog-if 10 [OHCI])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0, IRQ 10
> 	Memory at dd000000 (32-bit, non-prefetchable) [size=4K]
> 	Capabilities: [44] Power Management version 2
> 
> 00:02.1 USB Controller: nVidia Corporation CK804 USB Controller (rev a3)
> (prog-if 20 [EHCI])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0, IRQ 7
> 	Memory at dd001000 (32-bit, non-prefetchable) [size=256]
> 	Capabilities: [44] Debug port: BAR=1 offset=0098
> 	Capabilities: [80] Power Management version 2
> 
> 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev f2) (prog-if 8a
> [Master SecP PriP])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0
> 	[virtual] Memory at 000001f0 (32-bit, non-prefetchable) [size=8]
> 	[virtual] Memory at 000003f0 (type 3, non-prefetchable) [size=1]
> 	[virtual] Memory at 00000170 (32-bit, non-prefetchable) [size=8]
> 	[virtual] Memory at 00000370 (type 3, non-prefetchable) [size=1]
> 	I/O ports at 1400 [size=16]
> 	Capabilities: [44] Power Management version 2
> 
> 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> f3) (prog-if 85 [Master SecO PriO])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0, IRQ 11
> 	I/O ports at 1440 [size=8]
> 	I/O ports at 1434 [size=4]
> 	I/O ports at 1438 [size=8]
> 	I/O ports at 1430 [size=4]
> 	I/O ports at 1410 [size=16]
> 	Memory at dd002000 (32-bit, non-prefetchable) [size=4K]
> 	Capabilities: [44] Power Management version 2
> 
> 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> f3) (prog-if 85 [Master SecO PriO])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, 66MHz, fast devsel, latency 0, IRQ 10
> 	I/O ports at 1458 [size=8]
> 	I/O ports at 144c [size=4]
> 	I/O ports at 1450 [size=8]
> 	I/O ports at 1448 [size=4]
> 	I/O ports at 1420 [size=16]
> 	Memory at dd003000 (32-bit, non-prefetchable) [size=4K]
> 	Capabilities: [44] Power Management version 2
> 
> 00:09.0 PCI bridge: nVidia Corporation CK804 PCI Bridge (rev a2) (prog-if
> 00 [Normal decode])
> 	Flags: bus master, 66MHz, fast devsel, latency 0
> 	Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
> 	I/O behind bridge: 00002000-00002fff
> 	Memory behind bridge: dd100000-deffffff
> 	Prefetchable memory behind bridge: d0000000-d00fffff
> 
> 00:0e.0 PCI bridge: nVidia Corporation CK804 PCIE Bridge (rev a3) (prog-if
> 00 [Normal decode])
> 	Flags: bus master, fast devsel, latency 0
> 	Bus: primary=00, secondary=02, subordinate=02, sec-latency=0
> 	Capabilities: [40] Power Management version 2
> 	Capabilities: [48] MSI: Enable- Count=1/2 Maskable- 64bit+
> 	Capabilities: [58] HyperTransport: MSI Mapping Enable- Fixed-
> 	Capabilities: [80] Express Root Port (Slot+), MSI 00
> 	Kernel driver in use: pcieport
> 
> 00:18.0 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> HyperTransport Technology Configuration
> 	Flags: fast devsel
> 	Capabilities: [80] HyperTransport: Host or Secondary Interface
> 	Capabilities: [a0] HyperTransport: Host or Secondary Interface
> 	Capabilities: [c0] HyperTransport: Host or Secondary Interface
> 
> 00:18.1 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> Address Map
> 	Flags: fast devsel
> 
> 00:18.2 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> DRAM Controller
> 	Flags: fast devsel
> 
> 00:18.3 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> Miscellaneous Control
> 	Flags: fast devsel
> 
> 00:19.0 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> HyperTransport Technology Configuration
> 	Flags: fast devsel
> 	Capabilities: [80] HyperTransport: Host or Secondary Interface
> 	Capabilities: [a0] HyperTransport: Host or Secondary Interface
> 	Capabilities: [c0] HyperTransport: Host or Secondary Interface
> 
> 00:19.1 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> Address Map
> 	Flags: fast devsel
> 
> 00:19.2 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> DRAM Controller
> 	Flags: fast devsel
> 
> 00:19.3 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron]
> Miscellaneous Control
> 	Flags: fast devsel
> 
> 01:07.0 VGA compatible controller: ATI Technologies Inc Rage XL (rev 27)
> (prog-if 00 [VGA controller])
> 	Subsystem: ATI Technologies Inc Rage XL
> 	Flags: bus master, stepping, medium devsel, latency 66, IRQ 11
> 	Memory at de000000 (32-bit, non-prefetchable) [size=16M]
> 	I/O ports at 2000 [size=256]
> 	Memory at dd100000 (32-bit, non-prefetchable) [size=4K]
> 	[virtual] Expansion ROM at d0000000 [disabled] [size=128K]
> 	Capabilities: [5c] Power Management version 2
> 
> 08:0a.0 PCI bridge: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge (rev
> 12) (prog-if 00 [Normal decode])
> 	Flags: bus master, 66MHz, medium devsel, latency 64
> 	Bus: primary=08, secondary=09, subordinate=09, sec-latency=64
> 	I/O behind bridge: 00003000-00003fff
> 	Memory behind bridge: df300000-df3fffff
> 	Prefetchable memory behind bridge: 00000000df500000-00000000df5fffff
> 	Capabilities: [a0] PCI-X bridge device
> 	Capabilities: [b8] HyperTransport: Interrupt Discovery and Configuration
> 	Capabilities: [c0] HyperTransport: Slave or Primary Interface
> 
> 08:0a.1 PIC: Advanced Micro Devices [AMD] AMD-8131 PCI-X IOAPIC (rev 01)
> (prog-if 10 [IO-APIC])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, medium devsel, latency 0
> 	Memory at df200000 (64-bit, non-prefetchable) [size=4K]
> 
> 08:0b.0 PCI bridge: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge (rev
> 12) (prog-if 00 [Normal decode])
> 	Flags: bus master, 66MHz, medium devsel, latency 64
> 	Bus: primary=08, secondary=0a, subordinate=0a, sec-latency=64
> 	Memory behind bridge: df400000-df4fffff
> 	Capabilities: [a0] PCI-X bridge device
> 	Capabilities: [b8] HyperTransport: Interrupt Discovery and Configuration
> 
> 08:0b.1 PIC: Advanced Micro Devices [AMD] AMD-8131 PCI-X IOAPIC (rev 01)
> (prog-if 10 [IO-APIC])
> 	Subsystem: Tyan Computer Device 2891
> 	Flags: bus master, medium devsel, latency 0
> 	Memory at df201000 (64-bit, non-prefetchable) [size=4K]
> 
> 09:08.0 Fibre Channel: QLogic Corp. ISP2312-based 2Gb Fibre Channel to
> PCI-X HBA (rev 02)
> 	Subsystem: Compaq Computer Corporation Device 0100
> 	Flags: bus master, 66MHz, medium devsel, latency 128, IRQ 24
> 	I/O ports at 3000 [size=256]
> 	Memory at df300000 (64-bit, non-prefetchable) [size=4K]
> 	[virtual] Expansion ROM at df500000 [disabled] [size=128K]
> 	Capabilities: [44] Power Management version 2
> 	Capabilities: [4c] PCI-X non-bridge device
> 	Capabilities: [54] MSI: Enable- Count=1/8 Maskable- 64bit+
> 	Capabilities: [64] CompactPCI hot-swap <?>
> 	Kernel driver in use: qla2xxx
> 
> 0a:09.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5704 Gigabit
> Ethernet (rev 03)
> 	Subsystem: Broadcom Corporation NetXtreme BCM5704 Gigabit Ethernet
> 	Flags: bus master, 66MHz, medium devsel, latency 64, IRQ 28
> 	Memory at df410000 (64-bit, non-prefetchable) [size=64K]
> 	Memory at df400000 (64-bit, non-prefetchable) [size=64K]
> 	Capabilities: [40] PCI-X non-bridge device
> 	Capabilities: [48] Power Management version 2
> 	Capabilities: [50] Vital Product Data
> 	Capabilities: [58] MSI: Enable- Count=1/8 Maskable- 64bit+
> 	Kernel driver in use: tg3
> 
> 0a:09.1 Ethernet controller: Broadcom Corporation NetXtreme BCM5704 Gigabit
> Ethernet (rev 03)
> 	Subsystem: Broadcom Corporation NetXtreme BCM5704 Gigabit Ethernet
> 	Flags: bus master, 66MHz, medium devsel, latency 64, IRQ 29
> 	Memory at df430000 (64-bit, non-prefetchable) [size=64K]
> 	Memory at df420000 (64-bit, non-prefetchable) [size=64K]
> 	Capabilities: [40] PCI-X non-bridge device
> 	Capabilities: [48] Power Management version 2
> 	Capabilities: [50] Vital Product Data
> 	Capabilities: [58] MSI: Enable- Count=1/8 Maskable- 64bit+
> 	Kernel driver in use: tg3
> 
> 
> 
> # dmidecode 2.11
> SMBIOS version fixup (2.33 -> 2.3).
> SMBIOS 2.3 present.
> 34 structures occupying 1202 bytes.
> Table at 0x7FFEF000.
> 
> Handle 0x0000, DMI type 0, 20 bytes
> BIOS Information
> 	Vendor: Phoenix Technologies Ltd.
> 	Version: 2003Q2
> 	Release Date: 03/27/2006
> 	Address: 0xE65B0
> 	Runtime Size: 105040 bytes
> 	ROM Size: 1024 kB
> 	Characteristics:
> 		PCI is supported
> 		PNP is supported
> 		BIOS is upgradeable
> 		BIOS shadowing is allowed
> 		ESCD support is available
> 		Boot from CD is supported
> 		Selectable boot is supported
> 		EDD is supported
> 		5.25"/360 kB floppy services are supported (int 13h)
> 		5.25"/1.2 MB floppy services are supported (int 13h)
> 		3.5"/720 kB floppy services are supported (int 13h)
> 		3.5"/2.88 MB floppy services are supported (int 13h)
> 		Print screen service is supported (int 5h)
> 		8042 keyboard services are supported (int 9h)
> 		Serial services are supported (int 14h)
> 		Printer services are supported (int 17h)
> 		CGA/mono video services are supported (int 10h)
> 		ACPI is supported
> 		USB legacy is supported
> 
> Handle 0x0001, DMI type 1, 25 bytes
> System Information
> 	Manufacturer: TYAN Computer Corp
> 	Product Name: S2891
> 	Version: REFERENCE
> 	Serial Number: 0123456789
> 	UUID: Not Settable
> 	Wake-up Type: Power Switch
> 
> Handle 0x0002, DMI type 2, 8 bytes
> Base Board Information
> 	Manufacturer: TYAN Computer Corp
> 	Product Name: GT24-B2891
> 	Version: REFERENCE
> 	Serial Number: 0123456789
> 
> Handle 0x0003, DMI type 3, 17 bytes
> Chassis Information
> 	Manufacturer: TYAN Computer Corp
> 	Type: Main Server Chassis
> 	Lock: Not Present
> 	Version: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Boot-up State: Unknown
> 	Power Supply State: Unknown
> 	Thermal State: Unknown
> 	Security Status: Unknown
> 	OEM Information: 0x00000000
> 
> Handle 0x0004, DMI type 4, 35 bytes
> Processor Information
> 	Socket Designation: CPU0-Socket 940
> 	Type: Central Processor
> 	Family: Opteron
> 	Manufacturer: AMD
> 	ID: 12 0F 02 00 FF FB 8B 17
> 	Signature: Family 15, Model 33, Stepping 2
> 	Flags:
> 		FPU (Floating-point unit on-chip)
> 		VME (Virtual mode extension)
> 		DE (Debugging extension)
> 		PSE (Page size extension)
> 		TSC (Time stamp counter)
> 		MSR (Model specific registers)
> 		PAE (Physical address extension)
> 		MCE (Machine check exception)
> 		CX8 (CMPXCHG8 instruction supported)
> 		APIC (On-chip APIC hardware supported)
> 		SEP (Fast system call)
> 		MTRR (Memory type range registers)
> 		PGE (Page global enable)
> 		MCA (Machine check architecture)
> 		CMOV (Conditional move instruction supported)
> 		PAT (Page attribute table)
> 		PSE-36 (36-bit page size extension)
> 		CLFSH (CLFLUSH instruction supported)
> 		MMX (MMX technology supported)
> 		FXSR (FXSAVE and FXSTOR instructions supported)
> 		SSE (Streaming SIMD extensions)
> 		SSE2 (Streaming SIMD extensions 2)
> 		HTT (Multi-threading)
> 	Version: AMD
> 	Voltage: 1.2 V
> 	External Clock: 200 MHz
> 	Max Speed: 3000 MHz
> 	Current Speed: 2000 MHz
> 	Status: Populated, Enabled
> 	Upgrade: None
> 	L1 Cache Handle: 0x0006
> 	L2 Cache Handle: 0x0007
> 	L3 Cache Handle: Not Provided
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0005, DMI type 4, 35 bytes
> Processor Information
> 	Socket Designation: CPU1-Socket 940
> 	Type: Central Processor
> 	Family: Opteron
> 	Manufacturer: AMD
> 	ID: 12 0F 02 00 FF FB 8B 17
> 	Signature: Family 15, Model 33, Stepping 2
> 	Flags:
> 		FPU (Floating-point unit on-chip)
> 		VME (Virtual mode extension)
> 		DE (Debugging extension)
> 		PSE (Page size extension)
> 		TSC (Time stamp counter)
> 		MSR (Model specific registers)
> 		PAE (Physical address extension)
> 		MCE (Machine check exception)
> 		CX8 (CMPXCHG8 instruction supported)
> 		APIC (On-chip APIC hardware supported)
> 		SEP (Fast system call)
> 		MTRR (Memory type range registers)
> 		PGE (Page global enable)
> 		MCA (Machine check architecture)
> 		CMOV (Conditional move instruction supported)
> 		PAT (Page attribute table)
> 		PSE-36 (36-bit page size extension)
> 		CLFSH (CLFLUSH instruction supported)
> 		MMX (MMX technology supported)
> 		FXSR (FXSAVE and FXSTOR instructions supported)
> 		SSE (Streaming SIMD extensions)
> 		SSE2 (Streaming SIMD extensions 2)
> 		HTT (Multi-threading)
> 	Version: AMD
> 	Voltage: 1.2 V
> 	External Clock: 200 MHz
> 	Max Speed: 3000 MHz
> 	Current Speed: 2000 MHz
> 	Status: Populated, Enabled
> 	Upgrade: None
> 	L1 Cache Handle: 0x0008
> 	L2 Cache Handle: 0x0009
> 	L3 Cache Handle: Not Provided
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0006, DMI type 7, 19 bytes
> Cache Information
> 	Socket Designation: H0 L1 Cache
> 	Configuration: Enabled, Not Socketed, Level 1
> 	Operational Mode: Write Back
> 	Location: Internal
> 	Installed Size: 64 kB
> 	Maximum Size: 64 kB
> 	Supported SRAM Types:
> 		Burst
> 		Pipeline Burst
> 		Asynchronous
> 	Installed SRAM Type: Asynchronous
> 	Speed: Unknown
> 	Error Correction Type: Unknown
> 	System Type: Unknown
> 	Associativity: Unknown
> 
> Handle 0x0007, DMI type 7, 19 bytes
> Cache Information
> 	Socket Designation: H0 L2 Cache
> 	Configuration: Enabled, Not Socketed, Level 2
> 	Operational Mode: Write Through
> 	Location: Internal
> 	Installed Size: 2048 kB
> 	Maximum Size: 1024 kB
> 	Supported SRAM Types:
> 		Burst
> 		Pipeline Burst
> 		Synchronous
> 	Installed SRAM Type: Synchronous
> 	Speed: Unknown
> 	Error Correction Type: Unknown
> 	System Type: Unified
> 	Associativity: Unknown
> 
> Handle 0x0008, DMI type 7, 19 bytes
> Cache Information
> 	Socket Designation: H1 L1 Cache
> 	Configuration: Enabled, Not Socketed, Level 1
> 	Operational Mode: Write Back
> 	Location: Internal
> 	Installed Size: 64 kB
> 	Maximum Size: 64 kB
> 	Supported SRAM Types:
> 		Burst
> 		Pipeline Burst
> 		Asynchronous
> 	Installed SRAM Type: Asynchronous
> 	Speed: Unknown
> 	Error Correction Type: Unknown
> 	System Type: Unknown
> 	Associativity: Unknown
> 
> Handle 0x0009, DMI type 7, 19 bytes
> Cache Information
> 	Socket Designation: H1 L2 Cache
> 	Configuration: Enabled, Not Socketed, Level 2
> 	Operational Mode: Write Through
> 	Location: Internal
> 	Installed Size: 2048 kB
> 	Maximum Size: 1024 kB
> 	Supported SRAM Types:
> 		Burst
> 		Pipeline Burst
> 		Synchronous
> 	Installed SRAM Type: Synchronous
> 	Speed: Unknown
> 	Error Correction Type: Unknown
> 	System Type: Unified
> 	Associativity: Unknown
> 
> Handle 0x000A, DMI type 8, 9 bytes
> Port Connector Information
> 	Internal Reference Designator: J2
> 	Internal Connector Type: PS/2
> 	External Reference Designator: PS/2 Mouse
> 	External Connector Type: PS/2
> 	Port Type: Mouse Port
> 
> Handle 0x000B, DMI type 9, 13 bytes
> System Slot Information
> 	Designation: PCI Slot 1
> 	Type: 32-bit PCI
> 	Current Usage: Unknown
> 	Length: Long
> 	ID: 0
> 	Characteristics:
> 		3.3 V is provided
> 		PME signal is supported
> 
> Handle 0x000C, DMI type 11, 5 bytes
> OEM Strings
> 	String 1: 0
> 	String 2: 0
> 	String 3: .........................
> 
> Handle 0x000D, DMI type 16, 15 bytes
> Physical Memory Array
> 	Location: System Board Or Motherboard
> 	Use: System Memory
> 	Error Correction Type: Single-bit ECC
> 	Maximum Capacity: 32 GB
> 	Error Information Handle: Not Provided
> 	Number Of Devices: 8
> 
> Handle 0x000E, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 1
> 	Locator: C0_DIMM0
> 	Bank Locator: Bank 0
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x000F, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 2
> 	Locator: C0_DIMM1
> 	Bank Locator: Bank 1
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0010, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C0_DIMM2
> 	Bank Locator: Bank 2
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0011, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C0_DIMM3
> 	Bank Locator: Bank 3
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0012, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C0_DIMM0
> 	Bank Locator: Bank 0
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0013, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: 128 bits
> 	Data Width: 64 bits
> 	Size: 8192 MB
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C1_DIMM1
> 	Bank Locator: Bank 1
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0014, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: Unknown
> 	Data Width: Unknown
> 	Size: No Module Installed
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C1_DIMM2
> 	Bank Locator: Bank 2
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0015, DMI type 17, 27 bytes
> Memory Device
> 	Array Handle: 0x000D
> 	Error Information Handle: No Error
> 	Total Width: Unknown
> 	Data Width: Unknown
> 	Size: No Module Installed
> 	Form Factor: DIMM
> 	Set: 3
> 	Locator: C1_DIMM3
> 	Bank Locator: Bank 3
> 	Type: DRAM
> 	Type Detail: Synchronous
> 	Speed: Unknown
> 	Manufacturer: Not Specified
> 	Serial Number: Not Specified
> 	Asset Tag: Not Specified
> 	Part Number: Not Specified
> 
> Handle 0x0016, DMI type 19, 15 bytes
> Memory Array Mapped Address
> 	Starting Address: 0x00000000000
> 	Ending Address: 0x00BFFFFFFFF
> 	Range Size: 48 GB
> 	Physical Array Handle: 0x000D
> 	Partition Width: 2
> 
> Handle 0x0017, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00000000000
> 	Ending Address: 0x001FFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x000E
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x0018, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00200000000
> 	Ending Address: 0x003FFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x000F
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x0019, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00400000000
> 	Ending Address: 0x005FFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x0010
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001A, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00600000000
> 	Ending Address: 0x007FFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x0011
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001B, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00800000000
> 	Ending Address: 0x009FFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x0012
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001C, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00A00000000
> 	Ending Address: 0x00BFFFFFFFF
> 	Range Size: 8 GB
> 	Physical Device Handle: 0x0013
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001D, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00BFFFFFC00
> 	Ending Address: 0x00BFFFFFFFF
> 	Range Size: 1 kB
> 	Physical Device Handle: 0x0014
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001E, DMI type 20, 19 bytes
> Memory Device Mapped Address
> 	Starting Address: 0x00BFFFFFC00
> 	Ending Address: 0x00BFFFFFFFF
> 	Range Size: 1 kB
> 	Physical Device Handle: 0x0015
> 	Memory Array Mapped Address Handle: 0x0016
> 	Partition Row Position: 1
> 	Interleaved Data Depth: 1
> 
> Handle 0x001F, DMI type 32, 20 bytes
> System Boot Information
> 	Status: <OUT OF SPEC>
> 
> Handle 0x0020, DMI type 38, 18 bytes
> IPMI Device Information
> 	Interface Type: KCS (Keyboard Control Style)
> 	Specification Version: 2.0
> 	I2C Slave Address: 0x10
> 	NV Storage Device: Not Present
> 	Base Address: 0x0000000000000CA8 (I/O)
> 	Register Spacing: 32-bit Boundaries
> 	Interrupt Polarity: Active Low
> 	Interrupt Trigger Mode: Edge
> 
> Handle 0x0021, DMI type 127, 4 bytes
> End Of Table


-- 
Arkadiusz Miśkiewicz        PLD/Linux Team
arekm / maven.pl            http://ftp.pld-linux.org/

^ permalink raw reply

* Re: [patch net-next V2] net: introduce ethernet teaming device
From: Or Gerlitz @ 2011-10-26 14:31 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: netdev, fubar
In-Reply-To: <1319200747-2508-1-git-send-email-jpirko@redhat.com>

> [...] keep kernel code as slim and clean as possible and do what can be done in userspace
> in userspace. [...] Bonding is carring a lot of baggage, team does not. It's light and it suppose
> to stay that way.  Also I would add easy extensibility by writing new mode
> in case any existing is not suitable for your needs. I hope this clears it up for you.


Yes, this clears a lot, so it can be seen as major bonding design/face
lifting done through re-write, such that at the end of the day,
teaming could replace
bonding, makes sense?

Or.

^ permalink raw reply

* [PATCH 3/3] stmmac: update normal descriptor structure
From: Giuseppe CAVALLARO @ 2011-10-26 13:55 UTC (permalink / raw)
  To: netdev; +Cc: keguang.zhang, Giuseppe Cavallaro
In-Reply-To: <1319637339-14866-1-git-send-email-peppe.cavallaro@st.com>

This patch updates the normal descriptor structure
to work fine on new GMAC Synopsys chips.

Normal descriptors were designed on the old MAC10/100
databook 1.91 where some bits were reserved: for example
the tx checksum insertion and rx checksum offload.

The patch maintains the back-compatibility with old
MAC devices (tested on STx7109 MAC10/100) and adds new
fields that actually new GMAC devices can use.

For example, STx7109 (MAC10/100) will pass from the platform
  tx_coe = 0, enh_desc = 0, has_gmac = 0.
A platform like Loongson1B (GMAC) will pass:
  tx_coe = 1, enh_desc = 0, has_gmac = 1.

Thanks to Kelvin, he enhanced the normal descriptors for
GMAC (on MIPS Loongson1B platform).

Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/common.h       |    8 ++--
 drivers/net/ethernet/stmicro/stmmac/descs.h        |   31 +++++++++-------
 drivers/net/ethernet/stmicro/stmmac/norm_desc.c    |   38 +++++++++++---------
 .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c   |    8 ++--
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |    6 ++-
 5 files changed, 51 insertions(+), 40 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 9100c10..2cc1192 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -49,7 +49,7 @@ struct stmmac_extra_stats {
 	unsigned long tx_underflow ____cacheline_aligned;
 	unsigned long tx_carrier;
 	unsigned long tx_losscarrier;
-	unsigned long tx_heartbeat;
+	unsigned long vlan_tag;
 	unsigned long tx_deferred;
 	unsigned long tx_vlan;
 	unsigned long tx_jabber;
@@ -58,9 +58,9 @@ struct stmmac_extra_stats {
 	unsigned long tx_ip_header_error;
 	/* Receive errors */
 	unsigned long rx_desc;
-	unsigned long rx_partial;
-	unsigned long rx_runt;
-	unsigned long rx_toolong;
+	unsigned long sa_filter_fail;
+	unsigned long overflow_error;
+	unsigned long ipc_csum_error;
 	unsigned long rx_collision;
 	unsigned long rx_crc;
 	unsigned long rx_length;
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index 63a03e2..9820ec8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -25,33 +25,34 @@ struct dma_desc {
 	union {
 		struct {
 			/* RDES0 */
-			u32 reserved1:1;
+			u32 payload_csum_error:1;
 			u32 crc_error:1;
 			u32 dribbling:1;
 			u32 mii_error:1;
 			u32 receive_watchdog:1;
 			u32 frame_type:1;
 			u32 collision:1;
-			u32 frame_too_long:1;
+			u32 ipc_csum_error:1;
 			u32 last_descriptor:1;
 			u32 first_descriptor:1;
-			u32 multicast_frame:1;
-			u32 run_frame:1;
+			u32 vlan_tag:1;
+			u32 overflow_error:1;
 			u32 length_error:1;
-			u32 partial_frame_error:1;
+			u32 sa_filter_fail:1;
 			u32 descriptor_error:1;
 			u32 error_summary:1;
 			u32 frame_length:14;
-			u32 filtering_fail:1;
+			u32 da_filter_fail:1;
 			u32 own:1;
 			/* RDES1 */
 			u32 buffer1_size:11;
 			u32 buffer2_size:11;
-			u32 reserved2:2;
+			u32 reserved1:2;
 			u32 second_address_chained:1;
 			u32 end_ring:1;
-			u32 reserved3:5;
+			u32 reserved2:5;
 			u32 disable_ic:1;
+
 		} rx;
 		struct {
 			/* RDES0 */
@@ -91,24 +92,28 @@ struct dma_desc {
 			u32 underflow_error:1;
 			u32 excessive_deferral:1;
 			u32 collision_count:4;
-			u32 heartbeat_fail:1;
+			u32 vlan_frame:1;
 			u32 excessive_collisions:1;
 			u32 late_collision:1;
 			u32 no_carrier:1;
 			u32 loss_carrier:1;
-			u32 reserved1:3;
+			u32 payload_error:1;
+			u32 frame_flushed:1;
+			u32 jabber_timeout:1;
 			u32 error_summary:1;
-			u32 reserved2:15;
+			u32 ip_header_error:1;
+			u32 time_stamp_status:1;
+			u32 reserved1:13;
 			u32 own:1;
 			/* TDES1 */
 			u32 buffer1_size:11;
 			u32 buffer2_size:11;
-			u32 reserved3:1;
+			u32 time_stamp_enable:1;
 			u32 disable_padding:1;
 			u32 second_address_chained:1;
 			u32 end_ring:1;
 			u32 crc_disable:1;
-			u32 reserved4:2;
+			u32 checksum_insertion:2;
 			u32 first_segment:1;
 			u32 last_segment:1;
 			u32 interrupt:1;
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index f7e8ba7..fda5d2b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -50,11 +50,12 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
 			stats->collisions += p->des01.tx.collision_count;
 		ret = -1;
 	}
-	if (unlikely(p->des01.tx.heartbeat_fail)) {
-		x->tx_heartbeat++;
-		stats->tx_heartbeat_errors++;
-		ret = -1;
+
+	if (p->des01.etx.vlan_frame) {
+		CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
+		x->tx_vlan++;
 	}
+
 	if (unlikely(p->des01.tx.deferred))
 		x->tx_deferred++;
 
@@ -68,12 +69,12 @@ static int ndesc_get_tx_len(struct dma_desc *p)
 
 /* This function verifies if each incoming frame has some errors
  * and, if required, updates the multicast statistics.
- * In case of success, it returns csum_none because the device
- * is not able to compute the csum in HW. */
+ * In case of success, it returns good_frame because the GMAC device
+ * is supposed to be able to compute the csum in HW. */
 static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
 			       struct dma_desc *p)
 {
-	int ret = csum_none;
+	int ret = good_frame;
 	struct net_device_stats *stats = (struct net_device_stats *)data;
 
 	if (unlikely(p->des01.rx.last_descriptor == 0)) {
@@ -86,12 +87,12 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
 	if (unlikely(p->des01.rx.error_summary)) {
 		if (unlikely(p->des01.rx.descriptor_error))
 			x->rx_desc++;
-		if (unlikely(p->des01.rx.partial_frame_error))
-			x->rx_partial++;
-		if (unlikely(p->des01.rx.run_frame))
-			x->rx_runt++;
-		if (unlikely(p->des01.rx.frame_too_long))
-			x->rx_toolong++;
+		if (unlikely(p->des01.rx.sa_filter_fail))
+			x->sa_filter_fail++;
+		if (unlikely(p->des01.rx.overflow_error))
+			x->overflow_error++;
+		if (unlikely(p->des01.rx.ipc_csum_error))
+			x->ipc_csum_error++;
 		if (unlikely(p->des01.rx.collision)) {
 			x->rx_collision++;
 			stats->collisions++;
@@ -113,10 +114,10 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
 		x->rx_mii++;
 		ret = discard_frame;
 	}
-	if (p->des01.rx.multicast_frame) {
-		x->rx_multicast++;
-		stats->multicast++;
-	}
+#ifdef STMMAC_VLAN_TAG_USED
+	if (p->des01.rx.vlan_tag)
+		x->vlan_tag++;
+#endif
 	return ret;
 }
 
@@ -184,6 +185,9 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
 {
 	p->des01.tx.first_segment = is_fs;
 	norm_set_tx_desc_len(p, len);
+
+	if (likely(csum_flag))
+		p->des01.tx.checksum_insertion = cic_full;
 }
 
 static void ndesc_clear_tx_ic(struct dma_desc *p)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 406404f..e8eff09 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -50,7 +50,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
 	STMMAC_STAT(tx_underflow),
 	STMMAC_STAT(tx_carrier),
 	STMMAC_STAT(tx_losscarrier),
-	STMMAC_STAT(tx_heartbeat),
+	STMMAC_STAT(vlan_tag),
 	STMMAC_STAT(tx_deferred),
 	STMMAC_STAT(tx_vlan),
 	STMMAC_STAT(rx_vlan),
@@ -59,9 +59,9 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
 	STMMAC_STAT(tx_payload_error),
 	STMMAC_STAT(tx_ip_header_error),
 	STMMAC_STAT(rx_desc),
-	STMMAC_STAT(rx_partial),
-	STMMAC_STAT(rx_runt),
-	STMMAC_STAT(rx_toolong),
+	STMMAC_STAT(sa_filter_fail),
+	STMMAC_STAT(overflow_error),
+	STMMAC_STAT(ipc_csum_error),
 	STMMAC_STAT(rx_collision),
 	STMMAC_STAT(rx_crc),
 	STMMAC_STAT(rx_length),
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f77eaa6..451aa60 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -813,6 +813,7 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
 static int stmmac_get_hw_features(struct stmmac_priv *priv)
 {
 	u32 hw_cap = 0;
+
 	if (priv->hw->dma->get_hw_feature) {
 		hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
 
@@ -938,6 +939,7 @@ static int stmmac_open(struct net_device *dev)
 
 	stmmac_get_hw_features(priv);
 
+	priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr);
 	if (priv->rx_coe)
 		pr_info("stmmac: Rx Checksum Offload Engine supported\n");
 	if (priv->plat->tx_coe)
@@ -1275,8 +1277,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
 #endif
 			skb->protocol = eth_type_trans(skb, priv->dev);
 
-			if (unlikely(status == csum_none)) {
-				/* always for the old mac 10/100 */
+			if (unlikely(!priv->rx_coe)) {
+				/* No RX COE for old mac10/100 devices */
 				skb_checksum_none_assert(skb);
 				netif_receive_skb(skb);
 			} else {
-- 
1.7.4.4

^ permalink raw reply related

* [PATCH 2/3] stmmac: fix NULL pointer dereference in capabilities fixup
From: Giuseppe CAVALLARO @ 2011-10-26 13:55 UTC (permalink / raw)
  To: netdev; +Cc: keguang.zhang, Angus Clark
In-Reply-To: <1319637339-14866-1-git-send-email-peppe.cavallaro@st.com>

From: Angus Clark <angus.clark@st.com>

Signed-off-by: Angus Clark <angus.clark@st.com>
Acked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index fcdd5a2..f77eaa6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -325,7 +325,7 @@ static int stmmac_init_phy(struct net_device *dev)
 	    (interface == PHY_INTERFACE_MODE_RMII))) {
 		phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
 				      SUPPORTED_Asym_Pause);
-		priv->phydev->advertising = priv->phydev->supported;
+		priv->phydev->advertising = phydev->supported;
 	}
 
 	/*
-- 
1.7.4.4

^ permalink raw reply related

* [PATCH 1/3] stmmac: fix a bug while checking the HW cap reg
From: Giuseppe CAVALLARO @ 2011-10-26 13:55 UTC (permalink / raw)
  To: netdev; +Cc: keguang.zhang, Giuseppe Cavallaro

The patch fixes a bug while checking the HW cap reg
on old MAC10/100 where this feature is not available.

Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index aeaa15b..fcdd5a2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -812,9 +812,10 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
  */
 static int stmmac_get_hw_features(struct stmmac_priv *priv)
 {
-	u32 hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
+	u32 hw_cap = 0;
+	if (priv->hw->dma->get_hw_feature) {
+		hw_cap = priv->hw->dma->get_hw_feature(priv->ioaddr);
 
-	if (likely(hw_cap)) {
 		priv->dma_cap.mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL);
 		priv->dma_cap.mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1;
 		priv->dma_cap.half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2;
-- 
1.7.4.4

^ permalink raw reply related

* Re: [PATCH 2/2 v2] net/smsc911x: Add regulator support
From: Mark Brown @ 2011-10-26 13:36 UTC (permalink / raw)
  To: Linus Walleij
  Cc: netdev, Steve Glendinning, Mathieu Poirer, Robert Marklund,
	Linus Walleij
In-Reply-To: <1319627104-7779-1-git-send-email-linus.walleij@stericsson.com>

On Wed, Oct 26, 2011 at 01:05:04PM +0200, Linus Walleij wrote:
> From: Robert Marklund <robert.marklund@stericsson.com>
> 
> Add some basic regulator support for the power pins, as needed
> by the ST-Ericsson Snowball platform that powers up the SMSC911
> chip using an external regulator.

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

but it does look like the code could be simplified with the
regulator_bulk API - it'd mean you wouldn't need to do per regulator
API calls and would do all your unwinding if the second operation fails.

^ permalink raw reply

* [PATCH] ipv6: fix route lookup in addrconf_prefix_rcv()
From: Andreas Hofmeister @ 2011-10-26 13:24 UTC (permalink / raw)
  To: netdev

The route lookup to find a previously auto-configured route for a prefixes used
to use rt6_lookup(), with the prefix from the RA used as an address. However,
that kind of lookup ignores routing tables, the prefix length and route flags,
so when there were other matching routes, even in different tables and/or with
a different prefix length, the wrong route would be manipulated.

Now, a new function "addrconf_get_prefix_route()" is used for the route lookup,
which searches in RT6_TABLE_PREFIX and takes the prefix-length and route flags
into account.

Signed-off-by: Andreas Hofmeister <andi@collax.com>
---
 net/ipv6/addrconf.c |   43 ++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 12368c5..4193cbb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1713,6 +1713,40 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
 	ip6_route_add(&cfg);
 }
 
+
+static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
+						  int plen,
+						  const struct net_device *dev,
+						  u32 flags, u32 noflags)
+{
+	struct fib6_node *fn;
+	struct rt6_info *rt = NULL;
+	struct fib6_table *table;
+
+	table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX);
+	if (table == NULL)
+		return NULL;
+
+	write_lock_bh(&table->tb6_lock);
+	fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0);
+	if (!fn)
+		goto out;
+	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+		if (rt->rt6i_dev->ifindex != dev->ifindex)
+			continue;
+		if ((rt->rt6i_flags & flags) != flags)
+			continue;
+		if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0))
+			continue;
+		dst_hold(&rt->dst);
+		break;
+	}
+out:
+	write_unlock_bh(&table->tb6_lock);
+	return rt;
+}
+
+
 /* Create "default" multicast route to the interface */
 
 static void addrconf_add_mroute(struct net_device *dev)
@@ -1842,10 +1876,13 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
 		if (addrconf_finite_timeout(rt_expires))
 			rt_expires *= HZ;
 
-		rt = rt6_lookup(net, &pinfo->prefix, NULL,
-				dev->ifindex, 1);
+		rt = addrconf_get_prefix_route(&pinfo->prefix,
+					       pinfo->prefix_len,
+					       dev,
+					       RTF_ADDRCONF | RTF_PREFIX_RT,
+					       RTF_GATEWAY | RTF_DEFAULT);
 
-		if (rt && addrconf_is_prefix_route(rt)) {
+		if (rt) {
 			/* Autoconf prefix route */
 			if (valid_lft == 0) {
 				ip6_del_rt(rt);
-- 
1.7.6.1

^ permalink raw reply related

* [linux-firmware v6 4/4] rtl_nic: add new firmware for RTL8402
From: Hayes Wang @ 2011-10-26 13:07 UTC (permalink / raw)
  To: dwmw2, ben; +Cc: romieu, netdev, Hayes Wang
In-Reply-To: <1319634425-1529-1-git-send-email-hayeswang@realtek.com>

Add new firmware:
1. rtl_nic/rtl8402-1.fw
   version: 0.0.1

Signed-off-by: Hayes Wang <hayeswang@realtek.com>
---
 WHENCE               |    3 +++
 rtl_nic/rtl8402-1.fw |  Bin 0 -> 1824 bytes
 2 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 rtl_nic/rtl8402-1.fw

diff --git a/WHENCE b/WHENCE
index 3d4dc0a..555008e 100644
--- a/WHENCE
+++ b/WHENCE
@@ -1673,6 +1673,9 @@ Version: 0.0.3
 File: rtl_nic/rtl8411-1.fw
 Version: 0.0.1
 
+File: rtl_nic/rtl8402-1.fw
+Version: 0.0.1
+
 Licence:
  * Copyright © 2011, Realtek Semiconductor Corporation
  *
diff --git a/rtl_nic/rtl8402-1.fw b/rtl_nic/rtl8402-1.fw
new file mode 100644
index 0000000000000000000000000000000000000000..82fa35d70994dc679a481dc946d8985cd2f7bb3e
GIT binary patch
literal 1824
zcmZ{lU5J!r6vv<WSa+gg*9+0Y<IcrGYoWd~*cx7qq_P+G0SOrfGv$VwTP*9c6j+_i
zAV`hG5+P~I(Tj|X%pw!qyPyx$$Uy93y6Yk;v5O#S`u*p9Uv_w92hN=5eV%j9|9?K7
z_YEP0sp*LaR!7wpm3p)?T3IPqqE*$=Rh0@y-Cu+M)434Nh3B_TPt+&3?VjBITwN~p
zXic=bK0dW`=9#H-HCkI8U3-5u9B2*8uTOu)FyxZS$-6u(SnA=3d3XugndlwnN5{;E
z_VBzZ);V-=^Y$z8UVja}9_g_ge2nJ?g8%4tbcki>ar1xc=7o>Uw`?_E7MYKuZ=N*2
z_KW!<>=u1xzVNL1uFK{FJP&-%`jYvbtQUM{o)ecX=0)iz&F?0L_HxNykv+wI5xI^W
zt;Ad#@GiioJU+*#a2mXZc@4Y8=r;WBAkKc|I6TGso}Ro^he74P&pcvX#^*xjT=lv5
zvw0rh|2)Qa_R6vsw~T$BGu5l}s`)v1bnE(%c{O7v+!8TV*Y8{AXRZ>z6Wf=u4=*E6
zo8O+qnAIK}B*!i#e5j@3J7=n2kyygc?|@rRpHiHO3<u>_cxMhv{rtM8pNGI!KTrK&
z{sP<%pl8s#;5v<N!sDv+cf^6$UvB<BJ|83E)h2#6?^2R?39jKaxH2E=?Nf<QQ+f9E
zw5c9GG1tEqSVgeJMLoF*rm&jI<qPts4uiy2Z-+Kh*SdKh^XaNJbD%l>7OeDH`3-04
zQY2Q`JO8W*LwytOEVioId@<Uxg-35cy1jjNexL3+Jyb1)5gs;A?`8Kca@Knf2dGg8
z4!Vb1$uV$8wxbJ&StoOJoO-RGUazC?L#v0&(aYb(CKGR%J>izYExsAgY3Whool0hw
zbq=4Io*CP7&HRrJv55IAzBPQ;jF~T4Z~o1?IG*-uNj=mb&0nhf-=c><ns36t?~wU}
zO?Z-r=3n<-F&p^5N6e9paXdYbke{B@xY_r~Z(yewss0_Tb-a7-1!H6KK4}sw52rkQ
z$B@I|r1sJUxTZbsXZ<H}JILFKo%Uq67_IloKL_KyFN1Z3d<H&X=6<4n<mXq+zob_A
z51D)LyLT5%t@mMbgEQ&tXyvY6wBeWL_y)EY!A{R+IlGbJdmB7T8)6Qh!eI%u{e*Ze
z^fC0_ByYzzy`yPA7UDYshVW{eZ+#!Dre|8_N0J!jM{b>=>`b1(Rx|nc`M9V0PKEqZ
KkMyn5n&1B#>OCI-

literal 0
HcmV?d00001

-- 
1.7.6.2

^ permalink raw reply related

* [linux-firmware v6 3/4] rtl_nic: add new firmware for RTL8411
From: Hayes Wang @ 2011-10-26 13:07 UTC (permalink / raw)
  To: dwmw2, ben; +Cc: romieu, netdev, Hayes Wang
In-Reply-To: <1319634425-1529-1-git-send-email-hayeswang@realtek.com>

Add new firmware:
1. rtl_nic/rtl8411-1.fw
   version: 0.0.1

Signed-off-by: Hayes Wang <hayeswang@realtek.com>
---
 WHENCE               |    3 +++
 rtl_nic/rtl8411-1.fw |  Bin 0 -> 1584 bytes
 2 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 rtl_nic/rtl8411-1.fw

diff --git a/WHENCE b/WHENCE
index a9447ac..3d4dc0a 100644
--- a/WHENCE
+++ b/WHENCE
@@ -1670,6 +1670,9 @@ Version: 0.0.3
 File: rtl_nic/rtl8168f-2.fw
 Version: 0.0.3
 
+File: rtl_nic/rtl8411-1.fw
+Version: 0.0.1
+
 Licence:
  * Copyright © 2011, Realtek Semiconductor Corporation
  *
diff --git a/rtl_nic/rtl8411-1.fw b/rtl_nic/rtl8411-1.fw
new file mode 100644
index 0000000000000000000000000000000000000000..d9aaad151004e8853560918509f91d2329ec8f07
GIT binary patch
literal 1584
zcmZ9MPiS0q5XUE*>_my8ho;R+Oke)Asn%xqt+6)iWs#y4M2g_05erfeg+fJyAoef~
zRBYCUQl+Q|$-#@z?4fwF;6^C*CZ_bzVxn(rs>PP%64E3jSwFMyEnQvs?aXg}-<kQ&
z%<ug~L_YZN#DU>3+!r3r4dsTyAk1a+`?F!lQT=zA|FIU4ti1X9{(SDh(R?lpb0gt!
zkk1wJ!-eN_a_-v!hn8w0J59R8L^`FmBmr_p4vDp6&q@z=d7n^`NVkImlho8Pk@F(y
zT*KF~{r|ys2|j^<-Iid}U{-2rGtqi%$7}0$YiE5aoz`(GV_&&Ek+FM;Ty=ReV_V24
zkiQ`xMb05N%Nd(b&VP4#En|Nm*Ck^SvYN|4x3iu@$l+X@If%g}rS{;Ef=AZWY)Y++
zEw!T7)1ubm`d+m$OLVaBHtZj%eSy6csZ~E!J6i(N!@i2|1U7J`X4_}6?Od~MQf&&{
z&!Tpvtakc_+QJdF3t&oj$o;w6ks<Vo+I@0lz?HsJTRX2-TT~Nxy4-kxKC3nkE;X#y
zTYyVr|6;sfZNnF?({S4spu0bMK#dXkb}x_{?u&!5ey3k;5L?>@=c{UyV1jA2m8Zbr
z@B6*pA8J1JoLZ~vGstnZA=gi;eN4_Qdh~US?{)tU{Cng5X~%6>yVad=n^b$GPpyx-
zO5{$lUw)zK3{L$`XLrMgXXyd8f#fX0&-v~0d};FSiud*UJ;!cpY>G3nuPF}o4>p~B
z(i-<*ogN%tq855s75a-DBz}}vD|&C3fpe}D)qcV@LT%H_^nOp`6QJYcJ&d{%pCr#B
zJ}(g?;tozQlL_Xqz+S*d+=C_V$kxS}#~M6-S;xj&KMoJ>%=d4RcT4RZ`sjHtk+*}q
z-F_cN>EVdlo=e>A*T5Z98*Rdkf|Fmld+v8vEzNBD9Lv7)hT2s)ocU4h`hB&VZOkge
z_sh!6vF5q=?Ql6uZsOL^-+Nqb^=-ZZQJZ^3Z654p){Wb6#n#~Dcl7Bp^%1*rmHT~$
z`mPi6ChHcy#ByKHwR{<l)LFsbxfkHJ1<#Aj;~I5uveFxBx7XDEfp7FD-_1R>$B5Cx
z{Qe3O``?^l-*5~)-rR&MHF+;1dTI3BXIkk~`{C8Nml3@@h`nW}TIV;+n0ZVegx521
zPiOHjGoM-S7yJt2F~24FHD>1bWvr-HBxiJmTAo#VxtiupQhz6(M9vidw{N^xlDxk)
fUTPfb?{DKxefaL~kW@X+j+y$K%v&cH94_)N&kqV8

literal 0
HcmV?d00001

-- 
1.7.6.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox