Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next] tcp: md5: add more const attributes
From: Eric Dumazet @ 2011-10-24  6:35 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

Now tcp_md5_hash_header() has a const tcphdr argument, we can add more
const attributes to callers.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 include/net/tcp.h   |   17 +++++++++--------
 net/ipv4/tcp_ipv4.c |   12 ++++++------
 net/ipv6/tcp_ipv6.c |   13 +++++++------
 3 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 910cc29..ed0e814 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1185,8 +1185,9 @@ struct tcp_md5sig_pool {
 
 /* - functions */
 extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
-			       struct sock *sk, struct request_sock *req,
-			       struct sk_buff *skb);
+			       const struct sock *sk,
+			       const struct request_sock *req,
+			       const struct sk_buff *skb);
 extern struct tcp_md5sig_key * tcp_v4_md5_lookup(struct sock *sk,
 						 struct sock *addr_sk);
 extern int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, u8 *newkey,
@@ -1448,9 +1449,9 @@ struct tcp_sock_af_ops {
 						struct sock *addr_sk);
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
-						  struct sock *sk,
-						  struct request_sock *req,
-						  struct sk_buff *skb);
+						  const struct sock *sk,
+						  const struct request_sock *req,
+						  const struct sk_buff *skb);
 	int			(*md5_add) (struct sock *sk,
 					    struct sock *addr_sk,
 					    u8 *newkey,
@@ -1467,9 +1468,9 @@ struct tcp_request_sock_ops {
 						struct request_sock *req);
 	int			(*calc_md5_hash) (char *location,
 						  struct tcp_md5sig_key *md5,
-						  struct sock *sk,
-						  struct request_sock *req,
-						  struct sk_buff *skb);
+						  const struct sock *sk,
+						  const struct request_sock *req,
+						  const struct sk_buff *skb);
 #endif
 };
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 955c925..1dad7e9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -92,7 +92,7 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
 static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
 						   __be32 addr);
 static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
-			       __be32 daddr, __be32 saddr, struct tcphdr *th);
+			       __be32 daddr, __be32 saddr, const struct tcphdr *th);
 #else
 static inline
 struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
@@ -1090,7 +1090,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
 }
 
 static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
-			       __be32 daddr, __be32 saddr, struct tcphdr *th)
+			       __be32 daddr, __be32 saddr, const struct tcphdr *th)
 {
 	struct tcp_md5sig_pool *hp;
 	struct hash_desc *desc;
@@ -1122,12 +1122,12 @@ clear_hash_noput:
 }
 
 int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
-			struct sock *sk, struct request_sock *req,
-			struct sk_buff *skb)
+			const struct sock *sk, const struct request_sock *req,
+			const struct sk_buff *skb)
 {
 	struct tcp_md5sig_pool *hp;
 	struct hash_desc *desc;
-	struct tcphdr *th = tcp_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
 	__be32 saddr, daddr;
 
 	if (sk) {
@@ -1172,7 +1172,7 @@ clear_hash_noput:
 }
 EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
 
-static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
+static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
 {
 	/*
 	 * This gets called for each TCP segment that arrives
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index da2ada8..c8683fc 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -761,7 +761,7 @@ static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
 
 static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
 			       const struct in6_addr *daddr, struct in6_addr *saddr,
-			       struct tcphdr *th)
+			       const struct tcphdr *th)
 {
 	struct tcp_md5sig_pool *hp;
 	struct hash_desc *desc;
@@ -793,13 +793,14 @@ clear_hash_noput:
 }
 
 static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
-			       struct sock *sk, struct request_sock *req,
-			       struct sk_buff *skb)
+			       const struct sock *sk,
+			       const struct request_sock *req,
+			       const struct sk_buff *skb)
 {
 	const struct in6_addr *saddr, *daddr;
 	struct tcp_md5sig_pool *hp;
 	struct hash_desc *desc;
-	struct tcphdr *th = tcp_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
 
 	if (sk) {
 		saddr = &inet6_sk(sk)->saddr;
@@ -842,12 +843,12 @@ clear_hash_noput:
 	return 1;
 }
 
-static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
+static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
 {
 	const __u8 *hash_location = NULL;
 	struct tcp_md5sig_key *hash_expected;
 	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
-	struct tcphdr *th = tcp_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
 	int genhash;
 	u8 newhash[16];
 

^ permalink raw reply related

* Re: [PATCH net-next] tcp: md5: add more const attributes
From: David Miller @ 2011-10-24  6:51 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1319438111.2517.32.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 24 Oct 2011 08:35:11 +0200

> Now tcp_md5_hash_header() has a const tcphdr argument, we can add more
> const attributes to callers.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply

* Re: [patch net-next V2] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-24  6:54 UTC (permalink / raw)
  To: Or Gerlitz
  Cc: netdev, davem, eric.dumazet, bhutchings, shemminger, fubar, andy,
	tgraf, ebiederm, mirqus, kaber, greearb, jesse, fbl,
	benjamin.poirier, jzupka
In-Reply-To: <CAJZOPZ+80kZ8-dQLJJ3OYr+fxVw=z=VCQzui2aaif1ga7W-K_g@mail.gmail.com>

Sun, Oct 23, 2011 at 11:46:32PM CEST, or.gerlitz@gmail.com wrote:
>On Fri, Oct 21, 2011 at 2:39 PM, Jiri Pirko <jpirko@redhat.com> wrote:
>> This patch introduces new network device called team. It supposes to be
>> very fast, simple, userspace-driven alternative to existing bonding driver.
>
>Jiri,
>
>Could you elaborate a little further on the motivation for this
>teaming approach/solution vs. the current bonding driver? You say that
>it suppose to be very fast, simple  and user space driven, so... do
>you find bonding not to be fast enough? or too complex? or the fact
>that bonding's driving logic being in the kernel is something you
>prefer to see in user-space? anything else?

As I wrote before:

<quote>
Well as I already wrote, the key is to keep kernel code as slim and
clean as possible and do what can be done in userspace in userspace. For
example bonding has active backup slave selection, arp verification and
so on in kernel. That's not conceptional and flexible. Team allows users
to easily write/modify their own control logic based on whatever way
they want to decide to select active port. I can imagine for example
even things like communicating with smart switch and checking port
status there. Bonding is carring a lot of baggage, team does not. It's
light and it suppose to stay that way.
</quote>

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.

Jirka


>
>thanks,
>
>Or.
>
>
>>
>> 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>
>>
>> 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                  |   38 +
>>  drivers/net/team/Makefile                 |    7 +
>>  drivers/net/team/team.c                   | 1593 +++++++++++++++++++++++++++++
>>  drivers/net/team/team_mode_activebackup.c |  152 +++
>>  drivers/net/team/team_mode_roundrobin.c   |  107 ++
>>  include/linux/Kbuild                      |    1 +
>>  include/linux/if.h                        |    1 +
>>  include/linux/if_team.h                   |  233 +++++
>>  12 files changed, 2144 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 5008b08..c33400d 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:     Erik Gilling <konkers@android.com>
>> 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..70a43a6
>> --- /dev/null
>> +++ b/drivers/net/team/Kconfig
>> @@ -0,0 +1,38 @@
>> +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.
>> +
>> +         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.
>> +
>> +         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..398be58
>> --- /dev/null
>> +++ b/drivers/net/team/team.c
>> @@ -0,0 +1,1593 @@
>> +/*
>> + * 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);
>> +}
>> +EXPORT_SYMBOL(team_port_set_orig_mac);
>> +
>> +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.
>> + */
>> +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));
>> +
>> +               synchronize_rcu();
>> +
>> +               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;
>> +}
>> +
>> +static int team_port_list_init(struct team *team)
>> +{
>> +       int i;
>> +       struct hlist_head *hash;
>> +
>> +       hash = kmalloc(sizeof(*hash) * TEAM_PORT_HASHENTRIES, GFP_KERNEL);
>> +       if (!hash)
>> +               return -ENOMEM;
>> +
>> +       for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
>> +               INIT_HLIST_HEAD(&hash[i]);
>> +       team->port_hlist = hash;
>> +       INIT_LIST_HEAD(&team->port_list);
>> +       return 0;
>> +}
>> +
>> +static void team_port_list_fini(struct team *team)
>> +{
>> +       kfree(team->port_hlist);
>> +}
>> +
>> +/*
>> + * 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_rcu(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);
>> +       }
>> +       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;
>> +       char tmp_addr[ETH_ALEN];
>> +       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);
>> +       random_ether_addr(tmp_addr);
>> +       err = __set_port_mac(port_dev, tmp_addr);
>> +       if (err) {
>> +               netdev_dbg(dev, "Device %s mac addr set failed\n",
>> +                          portname);
>> +               goto err_set_mac_rand;
>> +       }
>> +
>> +       err = dev_open(port_dev);
>> +       if (err) {
>> +               netdev_dbg(dev, "Device %s opening failed\n",
>> +                          portname);
>> +               goto err_dev_open;
>> +       }
>> +
>> +       err = team_port_set_orig_mac(port);
>> +       if (err) {
>> +               netdev_dbg(dev, "Device %s mac addr set failed - Device does not support addr change when it's opened\n",
>> +                          portname);
>> +               goto err_set_mac_opened;
>> +       }
>> +
>> +       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 = 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:
>> +       team_port_leave(team, port);
>> +
>> +err_port_enter:
>> +err_set_mac_opened:
>> +       dev_close(port_dev);
>> +
>> +err_dev_open:
>> +       team_port_set_orig_mac(port);
>> +
>> +err_set_mac_rand:
>> +       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);
>> +       team_port_leave(team, port);
>> +       dev_close(port_dev);
>> +       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 err;
>> +
>> +       team->dev = dev;
>> +       spin_lock_init(&team->lock);
>> +
>> +       team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
>> +       if (!team->pcpu_stats)
>> +               return -ENOMEM;
>> +
>> +       err = team_port_list_init(team);
>> +       if (err)
>> +               goto err_port_list_init;
>> +
>> +       INIT_LIST_HEAD(&team->option_list);
>> +       team_options_register(team, team_options, ARRAY_SIZE(team_options));
>> +       netif_carrier_off(dev);
>> +
>> +       return 0;
>> +
>> +err_port_list_init:
>> +
>> +       free_percpu(team->pcpu_stats);
>> +
>> +       return err;
>> +}
>> +
>> +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);
>> +
>> +       team_port_list_fini(team);
>> +       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;
>> +
>> +       rcu_read_lock();
>> +       list_for_each_entry_rcu(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;
>> +               }
>> +       }
>> +       rcu_read_unlock();
>> +
>> +       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);
>> +
>> +       rcu_read_unlock();
>> +       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_rcu(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_CHANGEADDR:
>> +               /* Forbid to change addr 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_rtln_reg;
>> +
>> +       err = team_nl_init();
>> +       if (err)
>> +               goto err_nl_init;
>> +
>> +       return 0;
>> +
>> +err_nl_init:
>> +       rtnl_link_unregister(&team_link_ops);
>> +
>> +err_rtln_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..1aa2bfb
>> --- /dev/null
>> +++ b/drivers/net/team/team_mode_activebackup.c
>> @@ -0,0 +1,152 @@
>> +/*
>> + * 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 void ab_port_change_mac(struct team *team, struct team_port *port)
>> +{
>> +       if (ab_priv(team)->active_port == port)
>> +               team_port_set_team_mac(port);
>> +}
>> +
>> +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) {
>> +                       struct team_port *ac_port = ab_priv(team)->active_port;
>> +
>> +                       /* rtnl_lock needs to be held when setting macs */
>> +                       rtnl_lock();
>> +                       if (ac_port)
>> +                               team_port_set_orig_mac(ac_port);
>> +                       rcu_assign_pointer(ab_priv(team)->active_port, port);
>> +                       team_port_set_team_mac(port);
>> +                       rtnl_unlock();
>> +                       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,
>> +       .port_change_mac        = ab_port_change_mac,
>> +};
>> +
>> +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..21581a7
>> --- /dev/null
>> +++ b/include/linux/if_team.h
>> @@ -0,0 +1,233 @@
>> +/*
>> + * 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_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;
>> +       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];
>> +};
>> +
>> +#define TEAM_PORT_HASHBITS 4
>> +#define TEAM_PORT_HASHENTRIES (1 << TEAM_PORT_HASHBITS)
>> +
>> +static inline struct hlist_head *
>> +team_port_index_hash(const 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_rcu(const 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_orig_mac(struct team_port *port);
>> +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
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>

^ permalink raw reply

* Re: [PATCH v2 1/3] net: hold sock reference while processing tx timestamps
From: David Miller @ 2011-10-24  6:55 UTC (permalink / raw)
  To: eric.dumazet; +Cc: richardcochran, netdev, johannes, stable
In-Reply-To: <1319196710.2338.26.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 21 Oct 2011 13:31:50 +0200

> Le vendredi 21 octobre 2011 à 12:49 +0200, Richard Cochran a écrit :
>> The pair of functions,
>> 
>>  * skb_clone_tx_timestamp()
>>  * skb_complete_tx_timestamp()
>> 
>> were designed to allow timestamping in PHY devices. The first
>> function, called during the MAC driver's hard_xmit method, identifies
>> PTP protocol packets, clones them, and gives them to the PHY device
>> driver. The PHY driver may hold onto the packet and deliver it at a
>> later time using the second function, which adds the packet to the
>> socket's error queue.
>> 
>> As pointed out by Johannes, nothing prevents the socket from
>> disappearing while the cloned packet is sitting in the PHY driver
>> awaiting a timestamp. This patch fixes the issue by taking a reference
>> on the socket for each such packet. In addition, the comments
>> regarding the usage of these function are expanded to highlight the
>> rule that PHY drivers must use skb_complete_tx_timestamp() to release
>> the packet, in order to release the socket reference, too.
>> 
>> These functions first appeared in v2.6.36.
>> 
>> Reported-by: Johannes Berg <johannes@sipsolutions.net>
>> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
>> Cc: <stable@vger.kernel.org>
>> ---
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH net-next v3] route: fix ICMP redirect validation
From: David Miller @ 2011-10-24  6:56 UTC (permalink / raw)
  To: fbl; +Cc: netdev
In-Reply-To: <1319215448-6777-1-git-send-email-fbl@redhat.com>

From: Flavio Leitner <fbl@redhat.com>
Date: Fri, 21 Oct 2011 14:44:08 -0200

> The commit f39925dbde7788cfb96419c0f092b086aa325c0f
> (ipv4: Cache learned redirect information in inetpeer.)
> removed some ICMP packet validations which are required by
> RFC 1122, section 3.2.2.2:
> ...
>   A Redirect message SHOULD be silently discarded if the new
>   gateway address it specifies is not on the same connected
>   (sub-) net through which the Redirect arrived [INTRO:2,
>   Appendix A], or if the source of the Redirect is not the
>   current first-hop gateway for the specified destination (see
>   Section 3.3.1).
> 
> Signed-off-by: Flavio Leitner <fbl@redhat.com>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH v2 2/3] dp83640: use proper function to free transmit time stamping packets
From: David Miller @ 2011-10-24  6:55 UTC (permalink / raw)
  To: richardcochran; +Cc: netdev, eric.dumazet, johannes, stable
In-Reply-To: <966b346f5f73c0afc48f89686e9ed1f44457074b.1319193734.git.richard.cochran@omicron.at>

From: Richard Cochran <richardcochran@gmail.com>
Date: Fri, 21 Oct 2011 12:49:16 +0200

> The previous commit enforces a new rule for handling the cloned packets
> for transmit time stamping. These packets must not be freed using any other
> function than skb_complete_tx_timestamp. This commit fixes the one and only
> driver using this API.
> 
> The driver first appeared in v3.0.
> 
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> Cc: <stable@vger.kernel.org>

In the 'net' tree, which is where you should be targetting these dp83640
driver patches, the code looks nothing like what you're patching against.

Please respin patches #2 and #3 against current sources.

^ permalink raw reply

* Re: [PATCH] jme: fix irq storm after suspend/resume
From: David Miller @ 2011-10-24  6:58 UTC (permalink / raw)
  To: grawity; +Cc: linux-kernel, netdev
In-Reply-To: <j7uqst$kpc$1@dough.gmane.org>

From: "Mantas M." <grawity@gmail.com>
Date: Sat, 22 Oct 2011 19:27:09 +0300

> On 22/10/11 15:56, Clemens Buchacher wrote:
>> If the device is down during suspend/resume, interrupts are enabled
>> without a registered interrupt handler, causing a storm of
>> unhandled interrupts until the IRQ is disabled because "nobody
>> cared".
>>
>> Instead, check that the device is up before touching it in the
>> suspend/resume code.
>>
>> Fixes https://bugzilla.kernel.org/show_bug.cgi?id=39112
> 
> Just tested the patch, suspend/resume works well.

Applied, thanks.

^ permalink raw reply

* Re: [PATCH] ipv4: fix ipsec forward performance regression
From: David Miller @ 2011-10-24  7:01 UTC (permalink / raw)
  To: zheng.z.yan; +Cc: ja, netdev, eric.dumazet, kim.phillips
In-Reply-To: <4EA4B451.7010708@intel.com>

From: "Yan, Zheng" <zheng.z.yan@intel.com>
Date: Mon, 24 Oct 2011 08:41:53 +0800

> My understanding is that xfrm_dst(s) are managed by the flow cache
> (net/core/flow.c). We don't put them into the routing cache.

This is correct.

^ permalink raw reply

* Re: [PATCH] ipv4: fix ipsec forward performance regression
From: David Miller @ 2011-10-24  7:02 UTC (permalink / raw)
  To: eric.dumazet; +Cc: zheng.z.yan, netdev, kim.phillips
In-Reply-To: <1319360590.6180.75.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Sun, 23 Oct 2011 11:03:10 +0200

> Le dimanche 23 octobre 2011 à 15:58 +0800, Yan, Zheng a écrit :
>> There is bug in commit 5e2b61f(ipv4: Remove flowi from struct rtable).
>> It makes xfrm4_fill_dst() modify wrong data structure.
>> 
>> Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
 ...
> Reported-by: Kim Phillips <kim.phillips@freescale.com>
> 
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied, thanks everyone.

^ permalink raw reply

* Re: [PATCH] rtnetlink: Add missing manual netlink notification in dev_change_net_namespaces
From: David Miller @ 2011-10-24  7:03 UTC (permalink / raw)
  To: ebiederm; +Cc: netdev, kaber, equinox, renatowestphal
In-Reply-To: <m1ty72l3d7.fsf_-_@fess.ebiederm.org>

From: ebiederm@xmission.com (Eric W. Biederman)
Date: Fri, 21 Oct 2011 09:24:20 -0700

> Renato Westphal noticed that since commit a2835763e130c343ace5320c20d33c281e7097b7
> "rtnetlink: handle rtnl_link netlink notifications manually" was merged
> we no longer send a netlink message when a networking device is moved
> from one network namespace to another.
> 
> Fix this by adding the missing manual notification in dev_change_net_namespaces.
> 
> Since all network devices that are processed by dev_change_net_namspaces are
> in the initialized state the complicated tests that guard the manual
> rtmsg_ifinfo calls in rollback_registered and register_netdevice are
> unnecessary and we can just perform a plain notification.
> 
> Cc: stable@kernel.org
> Tested-by: Renato Westphal <renatowestphal@gmail.com>
> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>

Applied, thanks Eric.

^ permalink raw reply

* Re: [PATCH net-next] ipv4: tcp: fix TOS value in ACK messages sent from TIME_WAIT
From: David Miller @ 2011-10-24  7:06 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev, muralira, ak, hidden
In-Reply-To: <1319192965.2338.25.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 21 Oct 2011 12:29:25 +0200

> There is a long standing bug in linux tcp stack, about ACK messages sent
> on behalf of TIME_WAIT sockets.
> 
> In the IP header of the ACK message, we choose to reflect TOS field of
> incoming message, and this might break some setups.
> 
> Example of things that were broken :
>   - Routing using TOS as a selector
>   - Firewalls
>   - Trafic classification / shaping
> 
> We now remember in timewait structure the inet tos field and use it in
> ACK generation, and route lookup.
> 
> Notes :
>  - We still reflect incoming TOS in RST messages.
>  - We could extend MuraliRaja Muniraju patch to report TOS value in
> netlink messages for TIME_WAIT sockets.
>  - A patch is needed for IPv6
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied, thanks a lot Eric.

^ permalink raw reply

* Re: [PATCH V2 2/4] MIPS: Add board support for Loongson1B
From: Giuseppe CAVALLARO @ 2011-10-24  7:19 UTC (permalink / raw)
  To: keguang.zhang
  Cc: Wu Zhangjin, linux-mips, linux-kernel, ralf, r0bertz, netdev
In-Reply-To: <CAD+V5YKBkW52_md9rBeVZ1RXq2FGEXt=Ergsw+z8txMreZdNsA@mail.gmail.com>

On 10/21/2011 7:33 PM, Wu Zhangjin wrote:
> On Fri, Oct 21, 2011 at 6:28 PM,  <keguang.zhang@gmail.com> wrote:
>> From: Kelvin Cheung <keguang.zhang@gmail.com>
>>
>> This patch adds basic platform support for Loongson1B
>> including serial port, ethernet, and interrupt handler.
>>
>> Loongson1B UART is compatible with NS16550A.
>> Loongson1B GMAC is built around Synopsys IP Core.
>>
> 
> Perhaps you'd better split out the GMAC support to its own patch and
> send it to the net/ maintainer and the authors of the original files.

Also suggest you to provide the stmmac patches for net-next.
The stmmac driver has been recently updated and I've also several
patches to commit (for example for PCI etc) on it.

I'm happy that the support for big endianess arrived.
I supported a guy some time ago but he didn't provided me the patches
tested on his side :-(. So welcome yours and many thanks Kelvin!

Please send the stmmac patches and add me on CC. I'm happy to help you
on reviewing them.

>> diff --git a/drivers/net/stmmac/descs.h b/drivers/net/stmmac/descs.h
>> index 63a03e2..4db27d0 100644
>> --- a/drivers/net/stmmac/descs.h
>> +++ b/drivers/net/stmmac/descs.h
>> @@ -53,6 +53,38 @@ struct dma_desc {
>>                        u32 reserved3:5;
>>                        u32 disable_ic:1;
>>                } rx;
>> +#ifdef CONFIG_MACH_LOONGSON1
>> +               struct {
>> +                       /* RDES0 */
>> +                       u32 payload_csum_error:1;
>> +                       u32 crc_error:1;
>> +                       u32 dribbling:1;
>> +                       u32 error_gmii:1;
>> +                       u32 receive_watchdog:1;
>> +                       u32 frame_type:1;
>> +                       u32 late_collision:1;
>> +                       u32 ipc_csum_error:1;
>> +                       u32 last_descriptor:1;
>> +                       u32 first_descriptor:1;
>> +                       u32 vlan_tag:1;
>> +                       u32 overflow_error:1;
>> +                       u32 length_error:1;
>> +                       u32 sa_filter_fail:1;
>> +                       u32 descriptor_error:1;
>> +                       u32 error_summary:1;
>> +                       u32 frame_length:14;
>> +                       u32 da_filter_fail:1;
>> +                       u32 own:1;
>> +                       /* RDES1 */
>> +                       u32 buffer1_size:11;
>> +                       u32 buffer2_size:11;
>> +                       u32 reserved1:2;
>> +                       u32 second_address_chained:1;
>> +                       u32 end_ring:1;
>> +                       u32 reserved2:5;
>> +                       u32 disable_ic:1;
>> +               } erx;          /* -- enhanced -- */
>> +#else
>>                struct {
>>                        /* RDES0 */
>>                        u32 payload_csum_error:1;
>> @@ -83,6 +115,7 @@ struct dma_desc {
>>                        u32 reserved2:2;
>>                        u32 disable_ic:1;
>>                } erx;          /* -- enhanced -- */
>> +#endif
>>
>>                /* Transmit descriptor */
>>                struct {
>> @@ -113,6 +146,40 @@ struct dma_desc {
>>                        u32 last_segment:1;
>>                        u32 interrupt:1;
>>                } tx;
>> +#ifdef CONFIG_MACH_LOONGSON1
>> +               struct {
>> +                       /* TDES0 */
>> +                       u32 deferred:1;
>> +                       u32 underflow_error:1;
>> +                       u32 excessive_deferral:1;
>> +                       u32 collision_count:4;
>> +                       u32 vlan_frame:1;
>> +                       u32 excessive_collisions:1;
>> +                       u32 late_collision:1;
>> +                       u32 no_carrier:1;
>> +                       u32 loss_carrier:1;
>> +                       u32 payload_error:1;
>> +                       u32 frame_flushed:1;
>> +                       u32 jabber_timeout:1;
>> +                       u32 error_summary:1;
>> +                       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 time_stamp_enable:1;
>> +                       u32 disable_padding:1;
>> +                       u32 second_address_chained:1;
>> +                       u32 end_ring:1;
>> +                       u32 crc_disable:1;
>> +                       u32 checksum_insertion:2;
>> +                       u32 first_segment:1;
>> +                       u32 last_segment:1;
>> +                       u32 interrupt:1;
>> +               } etx;          /* -- enhanced -- */
>> +#else
>>                struct {
>>                        /* TDES0 */
>>                        u32 deferred:1;
>> @@ -148,6 +215,7 @@ struct dma_desc {
>>                        u32 buffer2_size:13;
>>                        u32 reserved4:3;
>>                } etx;          /* -- enhanced -- */
>> +#endif
>>        } des01;
>>        unsigned int des2;
>>        unsigned int des3;
> 
> 
> If the difference is very much, perhaps a new dma_desc struct can be
> defined instead.
> 

Concerning the descriptors, we could have two different files:

descs_le.h
descs_be.h

and select their inclusion inside the common.h.

Please use instead of the macro CONFIG_MACH_LOONGSON1 another one more
generic e.g. CONFIG_STMMAC_BE  (and add it in the driver's Kconfig).

On your platform you will have to enable it by default.
Or it could be the default on MIPS: LE will be on ARM and SuperH.

>> diff --git a/drivers/net/stmmac/enh_desc.c b/drivers/net/stmmac/enh_desc.c
>> index e5dfb6a..3b5e4f1 100644
>> --- a/drivers/net/stmmac/enh_desc.c
>> +++ b/drivers/net/stmmac/enh_desc.c
>> @@ -108,6 +108,7 @@ static int enh_desc_get_tx_len(struct dma_desc *p)
>>  static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
>>  {
>>        int ret = good_frame;
>> +#ifndef CONFIG_MACH_LOONGSON1
>>        u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
>>
>>        /* bits 5 7 0 | Frame status
>> @@ -145,6 +146,7 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
>>                CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n");
>>                ret = discard_frame;
>>        }
>> +#endif
>>        return ret;
>>  }

>>
>> @@ -232,9 +234,17 @@ static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
>>        int i;
>>        for (i = 0; i < ring_size; i++) {
>>                p->des01.erx.own = 1;
>> +#ifdef CONFIG_MACH_LOONGSON1
>> +               p->des01.erx.buffer1_size = BUF_SIZE_2KiB - 1;
>> +#else
>>                p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
>> +#endif
>>                /* To support jumbo frames */
>> +#ifdef CONFIG_MACH_LOONGSON1
>> +               p->des01.erx.buffer2_size = BUF_SIZE_2KiB - 1;
>> +#else
>>                p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
>> +#endif
>>                if (i == ring_size - 1)
>>                        p->des01.erx.end_ring = 1;
>>                if (disable_rx_ic)
>> @@ -292,9 +302,15 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
>>                                     int csum_flag)
>>  {
>>        p->des01.etx.first_segment = is_fs;
>> +#ifdef CONFIG_MACH_LOONGSON1
>> +       if (unlikely(len > BUF_SIZE_2KiB)) {
>> +               p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1;
>> +               p->des01.etx.buffer2_size = len - BUF_SIZE_2KiB + 1;
>> +#else
>>        if (unlikely(len > BUF_SIZE_4KiB)) {
>>                p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
>>                p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
>> +#endif
>>        } else {
>>                p->des01.etx.buffer1_size = len;
>>        }

No. I do not want to see all these ifdef inside the code.
I had to rework some driver's part just last week to avoid this kind of
code. I suggest you to re-base the work against the net-next kernel and
look at how the ring/chained modes have been managed.

I added a new file called descs_com.h that you can re-use adding small
inline functions where define the changes for be mode.

> Is it possible to add two new macros RX_BUF_SIZE and TX_BUF_SIZE to .h
> instead? which may reduce code duplication.

This code exists because we have to properly handle the jumbo frames.

Note that this code has been reworked to use the ring/chained modes.
Take a look at descs_com.h.

I expect to see the driver on your platform that uses jumbo and
chained/ring modes.

Best Regards
Giuseppe

> 
> Regards,
> Wu Zhangjin
> 
>> --
>> 1.7.1
>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [net-next 0/5][pull request] Intel Wired LAN Driver Updates
From: David Miller @ 2011-10-24  7:25 UTC (permalink / raw)
  To: jeffrey.t.kirsher; +Cc: netdev, gospo, sassmann
In-Reply-To: <1319193121-2729-1-git-send-email-jeffrey.t.kirsher@intel.com>

From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Fri, 21 Oct 2011 03:31:56 -0700

> The following series contains updates to igbvf and igb.
> This version of the series contains the following changes:
> 
> - igb 2 fixes (VFTA table, Alt. MAC addr) and move DMA coalescing init
>   code into separate function
> - igbvf bump version and update identification strings
> 
> The following are changes since commit :
>   
> and are available in the git repository at
>   git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git

Pulled, thanks Jeff.

^ permalink raw reply

* [patch net-next V4] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-24  8:13 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>

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() user 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                  |   38 +
 drivers/net/team/Makefile                 |    7 +
 drivers/net/team/team.c                   | 1573 +++++++++++++++++++++++++++++
 drivers/net/team/team_mode_activebackup.c |  152 +++
 drivers/net/team/team_mode_roundrobin.c   |  107 ++
 include/linux/Kbuild                      |    1 +
 include/linux/if.h                        |    1 +
 include/linux/if_team.h                   |  231 +++++
 include/linux/rculist.h                   |   14 +
 13 files changed, 2136 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 5008b08..c33400d 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:	Erik Gilling <konkers@android.com>
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..70a43a6
--- /dev/null
+++ b/drivers/net/team/Kconfig
@@ -0,0 +1,38 @@
+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.
+
+	  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.
+
+	  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..acfef4c
--- /dev/null
+++ b/drivers/net/team/team.c
@@ -0,0 +1,1573 @@
+/*
+ * 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);
+}
+EXPORT_SYMBOL(team_port_set_orig_mac);
+
+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_rcu(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;
+	char tmp_addr[ETH_ALEN];
+	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);
+	random_ether_addr(tmp_addr);
+	err = __set_port_mac(port_dev, tmp_addr);
+	if (err) {
+		netdev_dbg(dev, "Device %s mac addr set failed\n",
+			   portname);
+		goto err_set_mac_rand;
+	}
+
+	err = dev_open(port_dev);
+	if (err) {
+		netdev_dbg(dev, "Device %s opening failed\n",
+			   portname);
+		goto err_dev_open;
+	}
+
+	err = team_port_set_orig_mac(port);
+	if (err) {
+		netdev_dbg(dev, "Device %s mac addr set failed - Device does not support addr change when it's opened\n",
+			   portname);
+		goto err_set_mac_opened;
+	}
+
+	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 = 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:
+	team_port_leave(team, port);
+
+err_port_enter:
+err_set_mac_opened:
+	dev_close(port_dev);
+
+err_dev_open:
+	team_port_set_orig_mac(port);
+
+err_set_mac_rand:
+	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);
+	team_port_leave(team, port);
+	dev_close(port_dev);
+	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;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(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;
+		}
+	}
+	rcu_read_unlock();
+
+	dev->mtu = new_mtu;
+
+	return 0;
+
+unwind:
+	list_for_each_entry_continue_reverse_rcu(port, &team->port_list, list)
+		dev_set_mtu(port->dev, dev->mtu);
+
+	rcu_read_unlock();
+	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_rcu(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_CHANGEADDR:
+		/* Forbid to change addr 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..1aa2bfb
--- /dev/null
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -0,0 +1,152 @@
+/*
+ * 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 void ab_port_change_mac(struct team *team, struct team_port *port)
+{
+	if (ab_priv(team)->active_port == port)
+		team_port_set_team_mac(port);
+}
+
+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) {
+			struct team_port *ac_port = ab_priv(team)->active_port;
+
+			/* rtnl_lock needs to be held when setting macs */
+			rtnl_lock();
+			if (ac_port)
+				team_port_set_orig_mac(ac_port);
+			rcu_assign_pointer(ab_priv(team)->active_port, port);
+			team_port_set_team_mac(port);
+			rtnl_unlock();
+			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,
+	.port_change_mac	= ab_port_change_mac,
+};
+
+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..d133ac3
--- /dev/null
+++ b/include/linux/if_team.h
@@ -0,0 +1,231 @@
+/*
+ * 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_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_orig_mac(struct team_port *port);
+extern int team_port_set_team_mac(struct team_port *port);
+extern void team_options_register(struct team *team,
+				  struct team_option *option,
+				  size_t option_count);
+extern void team_options_unregister(struct team *team,
+				    struct team_option *option,
+				    size_t option_count);
+extern int team_mode_register(struct team_mode *mode);
+extern int team_mode_unregister(struct team_mode *mode);
+
+#endif /* __KERNEL__ */
+
+#define TEAM_STRING_MAX_LEN 32
+
+/**********************************
+ * NETLINK_GENERIC netlink family.
+ **********************************/
+
+enum {
+	TEAM_CMD_NOOP,
+	TEAM_CMD_OPTIONS_SET,
+	TEAM_CMD_OPTIONS_GET,
+	TEAM_CMD_PORT_LIST_GET,
+
+	__TEAM_CMD_MAX,
+	TEAM_CMD_MAX = (__TEAM_CMD_MAX - 1),
+};
+
+enum {
+	TEAM_ATTR_UNSPEC,
+	TEAM_ATTR_TEAM_IFINDEX,		/* u32 */
+	TEAM_ATTR_LIST_OPTION,		/* nest */
+	TEAM_ATTR_LIST_PORT,		/* nest */
+
+	__TEAM_ATTR_MAX,
+	TEAM_ATTR_MAX = __TEAM_ATTR_MAX - 1,
+};
+
+/* Nested layout of get/set msg:
+ *
+ *	[TEAM_ATTR_LIST_OPTION]
+ *		[TEAM_ATTR_ITEM_OPTION]
+ *			[TEAM_ATTR_OPTION_*], ...
+ *		[TEAM_ATTR_ITEM_OPTION]
+ *			[TEAM_ATTR_OPTION_*], ...
+ *		...
+ *	[TEAM_ATTR_LIST_PORT]
+ *		[TEAM_ATTR_ITEM_PORT]
+ *			[TEAM_ATTR_PORT_*], ...
+ *		[TEAM_ATTR_ITEM_PORT]
+ *			[TEAM_ATTR_PORT_*], ...
+ *		...
+ */
+
+enum {
+	TEAM_ATTR_ITEM_OPTION_UNSPEC,
+	TEAM_ATTR_ITEM_OPTION,		/* nest */
+
+	__TEAM_ATTR_ITEM_OPTION_MAX,
+	TEAM_ATTR_ITEM_OPTION_MAX = __TEAM_ATTR_ITEM_OPTION_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_OPTION_UNSPEC,
+	TEAM_ATTR_OPTION_NAME,		/* string */
+	TEAM_ATTR_OPTION_CHANGED,	/* flag */
+	TEAM_ATTR_OPTION_TYPE,		/* u8 */
+	TEAM_ATTR_OPTION_DATA,		/* dynamic */
+
+	__TEAM_ATTR_OPTION_MAX,
+	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_ITEM_PORT_UNSPEC,
+	TEAM_ATTR_ITEM_PORT,		/* nest */
+
+	__TEAM_ATTR_ITEM_PORT_MAX,
+	TEAM_ATTR_ITEM_PORT_MAX = __TEAM_ATTR_ITEM_PORT_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_PORT_UNSPEC,
+	TEAM_ATTR_PORT_IFINDEX,		/* u32 */
+	TEAM_ATTR_PORT_CHANGED,		/* flag */
+	TEAM_ATTR_PORT_LINKUP,		/* flag */
+	TEAM_ATTR_PORT_SPEED,		/* u32 */
+	TEAM_ATTR_PORT_DUPLEX,		/* u8 */
+
+	__TEAM_ATTR_PORT_MAX,
+	TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
+};
+
+/*
+ * NETLINK_GENERIC related info
+ */
+#define TEAM_GENL_NAME "team"
+#define TEAM_GENL_VERSION 0x1
+#define TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME "change_event"
+
+#endif /* _LINUX_IF_TEAM_H_ */
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
index d079290..7586b2c 100644
--- a/include/linux/rculist.h
+++ b/include/linux/rculist.h
@@ -288,6 +288,20 @@ static inline void list_splice_init_rcu(struct list_head *list,
 	     pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
 
 /**
+ * list_for_each_entry_continue_reverse_rcu - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse_rcu(pos, head, member)	\
+	for (pos = list_entry_rcu(pos->member.prev, typeof(*pos), member); \
+	     &pos->member != (head);	\
+	     pos = list_entry_rcu(pos->member.prev, typeof(*pos), member))
+
+/**
  * hlist_del_rcu - deletes entry from hash list without re-initialization
  * @n: the element to delete from the hash list.
  *
-- 
1.7.6

^ permalink raw reply related

* Re: [Qemu-devel] [RFC v2 PATCH 5/4 PATCH] virtio-net: send gratuitous packet when needed
From: Ben Hutchings @ 2011-10-24  9:11 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Rusty Russell, Jason Wang, aliguori, quintela, jan.kiszka,
	qemu-devel, blauwirbel, pbonzini, kvm, netdev
In-Reply-To: <20111024052526.GA2362@redhat.com>

On Mon, 2011-10-24 at 07:25 +0200, Michael S. Tsirkin wrote:
> On Mon, Oct 24, 2011 at 02:54:59PM +1030, Rusty Russell wrote:
> > On Sat, 22 Oct 2011 13:43:11 +0800, Jason Wang <jasowang@redhat.com> wrote:
> > > This make let virtio-net driver can send gratituous packet by a new
> > > config bit - VIRTIO_NET_S_ANNOUNCE in each config update
> > > interrupt. When this bit is set by backend, the driver would schedule
> > > a workqueue to send gratituous packet through NETDEV_NOTIFY_PEERS.
> > > 
> > > This feature is negotiated through bit VIRTIO_NET_F_GUEST_ANNOUNCE.
> > > 
> > > Signed-off-by: Jason Wang <jasowang@redhat.com>
> > 
> > This seems like a huge layering violation.  Imagine this in real
> > hardware, for example.
> 
> commits 06c4648d46d1b757d6b9591a86810be79818b60c
> and 99606477a5888b0ead0284fecb13417b1da8e3af
> document the need for this:
> 
> NETDEV_NOTIFY_PEERS notifier indicates that a device moved to a 
> different physical link.
> 	and
> In real hardware such notifications are only
> generated when the device comes up or the address changes.
> 
> So hypervisor could get the same behaviour by sending link up/down
> events, this is just an optimization so guest won't do
> unecessary stuff like try to reconfigure an IP address.
> 
> 
> Maybe LOCATION_CHANGE would be a better name?
[...]

We also use this in bonding failover, where the system location doesn't
change but a different link is used.  However, I do recognise that the
name ought to indicate what kind of change happened and not what the
expected action is.

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 5/6] xen/netback: Enable netback on HVM guests
From: Ian Campbell @ 2011-10-24  9:31 UTC (permalink / raw)
  To: Daniel De Graaf, David Miller
  Cc: konrad.wilk@oracle.com, David Vrabel,
	xen-devel@lists.xensource.com, netdev
In-Reply-To: <1319124957-32269-6-git-send-email-dgdegra@tycho.nsa.gov>

On Thu, 2011-10-20 at 16:35 +0100, Daniel De Graaf wrote:
> Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>

Acked-by: Ian Campbell <ian.campbell@citrix.com>

Normally netback patches would go in via the networking subsystem
maintainer's tree but since this depends on core Xen patches from this
series and is unlikely to conflict with anything in the net-next tree I
suspect it would make more sense for Konrad to take this one.

David (Miller) does that work for you?

Ian.

> ---
>  drivers/net/xen-netback/netback.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
> index 3af2924..9d80f99 100644
> --- a/drivers/net/xen-netback/netback.c
> +++ b/drivers/net/xen-netback/netback.c
> @@ -1626,7 +1626,7 @@ static int __init netback_init(void)
>  	int rc = 0;
>  	int group;
>  
> -	if (!xen_pv_domain())
> +	if (!xen_domain())
>  		return -ENODEV;
>  
>  	xen_netbk_group_nr = num_online_cpus();

^ permalink raw reply

* Re: [PATCH 5/6] xen/netback: Enable netback on HVM guests
From: David Miller @ 2011-10-24  9:34 UTC (permalink / raw)
  To: Ian.Campbell; +Cc: netdev, dgdegra, xen-devel, david.vrabel, konrad.wilk
In-Reply-To: <1319448669.3385.161.camel@zakaz.uk.xensource.com>

From: Ian Campbell <Ian.Campbell@citrix.com>
Date: Mon, 24 Oct 2011 10:31:08 +0100

> On Thu, 2011-10-20 at 16:35 +0100, Daniel De Graaf wrote:
>> Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
> 
> Acked-by: Ian Campbell <ian.campbell@citrix.com>
> 
> Normally netback patches would go in via the networking subsystem
> maintainer's tree but since this depends on core Xen patches from this
> series and is unlikely to conflict with anything in the net-next tree I
> suspect it would make more sense for Konrad to take this one.
> 
> David (Miller) does that work for you?

Yes, it does.

^ permalink raw reply

* Re: [PATCH V2 2/4] MIPS: Add board support for Loongson1B
From: Kelvin Cheung @ 2011-10-24 10:36 UTC (permalink / raw)
  To: Giuseppe CAVALLARO
  Cc: Wu Zhangjin, linux-mips, linux-kernel, ralf, r0bertz, netdev
In-Reply-To: <4EA5117C.3000402@st.com>

Hi Giuseppe,

2011/10/24, Giuseppe CAVALLARO <peppe.cavallaro@st.com>:
> On 10/21/2011 7:33 PM, Wu Zhangjin wrote:
>> On Fri, Oct 21, 2011 at 6:28 PM,  <keguang.zhang@gmail.com> wrote:
>>> From: Kelvin Cheung <keguang.zhang@gmail.com>
>>>
>>> This patch adds basic platform support for Loongson1B
>>> including serial port, ethernet, and interrupt handler.
>>>
>>> Loongson1B UART is compatible with NS16550A.
>>> Loongson1B GMAC is built around Synopsys IP Core.
>>>
>>
>> Perhaps you'd better split out the GMAC support to its own patch and
>> send it to the net/ maintainer and the authors of the original files.
>
> Also suggest you to provide the stmmac patches for net-next.
> The stmmac driver has been recently updated and I've also several
> patches to commit (for example for PCI etc) on it.
>
> I'm happy that the support for big endianess arrived.
> I supported a guy some time ago but he didn't provided me the patches
> tested on his side :-(. So welcome yours and many thanks Kelvin!
>
> Please send the stmmac patches and add me on CC. I'm happy to help you
> on reviewing them.

Thanks for your review.

>>> diff --git a/drivers/net/stmmac/descs.h b/drivers/net/stmmac/descs.h
>>> index 63a03e2..4db27d0 100644
>>> --- a/drivers/net/stmmac/descs.h
>>> +++ b/drivers/net/stmmac/descs.h
>>> @@ -53,6 +53,38 @@ struct dma_desc {
>>>                        u32 reserved3:5;
>>>                        u32 disable_ic:1;
>>>                } rx;
>>> +#ifdef CONFIG_MACH_LOONGSON1
>>> +               struct {
>>> +                       /* RDES0 */
>>> +                       u32 payload_csum_error:1;
>>> +                       u32 crc_error:1;
>>> +                       u32 dribbling:1;
>>> +                       u32 error_gmii:1;
>>> +                       u32 receive_watchdog:1;
>>> +                       u32 frame_type:1;
>>> +                       u32 late_collision:1;
>>> +                       u32 ipc_csum_error:1;
>>> +                       u32 last_descriptor:1;
>>> +                       u32 first_descriptor:1;
>>> +                       u32 vlan_tag:1;
>>> +                       u32 overflow_error:1;
>>> +                       u32 length_error:1;
>>> +                       u32 sa_filter_fail:1;
>>> +                       u32 descriptor_error:1;
>>> +                       u32 error_summary:1;
>>> +                       u32 frame_length:14;
>>> +                       u32 da_filter_fail:1;
>>> +                       u32 own:1;
>>> +                       /* RDES1 */
>>> +                       u32 buffer1_size:11;
>>> +                       u32 buffer2_size:11;
>>> +                       u32 reserved1:2;
>>> +                       u32 second_address_chained:1;
>>> +                       u32 end_ring:1;
>>> +                       u32 reserved2:5;
>>> +                       u32 disable_ic:1;
>>> +               } erx;          /* -- enhanced -- */
>>> +#else
>>>                struct {
>>>                        /* RDES0 */
>>>                        u32 payload_csum_error:1;
>>> @@ -83,6 +115,7 @@ struct dma_desc {
>>>                        u32 reserved2:2;
>>>                        u32 disable_ic:1;
>>>                } erx;          /* -- enhanced -- */
>>> +#endif
>>>
>>>                /* Transmit descriptor */
>>>                struct {
>>> @@ -113,6 +146,40 @@ struct dma_desc {
>>>                        u32 last_segment:1;
>>>                        u32 interrupt:1;
>>>                } tx;
>>> +#ifdef CONFIG_MACH_LOONGSON1
>>> +               struct {
>>> +                       /* TDES0 */
>>> +                       u32 deferred:1;
>>> +                       u32 underflow_error:1;
>>> +                       u32 excessive_deferral:1;
>>> +                       u32 collision_count:4;
>>> +                       u32 vlan_frame:1;
>>> +                       u32 excessive_collisions:1;
>>> +                       u32 late_collision:1;
>>> +                       u32 no_carrier:1;
>>> +                       u32 loss_carrier:1;
>>> +                       u32 payload_error:1;
>>> +                       u32 frame_flushed:1;
>>> +                       u32 jabber_timeout:1;
>>> +                       u32 error_summary:1;
>>> +                       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 time_stamp_enable:1;
>>> +                       u32 disable_padding:1;
>>> +                       u32 second_address_chained:1;
>>> +                       u32 end_ring:1;
>>> +                       u32 crc_disable:1;
>>> +                       u32 checksum_insertion:2;
>>> +                       u32 first_segment:1;
>>> +                       u32 last_segment:1;
>>> +                       u32 interrupt:1;
>>> +               } etx;          /* -- enhanced -- */
>>> +#else
>>>                struct {
>>>                        /* TDES0 */
>>>                        u32 deferred:1;
>>> @@ -148,6 +215,7 @@ struct dma_desc {
>>>                        u32 buffer2_size:13;
>>>                        u32 reserved4:3;
>>>                } etx;          /* -- enhanced -- */
>>> +#endif
>>>        } des01;
>>>        unsigned int des2;
>>>        unsigned int des3;
>>
>>
>> If the difference is very much, perhaps a new dma_desc struct can be
>> defined instead.
>>
>
> Concerning the descriptors, we could have two different files:
>
> descs_le.h
> descs_be.h
>
> and select their inclusion inside the common.h.
>
> Please use instead of the macro CONFIG_MACH_LOONGSON1 another one more
> generic e.g. CONFIG_STMMAC_BE  (and add it in the driver's Kconfig).
>
> On your platform you will have to enable it by default.
> Or it could be the default on MIPS: LE will be on ARM and SuperH.

Loongson1B(MIPS32 R2 compatible) is little endian.
And as you can see, the bitfield of RX/TX descriptor is different from
the enhanced descriptor.

>>> diff --git a/drivers/net/stmmac/enh_desc.c
>>> b/drivers/net/stmmac/enh_desc.c
>>> index e5dfb6a..3b5e4f1 100644
>>> --- a/drivers/net/stmmac/enh_desc.c
>>> +++ b/drivers/net/stmmac/enh_desc.c
>>> @@ -108,6 +108,7 @@ static int enh_desc_get_tx_len(struct dma_desc *p)
>>>  static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
>>>  {
>>>        int ret = good_frame;
>>> +#ifndef CONFIG_MACH_LOONGSON1
>>>        u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
>>>
>>>        /* bits 5 7 0 | Frame status
>>> @@ -145,6 +146,7 @@ static int enh_desc_coe_rdes0(int ipc_err, int type,
>>> int payload_err)
>>>                CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6
>>> frame.\n");
>>>                ret = discard_frame;
>>>        }
>>> +#endif
>>>        return ret;
>>>  }
>
>>>
>>> @@ -232,9 +234,17 @@ static void enh_desc_init_rx_desc(struct dma_desc
>>> *p, unsigned int ring_size,
>>>        int i;
>>>        for (i = 0; i < ring_size; i++) {
>>>                p->des01.erx.own = 1;
>>> +#ifdef CONFIG_MACH_LOONGSON1
>>> +               p->des01.erx.buffer1_size = BUF_SIZE_2KiB - 1;
>>> +#else
>>>                p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
>>> +#endif
>>>                /* To support jumbo frames */
>>> +#ifdef CONFIG_MACH_LOONGSON1
>>> +               p->des01.erx.buffer2_size = BUF_SIZE_2KiB - 1;
>>> +#else
>>>                p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
>>> +#endif
>>>                if (i == ring_size - 1)
>>>                        p->des01.erx.end_ring = 1;
>>>                if (disable_rx_ic)
>>> @@ -292,9 +302,15 @@ static void enh_desc_prepare_tx_desc(struct dma_desc
>>> *p, int is_fs, int len,
>>>                                     int csum_flag)
>>>  {
>>>        p->des01.etx.first_segment = is_fs;
>>> +#ifdef CONFIG_MACH_LOONGSON1
>>> +       if (unlikely(len > BUF_SIZE_2KiB)) {
>>> +               p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1;
>>> +               p->des01.etx.buffer2_size = len - BUF_SIZE_2KiB + 1;
>>> +#else
>>>        if (unlikely(len > BUF_SIZE_4KiB)) {
>>>                p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
>>>                p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
>>> +#endif
>>>        } else {
>>>                p->des01.etx.buffer1_size = len;
>>>        }
>
> No. I do not want to see all these ifdef inside the code.
> I had to rework some driver's part just last week to avoid this kind of
> code. I suggest you to re-base the work against the net-next kernel and
> look at how the ring/chained modes have been managed.
>
> I added a new file called descs_com.h that you can re-use adding small
> inline functions where define the changes for be mode.

According to datasheet of Loongson 1B, the buffer size in RX/TX
descriptor is only 2KB.
So the Loongson1B's GMAC could not handle jumbo frames.
And the second buffer is useless in this case.
Am I right?

Is there a better way than ifdef CONFIG_MACH_LOONGSON1 to avoid duplicate code?

>> Is it possible to add two new macros RX_BUF_SIZE and TX_BUF_SIZE to .h
>> instead? which may reduce code duplication.
>
> This code exists because we have to properly handle the jumbo frames.
>
> Note that this code has been reworked to use the ring/chained modes.
> Take a look at descs_com.h.
>
> I expect to see the driver on your platform that uses jumbo and
> chained/ring modes.
>
> Best Regards
> Giuseppe
>
>>
>> Regards,
>> Wu Zhangjin
>>
>>> --
>>> 1.7.1
>>>
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>


-- 
Best Regards!
Kelvin

^ permalink raw reply

* [PATCH] [TRIVIAL] netfilter: make physdev_mt more readable
From: Richard Weinberger @ 2011-10-24 12:06 UTC (permalink / raw)
  To: netfilter-devel
  Cc: netdev, linux-kernel, trivial, davem, kaber, Richard Weinberger

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 net/netfilter/xt_physdev.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c
index d7ca16b..eabb751 100644
--- a/net/netfilter/xt_physdev.c
+++ b/net/netfilter/xt_physdev.c
@@ -28,12 +28,12 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
 	const struct xt_physdev_info *info = par->matchinfo;
 	unsigned long ret;
 	const char *indev, *outdev;
-	const struct nf_bridge_info *nf_bridge;
+	const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
 
 	/* Not a bridged IP packet or no info available yet:
 	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
 	 * the destination device will be a bridge. */
-	if (!(nf_bridge = skb->nf_bridge)) {
+	if (!nf_bridge) {
 		/* Return MATCH if the invert flags of the used options are on */
 		if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
 		    !(info->invert & XT_PHYSDEV_OP_BRIDGED))
-- 
1.7.7

^ permalink raw reply related

* Re: [PATCH V2 2/4] MIPS: Add board support for Loongson1B
From: Giuseppe CAVALLARO @ 2011-10-24 12:18 UTC (permalink / raw)
  To: Kelvin Cheung
  Cc: Wu Zhangjin, linux-mips, linux-kernel, ralf, r0bertz, netdev
In-Reply-To: <CAJhJPsVSzXXmBOwLaGNtOsxhVEyC0fAJtiBvEWzcKcSDC8NEcA@mail.gmail.com>

Hello Kelvin.

On 10/24/2011 12:36 PM, Kelvin Cheung wrote:

[snip]

> According to datasheet of Loongson 1B, the buffer size in RX/TX 
> descriptor is only 2KB. So the Loongson1B's GMAC could not handle
> jumbo frames. And the second buffer is useless in this case. Am I
> right? Is there a better way than ifdef CONFIG_MACH_LOONGSON1 to
> avoid duplicate code?

Sorry for my misunderstanding.

I think you have to use the normal descriptor and remove the enh_desc
from the platform w/o modifying the driver at all.

The driver will be able to select/configure all automatically (also jumbo).

Let me know.

Note:
IIRC, there is a bit difference in case of normal descriptors for
Synopsys databook newer than the 1.91 (I used for testing this mode).
In any case, I remember that, on some platforms, the normal descriptors
have been used w/o problems also on these new chip generations.

Peppe

^ permalink raw reply

* [PATCH net-next 0/4] be2net: fixes
From: Sathya Perla @ 2011-10-24 12:44 UTC (permalink / raw)
  To: netdev

Pls apply.

Sathya Perla (4):
  be2net: add vlan/rx-mode/flow-control config to be_setup()
  be2net: refactor VF setup/teardown code into be_vf_setup/clear()
  be2net: don't create multiple TXQs in BE2
  be2net: don't create multiple RX/TX rings in multi channel mode

 drivers/net/ethernet/emulex/benet/be_cmds.c |   66 +++--
 drivers/net/ethernet/emulex/benet/be_cmds.h |   10 +-
 drivers/net/ethernet/emulex/benet/be_main.c |  362 +++++++++++++--------------
 3 files changed, 222 insertions(+), 216 deletions(-)

-- 
1.7.4

^ permalink raw reply

* [PATCH net-next 1/4] be2net: add vlan/rx-mode/flow-control config to be_setup()
From: Sathya Perla @ 2011-10-24 12:45 UTC (permalink / raw)
  To: netdev
In-Reply-To: <1319460303-25560-1-git-send-email-sathya.perla@emulex.com>

When a card is reset due to EEH error recovery or due to a suspend, rx-mode config (promisc/mc) is not being sent to the FW. be_setup() is called in these flows and is the best place for such config/re-config cmds. Hence include rx-mode, vlan and flow-control config in be_setup().

Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
---
 drivers/net/ethernet/emulex/benet/be_main.c |  121 ++++++++++++--------------
 1 files changed, 56 insertions(+), 65 deletions(-)

diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 706fc59..88c9dca 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -796,7 +796,7 @@ static void be_vlan_rem_vid(struct net_device *netdev, u16 vid)
 		be_vid_config(adapter, false, 0);
 }
 
-static void be_set_multicast_list(struct net_device *netdev)
+static void be_set_rx_mode(struct net_device *netdev)
 {
 	struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -2352,17 +2352,6 @@ static int be_open(struct net_device *netdev)
 	/* Now that interrupts are on we can process async mcc */
 	be_async_mcc_enable(adapter);
 
-	if (be_physfn(adapter)) {
-		status = be_vid_config(adapter, false, 0);
-		if (status)
-			goto err;
-
-		status = be_cmd_set_flow_control(adapter,
-				adapter->tx_fc, adapter->rx_fc);
-		if (status)
-			goto err;
-	}
-
 	return 0;
 err:
 	be_close(adapter->netdev);
@@ -2450,10 +2439,40 @@ static inline void be_vf_eth_addr_rem(struct be_adapter *adapter)
 	}
 }
 
+static int be_clear(struct be_adapter *adapter)
+{
+	int vf;
+
+	if (be_physfn(adapter) && adapter->sriov_enabled)
+		be_vf_eth_addr_rem(adapter);
+
+	be_mcc_queues_destroy(adapter);
+	be_rx_queues_destroy(adapter);
+	be_tx_queues_destroy(adapter);
+	adapter->eq_next_idx = 0;
+
+	if (be_physfn(adapter) && adapter->sriov_enabled)
+		for (vf = 0; vf < num_vfs; vf++)
+			if (adapter->vf_cfg[vf].vf_if_handle)
+				be_cmd_if_destroy(adapter,
+					adapter->vf_cfg[vf].vf_if_handle,
+					vf + 1);
+
+	be_cmd_if_destroy(adapter, adapter->if_handle,  0);
+
+	adapter->be3_native = false;
+	adapter->promiscuous = false;
+
+	/* tell fw we're done with firing cmds */
+	be_cmd_fw_clean(adapter);
+	return 0;
+}
+
 static int be_setup(struct be_adapter *adapter)
 {
 	struct net_device *netdev = adapter->netdev;
 	u32 cap_flags, en_flags, vf = 0;
+	u32 tx_fc, rx_fc;
 	int status;
 	u8 mac[ETH_ALEN];
 
@@ -2479,7 +2498,7 @@ static int be_setup(struct be_adapter *adapter)
 			netdev->dev_addr, false/* pmac_invalid */,
 			&adapter->if_handle, &adapter->pmac_id, 0);
 	if (status != 0)
-		goto do_none;
+		goto err;
 
 	if (be_physfn(adapter)) {
 		if (adapter->sriov_enabled) {
@@ -2494,7 +2513,7 @@ static int be_setup(struct be_adapter *adapter)
 					dev_err(&adapter->pdev->dev,
 					"Interface Create failed for VF %d\n",
 					vf);
-					goto if_destroy;
+					goto err;
 				}
 				adapter->vf_cfg[vf].vf_pmac_id =
 							BE_INVALID_PMAC_ID;
@@ -2512,71 +2531,47 @@ static int be_setup(struct be_adapter *adapter)
 
 	status = be_tx_queues_create(adapter);
 	if (status != 0)
-		goto if_destroy;
+		goto err;
 
 	status = be_rx_queues_create(adapter);
 	if (status != 0)
-		goto tx_qs_destroy;
+		goto err;
 
 	/* Allow all priorities by default. A GRP5 evt may modify this */
 	adapter->vlan_prio_bmap = 0xff;
 
 	status = be_mcc_queues_create(adapter);
 	if (status != 0)
-		goto rx_qs_destroy;
+		goto err;
 
 	adapter->link_speed = -1;
 
 	be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL);
 
+	status = be_vid_config(adapter, false, 0);
+	if (status)
+		goto err;
+
+	be_set_rx_mode(adapter->netdev);
+
+	status = be_cmd_get_flow_control(adapter, &tx_fc, &rx_fc);
+	if (status)
+		goto err;
+	if (rx_fc != adapter->rx_fc || tx_fc != adapter->tx_fc) {
+		status = be_cmd_set_flow_control(adapter, adapter->tx_fc,
+					adapter->rx_fc);
+		if (status)
+			goto err;
+	}	
+
 	pcie_set_readrq(adapter->pdev, 4096);
 	return 0;
 
-rx_qs_destroy:
-	be_rx_queues_destroy(adapter);
-tx_qs_destroy:
-	be_tx_queues_destroy(adapter);
-if_destroy:
-	if (be_physfn(adapter) && adapter->sriov_enabled)
-		for (vf = 0; vf < num_vfs; vf++)
-			if (adapter->vf_cfg[vf].vf_if_handle)
-				be_cmd_if_destroy(adapter,
-					adapter->vf_cfg[vf].vf_if_handle,
-					vf + 1);
-	be_cmd_if_destroy(adapter, adapter->if_handle, 0);
-do_none:
+err:
+	be_clear(adapter);
 	return status;
 }
 
-static int be_clear(struct be_adapter *adapter)
-{
-	int vf;
-
-	if (be_physfn(adapter) && adapter->sriov_enabled)
-		be_vf_eth_addr_rem(adapter);
-
-	be_mcc_queues_destroy(adapter);
-	be_rx_queues_destroy(adapter);
-	be_tx_queues_destroy(adapter);
-	adapter->eq_next_idx = 0;
-
-	if (be_physfn(adapter) && adapter->sriov_enabled)
-		for (vf = 0; vf < num_vfs; vf++)
-			if (adapter->vf_cfg[vf].vf_if_handle)
-				be_cmd_if_destroy(adapter,
-					adapter->vf_cfg[vf].vf_if_handle,
-					vf + 1);
-
-	be_cmd_if_destroy(adapter, adapter->if_handle,  0);
-
-	adapter->be3_native = 0;
-
-	/* tell fw we're done with firing cmds */
-	be_cmd_fw_clean(adapter);
-	return 0;
-}
-
-
 #define FW_FILE_HDR_SIGN 	"ServerEngines Corp. "
 static bool be_flash_redboot(struct be_adapter *adapter,
 			const u8 *p, u32 img_start, int image_size,
@@ -2915,7 +2910,7 @@ static struct net_device_ops be_netdev_ops = {
 	.ndo_open		= be_open,
 	.ndo_stop		= be_close,
 	.ndo_start_xmit		= be_xmit,
-	.ndo_set_rx_mode	= be_set_multicast_list,
+	.ndo_set_rx_mode	= be_set_rx_mode,
 	.ndo_set_mac_address	= be_mac_addr_set,
 	.ndo_change_mtu		= be_change_mtu,
 	.ndo_get_stats64	= be_get_stats64,
@@ -2948,10 +2943,6 @@ static void be_netdev_init(struct net_device *netdev)
 
 	netdev->flags |= IFF_MULTICAST;
 
-	/* Default settings for Rx and Tx flow control */
-	adapter->rx_fc = true;
-	adapter->tx_fc = true;
-
 	netif_set_gso_max_size(netdev, 65535);
 
 	BE_SET_NETDEV_OPS(netdev, &be_netdev_ops);
@@ -3373,6 +3364,7 @@ static int __devinit be_probe(struct pci_dev *pdev,
 	be_msix_enable(adapter);
 
 	INIT_DELAYED_WORK(&adapter->work, be_worker);
+	adapter->rx_fc = adapter->tx_fc = true;
 
 	status = be_setup(adapter);
 	if (status)
@@ -3448,7 +3440,6 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state)
 		be_close(netdev);
 		rtnl_unlock();
 	}
-	be_cmd_get_flow_control(adapter, &adapter->tx_fc, &adapter->rx_fc);
 	be_clear(adapter);
 
 	be_msix_disable(adapter);
-- 
1.7.4

^ permalink raw reply related

* [PATCH net-next 2/4] be2net: refactor VF setup/teardown code into be_vf_setup/clear()
From: Sathya Perla @ 2011-10-24 12:45 UTC (permalink / raw)
  To: netdev
In-Reply-To: <1319460303-25560-1-git-send-email-sathya.perla@emulex.com>

Currently the code for VF setup/teardown done by a PF (if_create, mac_add_config, link_status_query etc) is scattered; this patch refactors this code into be_vf_setup() and be_vf_clear().
The if_create/if_destroy/mac_addr_query cmds are now called after the MCCQ is created; so these cmds are now modified to use the MCCQ instead of MBOX.

Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
---
 drivers/net/ethernet/emulex/benet/be_cmds.c |   66 +++++---
 drivers/net/ethernet/emulex/benet/be_cmds.h |    4 +-
 drivers/net/ethernet/emulex/benet/be_main.c |  233 ++++++++++++---------------
 3 files changed, 149 insertions(+), 154 deletions(-)

diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 6e7b521..8ae5a4c 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -615,7 +615,7 @@ int be_cmd_eq_create(struct be_adapter *adapter,
 	return status;
 }
 
-/* Uses mbox */
+/* Use MCC */
 int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
 			u8 type, bool permanent, u32 if_handle)
 {
@@ -623,10 +623,13 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
 	struct be_cmd_req_mac_query *req;
 	int status;
 
-	if (mutex_lock_interruptible(&adapter->mbox_lock))
-		return -1;
+	spin_lock_bh(&adapter->mcc_lock);
 
-	wrb = wrb_from_mbox(adapter);
+	wrb = wrb_from_mccq(adapter);
+	if (!wrb) {
+		status = -EBUSY;
+		goto err;
+	}
 	req = embedded_payload(wrb);
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
@@ -643,13 +646,14 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
 		req->permanent = 0;
 	}
 
-	status = be_mbox_notify_wait(adapter);
+	status = be_mcc_notify_wait(adapter);
 	if (!status) {
 		struct be_cmd_resp_mac_query *resp = embedded_payload(wrb);
 		memcpy(mac_addr, resp->mac.addr, ETH_ALEN);
 	}
 
-	mutex_unlock(&adapter->mbox_lock);
+err:
+	spin_unlock_bh(&adapter->mcc_lock);
 	return status;
 }
 
@@ -1111,20 +1115,22 @@ err:
 }
 
 /* Create an rx filtering policy configuration on an i/f
- * Uses mbox
+ * Uses MCCQ 
  */
 int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
-		u8 *mac, bool pmac_invalid, u32 *if_handle, u32 *pmac_id,
-		u32 domain)
+		u8 *mac, u32 *if_handle, u32 *pmac_id, u32 domain)
 {
 	struct be_mcc_wrb *wrb;
 	struct be_cmd_req_if_create *req;
 	int status;
 
-	if (mutex_lock_interruptible(&adapter->mbox_lock))
-		return -1;
+	spin_lock_bh(&adapter->mcc_lock);
 
-	wrb = wrb_from_mbox(adapter);
+	wrb = wrb_from_mccq(adapter);
+	if (!wrb) {
+		status = -EBUSY;
+		goto err;
+	}
 	req = embedded_payload(wrb);
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
@@ -1136,23 +1142,25 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
 	req->hdr.domain = domain;
 	req->capability_flags = cpu_to_le32(cap_flags);
 	req->enable_flags = cpu_to_le32(en_flags);
-	req->pmac_invalid = pmac_invalid;
-	if (!pmac_invalid)
+	if (mac)
 		memcpy(req->mac_addr, mac, ETH_ALEN);
+	else
+		req->pmac_invalid = true;
 
-	status = be_mbox_notify_wait(adapter);
+	status = be_mcc_notify_wait(adapter);
 	if (!status) {
 		struct be_cmd_resp_if_create *resp = embedded_payload(wrb);
 		*if_handle = le32_to_cpu(resp->interface_id);
-		if (!pmac_invalid)
+		if (mac)
 			*pmac_id = le32_to_cpu(resp->pmac_id);
 	}
 
-	mutex_unlock(&adapter->mbox_lock);
+err:
+	spin_unlock_bh(&adapter->mcc_lock);
 	return status;
 }
 
-/* Uses mbox */
+/* Uses MCCQ */
 int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
 {
 	struct be_mcc_wrb *wrb;
@@ -1162,10 +1170,16 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
 	if (adapter->eeh_err)
 		return -EIO;
 
-	if (mutex_lock_interruptible(&adapter->mbox_lock))
-		return -1;
+	if (!interface_id)
+		return 0;
 
-	wrb = wrb_from_mbox(adapter);
+	spin_lock_bh(&adapter->mcc_lock);
+
+	wrb = wrb_from_mccq(adapter);
+	if (!wrb) {
+		status = -EBUSY;
+		goto err;
+	}
 	req = embedded_payload(wrb);
 
 	be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
@@ -1177,10 +1191,9 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id, u32 domain)
 	req->hdr.domain = domain;
 	req->interface_id = cpu_to_le32(interface_id);
 
-	status = be_mbox_notify_wait(adapter);
-
-	mutex_unlock(&adapter->mbox_lock);
-
+	status = be_mcc_notify_wait(adapter);
+err:
+	spin_unlock_bh(&adapter->mcc_lock);
 	return status;
 }
 
@@ -1301,7 +1314,8 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u8 *mac_speed,
 		struct be_cmd_resp_link_status *resp = embedded_payload(wrb);
 		if (resp->mac_speed != PHY_LINK_SPEED_ZERO) {
 			*link_speed = le16_to_cpu(resp->link_speed);
-			*mac_speed = resp->mac_speed;
+			if (mac_speed)
+				*mac_speed = resp->mac_speed;
 		}
 	}
 
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index abaa90c..75b7574 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -1413,8 +1413,8 @@ extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
 extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id,
 			u32 pmac_id, u32 domain);
 extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags,
-			u32 en_flags, u8 *mac, bool pmac_invalid,
-			u32 *if_handle, u32 *pmac_id, u32 domain);
+			u32 en_flags, u8 *mac, u32 *if_handle, u32 *pmac_id,
+			u32 domain);
 extern int be_cmd_if_destroy(struct be_adapter *adapter, u32 if_handle,
 			u32 domain);
 extern int be_cmd_eq_create(struct be_adapter *adapter,
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 88c9dca..4ab182f 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -2069,7 +2069,7 @@ done:
 	return;
 }
 
-static void be_sriov_enable(struct be_adapter *adapter)
+static int be_sriov_enable(struct be_adapter *adapter)
 {
 	be_check_sriov_fn_type(adapter);
 #ifdef CONFIG_PCI_IOV
@@ -2091,8 +2091,17 @@ static void be_sriov_enable(struct be_adapter *adapter)
 
 		status = pci_enable_sriov(adapter->pdev, num_vfs);
 		adapter->sriov_enabled = status ? false : true;
+
+		if (adapter->sriov_enabled) {
+			adapter->vf_cfg = kcalloc(num_vfs,
+						sizeof(struct be_vf_cfg),
+						GFP_KERNEL);
+			if (!adapter->vf_cfg)
+				return -ENOMEM;
+		}
 	}
 #endif
+	return 0;
 }
 
 static void be_sriov_disable(struct be_adapter *adapter)
@@ -2100,6 +2109,7 @@ static void be_sriov_disable(struct be_adapter *adapter)
 #ifdef CONFIG_PCI_IOV
 	if (adapter->sriov_enabled) {
 		pci_disable_sriov(adapter->pdev);
+		kfree(adapter->vf_cfg);
 		adapter->sriov_enabled = false;
 	}
 #endif
@@ -2405,7 +2415,7 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
  */
 static inline int be_vf_eth_addr_config(struct be_adapter *adapter)
 {
-	u32 vf = 0;
+	u32 vf;
 	int status = 0;
 	u8 mac[ETH_ALEN];
 
@@ -2427,7 +2437,7 @@ static inline int be_vf_eth_addr_config(struct be_adapter *adapter)
 	return status;
 }
 
-static inline void be_vf_eth_addr_rem(struct be_adapter *adapter)
+static void be_vf_clear(struct be_adapter *adapter)
 {
 	u32 vf;
 
@@ -2437,29 +2447,25 @@ static inline void be_vf_eth_addr_rem(struct be_adapter *adapter)
 					adapter->vf_cfg[vf].vf_if_handle,
 					adapter->vf_cfg[vf].vf_pmac_id, vf + 1);
 	}
+
+	for (vf = 0; vf < num_vfs; vf++)
+		if (adapter->vf_cfg[vf].vf_if_handle)
+			be_cmd_if_destroy(adapter,
+				adapter->vf_cfg[vf].vf_if_handle, vf + 1);
 }
 
 static int be_clear(struct be_adapter *adapter)
 {
-	int vf;
-
 	if (be_physfn(adapter) && adapter->sriov_enabled)
-		be_vf_eth_addr_rem(adapter);
+		be_vf_clear(adapter);
+
+	be_cmd_if_destroy(adapter, adapter->if_handle,  0);
 
 	be_mcc_queues_destroy(adapter);
 	be_rx_queues_destroy(adapter);
 	be_tx_queues_destroy(adapter);
 	adapter->eq_next_idx = 0;
 
-	if (be_physfn(adapter) && adapter->sriov_enabled)
-		for (vf = 0; vf < num_vfs; vf++)
-			if (adapter->vf_cfg[vf].vf_if_handle)
-				be_cmd_if_destroy(adapter,
-					adapter->vf_cfg[vf].vf_if_handle,
-					vf + 1);
-
-	be_cmd_if_destroy(adapter, adapter->if_handle,  0);
-
 	adapter->be3_native = false;
 	adapter->promiscuous = false;
 
@@ -2468,59 +2474,92 @@ static int be_clear(struct be_adapter *adapter)
 	return 0;
 }
 
+static int be_vf_setup(struct be_adapter *adapter)
+{
+	u32 cap_flags, en_flags, vf;
+	u16 lnk_speed;
+	int status;
+
+	cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST;
+	for (vf = 0; vf < num_vfs; vf++) {
+		status = be_cmd_if_create(adapter, cap_flags, en_flags, NULL,
+					&adapter->vf_cfg[vf].vf_if_handle,
+					NULL, vf+1);
+		if (status)
+			goto err;
+		adapter->vf_cfg[vf].vf_pmac_id = BE_INVALID_PMAC_ID;
+	}
+
+	if (!lancer_chip(adapter)) {
+		status = be_vf_eth_addr_config(adapter);
+		if (status)
+			goto err;
+	}
+
+	for (vf = 0; vf < num_vfs; vf++) {
+		status = be_cmd_link_status_query(adapter, NULL, &lnk_speed,
+				vf + 1);
+		if (status)
+			goto err;
+		adapter->vf_cfg[vf].vf_tx_rate = lnk_speed * 10;
+	}
+	return 0;
+err:
+	return status;
+}
+
 static int be_setup(struct be_adapter *adapter)
 {
 	struct net_device *netdev = adapter->netdev;
-	u32 cap_flags, en_flags, vf = 0;
+	u32 cap_flags, en_flags;
 	u32 tx_fc, rx_fc;
 	int status;
 	u8 mac[ETH_ALEN];
 
+	/* Allow all priorities by default. A GRP5 evt may modify this */
+	adapter->vlan_prio_bmap = 0xff;
+	adapter->link_speed = -1;
+
 	be_cmd_req_native_mode(adapter);
 
-	cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED |
-				BE_IF_FLAGS_BROADCAST |
-				BE_IF_FLAGS_MULTICAST;
-
-	if (be_physfn(adapter)) {
-		cap_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS |
-				BE_IF_FLAGS_PROMISCUOUS |
-				BE_IF_FLAGS_PASS_L3L4_ERRORS;
-		en_flags |= BE_IF_FLAGS_PASS_L3L4_ERRORS;
-
-		if (adapter->function_caps & BE_FUNCTION_CAPS_RSS) {
-			cap_flags |= BE_IF_FLAGS_RSS;
-			en_flags |= BE_IF_FLAGS_RSS;
-		}
+	status = be_tx_queues_create(adapter);
+	if (status != 0)
+		goto err;
+
+	status = be_rx_queues_create(adapter);
+	if (status != 0)
+		goto err;
+
+	status = be_mcc_queues_create(adapter);
+	if (status != 0)
+		goto err;
+
+	memset(mac, 0, ETH_ALEN);
+	status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK,
+			true /*permanent */, 0);
+	if (status)
+		return status;
+	memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+	memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
+
+	en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST |
+			BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS;
+	cap_flags = en_flags | BE_IF_FLAGS_MCAST_PROMISCUOUS |
+			BE_IF_FLAGS_PROMISCUOUS;
+	if (adapter->function_caps & BE_FUNCTION_CAPS_RSS) {
+		cap_flags |= BE_IF_FLAGS_RSS;
+		en_flags |= BE_IF_FLAGS_RSS;
 	}
-
 	status = be_cmd_if_create(adapter, cap_flags, en_flags,
-			netdev->dev_addr, false/* pmac_invalid */,
-			&adapter->if_handle, &adapter->pmac_id, 0);
+			netdev->dev_addr, &adapter->if_handle,
+			&adapter->pmac_id, 0);
 	if (status != 0)
 		goto err;
-
-	if (be_physfn(adapter)) {
-		if (adapter->sriov_enabled) {
-			while (vf < num_vfs) {
-				cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED |
-							BE_IF_FLAGS_BROADCAST;
-				status = be_cmd_if_create(adapter, cap_flags,
-					en_flags, mac, true,
-					&adapter->vf_cfg[vf].vf_if_handle,
-					NULL, vf+1);
-				if (status) {
-					dev_err(&adapter->pdev->dev,
-					"Interface Create failed for VF %d\n",
-					vf);
-					goto err;
-				}
-				adapter->vf_cfg[vf].vf_pmac_id =
-							BE_INVALID_PMAC_ID;
-				vf++;
-			}
-		}
-	} else {
+	
+	/* For BEx, the VF's permanent mac queried from card is incorrect.
+	 * Query the mac configued by the PF using if_handle
+	 */
+	if (!be_physfn(adapter) && !lancer_chip(adapter)) {
 		status = be_cmd_mac_addr_query(adapter, mac,
 			MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
 		if (!status) {
@@ -2529,23 +2568,6 @@ static int be_setup(struct be_adapter *adapter)
 		}
 	}
 
-	status = be_tx_queues_create(adapter);
-	if (status != 0)
-		goto err;
-
-	status = be_rx_queues_create(adapter);
-	if (status != 0)
-		goto err;
-
-	/* Allow all priorities by default. A GRP5 evt may modify this */
-	adapter->vlan_prio_bmap = 0xff;
-
-	status = be_mcc_queues_create(adapter);
-	if (status != 0)
-		goto err;
-
-	adapter->link_speed = -1;
-
 	be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL);
 
 	status = be_vid_config(adapter, false, 0);
@@ -2565,8 +2587,14 @@ static int be_setup(struct be_adapter *adapter)
 	}	
 
 	pcie_set_readrq(adapter->pdev, 4096);
-	return 0;
 
+	if (be_physfn(adapter) && adapter->sriov_enabled) {
+		status = be_vf_setup(adapter);
+		if (status)
+			goto err;
+	}
+		
+	return 0;
 err:
 	be_clear(adapter);
 	return status;
@@ -3123,7 +3151,6 @@ static void __devexit be_remove(struct pci_dev *pdev)
 
 	be_ctrl_cleanup(adapter);
 
-	kfree(adapter->vf_cfg);
 	be_sriov_disable(adapter);
 
 	be_msix_disable(adapter);
@@ -3138,30 +3165,12 @@ static void __devexit be_remove(struct pci_dev *pdev)
 static int be_get_config(struct be_adapter *adapter)
 {
 	int status;
-	u8 mac[ETH_ALEN];
 
 	status = be_cmd_query_fw_cfg(adapter, &adapter->port_num,
 			&adapter->function_mode, &adapter->function_caps);
 	if (status)
 		return status;
 
-	memset(mac, 0, ETH_ALEN);
-
-	/* A default permanent address is given to each VF for Lancer*/
-	if (be_physfn(adapter) || lancer_chip(adapter)) {
-		status = be_cmd_mac_addr_query(adapter, mac,
-			MAC_ADDRESS_TYPE_NETWORK, true /*permanent */, 0);
-
-		if (status)
-			return status;
-
-		if (!is_valid_ether_addr(mac))
-			return -EADDRNOTAVAIL;
-
-		memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
-		memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
-	}
-
 	if (adapter->function_mode & 0x400)
 		adapter->max_vlans = BE_NUM_VLANS_SUPPORTED/4;
 	else
@@ -3310,18 +3319,13 @@ static int __devinit be_probe(struct pci_dev *pdev,
 		}
 	}
 
-	be_sriov_enable(adapter);
-	if (adapter->sriov_enabled) {
-		adapter->vf_cfg = kcalloc(num_vfs,
-			sizeof(struct be_vf_cfg), GFP_KERNEL);
-
-		if (!adapter->vf_cfg)
-			goto free_netdev;
-	}
+	status = be_sriov_enable(adapter);
+	if (status)
+		goto free_netdev;
 
 	status = be_ctrl_init(adapter);
 	if (status)
-		goto free_vf_cfg;
+		goto disable_sriov;
 
 	if (lancer_chip(adapter)) {
 		status = lancer_test_and_set_rdy_state(adapter);
@@ -3375,33 +3379,11 @@ static int __devinit be_probe(struct pci_dev *pdev,
 	if (status != 0)
 		goto unsetup;
 
-	if (be_physfn(adapter) && adapter->sriov_enabled) {
-		u8 mac_speed;
-		u16 vf, lnk_speed;
-
-		if (!lancer_chip(adapter)) {
-			status = be_vf_eth_addr_config(adapter);
-			if (status)
-				goto unreg_netdev;
-		}
-
-		for (vf = 0; vf < num_vfs; vf++) {
-			status = be_cmd_link_status_query(adapter, &mac_speed,
-						&lnk_speed, vf + 1);
-			if (!status)
-				adapter->vf_cfg[vf].vf_tx_rate = lnk_speed * 10;
-			else
-				goto unreg_netdev;
-		}
-	}
-
 	dev_info(&pdev->dev, "%s port %d\n", nic_name(pdev), adapter->port_num);
 
 	schedule_delayed_work(&adapter->work, msecs_to_jiffies(100));
 	return 0;
 
-unreg_netdev:
-	unregister_netdev(netdev);
 unsetup:
 	be_clear(adapter);
 msix_disable:
@@ -3410,10 +3392,9 @@ stats_clean:
 	be_stats_cleanup(adapter);
 ctrl_clean:
 	be_ctrl_cleanup(adapter);
-free_vf_cfg:
-	kfree(adapter->vf_cfg);
+disable_sriov:
+	be_sriov_disable(adapter);
 free_netdev:
-	be_sriov_disable(adapter);
 	free_netdev(netdev);
 	pci_set_drvdata(pdev, NULL);
 rel_reg:
-- 
1.7.4

^ permalink raw reply related

* [PATCH net-next 3/4] be2net: don't create multiple TXQs in BE2
From: Sathya Perla @ 2011-10-24 12:45 UTC (permalink / raw)
  To: netdev; +Cc: Vasundhara Volam
In-Reply-To: <1319460303-25560-1-git-send-email-sathya.perla@emulex.com>

Multiple TXQ support is partially broken in BE2. It is fully
supported BE3 onwards and in Lancer.

Signed-off-by: Vasundhara Volam <vasundhara.volam@emulex.com>
Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
---
 drivers/net/ethernet/emulex/benet/be_main.c |   26 ++++++++++++++++----------
 1 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 4ab182f..77ce24b 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -1633,6 +1633,17 @@ static void be_tx_queues_destroy(struct be_adapter *adapter)
 	be_queue_free(adapter, q);
 }
 
+static int be_num_txqs_want(struct be_adapter *adapter)
+{
+	if ((num_vfs && adapter->sriov_enabled) ||
+		(adapter->function_mode & 0x400) ||
+		lancer_chip(adapter) || !be_physfn(adapter) ||
+		adapter->generation == BE_GEN2)
+		return 1;
+	else
+		return MAX_TX_QS;
+}
+
 /* One TX event queue is shared by all TX compl qs */
 static int be_tx_queues_create(struct be_adapter *adapter)
 {
@@ -1640,6 +1651,11 @@ static int be_tx_queues_create(struct be_adapter *adapter)
 	struct be_tx_obj *txo;
 	u8 i;
 
+	adapter->num_tx_qs = be_num_txqs_want(adapter);
+	if (adapter->num_tx_qs != MAX_TX_QS)
+		netif_set_real_num_tx_queues(adapter->netdev,
+			adapter->num_tx_qs);
+
 	adapter->tx_eq.max_eqd = 0;
 	adapter->tx_eq.min_eqd = 0;
 	adapter->tx_eq.cur_eqd = 96;
@@ -3180,16 +3196,6 @@ static int be_get_config(struct be_adapter *adapter)
 	if (status)
 		return status;
 
-	if ((num_vfs && adapter->sriov_enabled) ||
-		(adapter->function_mode & 0x400) ||
-		lancer_chip(adapter) || !be_physfn(adapter)) {
-		adapter->num_tx_qs = 1;
-		netif_set_real_num_tx_queues(adapter->netdev,
-			adapter->num_tx_qs);
-	} else {
-		adapter->num_tx_qs = MAX_TX_QS;
-	}
-
 	return 0;
 }
 
-- 
1.7.4

^ permalink raw reply related

* [PATCH net-next 4/4] be2net: don't create multiple RX/TX rings in multi channel mode
From: Sathya Perla @ 2011-10-24 12:45 UTC (permalink / raw)
  To: netdev; +Cc: Suresh Reddy
In-Reply-To: <1319460303-25560-1-git-send-email-sathya.perla@emulex.com>

When the HW is in multi-channel mode based on the skew/IPL, there are 4 functions per port and so not enough resources to create multiple RX/TX rings for each function.

Signed-off-by: Suresh Reddy <suresh.reddy@emulex.com>
Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
---
 drivers/net/ethernet/emulex/benet/be_cmds.h |    6 ++++++
 drivers/net/ethernet/emulex/benet/be_main.c |   16 ++++++++++++----
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index 75b7574..a35cd03 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -1046,6 +1046,12 @@ struct be_cmd_resp_modify_eq_delay {
 
 /******************** Get FW Config *******************/
 #define BE_FUNCTION_CAPS_RSS			0x2
+/* The HW can come up in either of the following multi-channel modes
+ * based on the skew/IPL.
+ */
+#define FLEX10_MODE				0x400
+#define VNIC_MODE				0x20000
+#define UMC_ENABLED				0x1000000
 struct be_cmd_req_query_fw_cfg {
 	struct be_cmd_req_hdr hdr;
 	u32 rsvd[31];
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 77ce24b..91fe12a 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -114,6 +114,13 @@ static const char * const ue_status_hi_desc[] = {
 	"Unknown"
 };
 
+/* Is BE in a multi-channel mode */
+static inline bool be_is_mc(struct be_adapter *adapter) {
+	return (adapter->function_mode & FLEX10_MODE ||
+		adapter->function_mode & VNIC_MODE ||
+		adapter->function_mode & UMC_ENABLED);
+}
+
 static void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q)
 {
 	struct be_dma_mem *mem = &q->dma_mem;
@@ -1289,7 +1296,7 @@ static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
 	if (rxcp->vlanf) {
 		/* vlanf could be wrongly set in some cards.
 		 * ignore if vtm is not set */
-		if ((adapter->function_mode & 0x400) && !rxcp->vtm)
+		if ((adapter->function_mode & FLEX10_MODE) && !rxcp->vtm)
 			rxcp->vlanf = 0;
 
 		if (!lancer_chip(adapter))
@@ -1636,7 +1643,7 @@ static void be_tx_queues_destroy(struct be_adapter *adapter)
 static int be_num_txqs_want(struct be_adapter *adapter)
 {
 	if ((num_vfs && adapter->sriov_enabled) ||
-		(adapter->function_mode & 0x400) ||
+		be_is_mc(adapter) ||
 		lancer_chip(adapter) || !be_physfn(adapter) ||
 		adapter->generation == BE_GEN2)
 		return 1;
@@ -1718,7 +1725,8 @@ static void be_rx_queues_destroy(struct be_adapter *adapter)
 static u32 be_num_rxqs_want(struct be_adapter *adapter)
 {
 	if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) &&
-		!adapter->sriov_enabled && !(adapter->function_mode & 0x400)) {
+		!adapter->sriov_enabled && be_physfn(adapter) &&
+		!be_is_mc(adapter)) {
 		return 1 + MAX_RSS_QS; /* one default non-RSS queue */
 	} else {
 		dev_warn(&adapter->pdev->dev,
@@ -3187,7 +3195,7 @@ static int be_get_config(struct be_adapter *adapter)
 	if (status)
 		return status;
 
-	if (adapter->function_mode & 0x400)
+	if (adapter->function_mode & FLEX10_MODE)
 		adapter->max_vlans = BE_NUM_VLANS_SUPPORTED/4;
 	else
 		adapter->max_vlans = BE_NUM_VLANS_SUPPORTED;
-- 
1.7.4

^ 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