net: Backport team driver from Linux 3.5-rc1
svn path=/dists/sid/linux/; revision=19094
This commit is contained in:
parent
5ba1dec55d
commit
dbf716c798
|
@ -19,6 +19,8 @@ linux (3.2.19-2) UNRELEASED; urgency=low
|
|||
* Convert patch system to quilt, except for the 'orig' patch series
|
||||
* udeb: Build-Depend on kernel-wedge >= 2.84; this allows us to list
|
||||
modules as required even if they are built-in in some configurations
|
||||
* filter: Allow to create sk-unattached filters
|
||||
* net: Backport team driver from Linux 3.5-rc1
|
||||
|
||||
-- Ben Hutchings <ben@decadent.org.uk> Sat, 02 Jun 2012 20:31:53 +0100
|
||||
|
||||
|
|
|
@ -2075,6 +2075,14 @@ CONFIG_SLIP_COMPRESSED=y
|
|||
CONFIG_SLIP_SMART=y
|
||||
CONFIG_SLIP_MODE_SLIP6=y
|
||||
|
||||
##
|
||||
## file: drivers/net/team/Kconfig
|
||||
##
|
||||
CONFIG_NET_TEAM=m
|
||||
CONFIG_NET_TEAM_MODE_ROUNDROBIN=m
|
||||
CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m
|
||||
CONFIG_NET_TEAM_MODE_LOADBALANCE=m
|
||||
|
||||
##
|
||||
## file: drivers/net/tokenring/Kconfig
|
||||
##
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
From: Ben Hutchings <ben@decadent.org.uk>
|
||||
Subject: Define netdev_features_t
|
||||
|
||||
This was done in upstream commit
|
||||
c8f44affb7244f2ac3e703cab13d55ede27621bb, but we just want the type
|
||||
definition for use by backported drivers rather than changing
|
||||
everything to use it.
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -950,6 +950,8 @@
|
||||
u32 features);
|
||||
};
|
||||
|
||||
+typedef u32 netdev_features_t;
|
||||
+
|
||||
/*
|
||||
* The DEVICE structure.
|
||||
* Actually, this whole structure is a big mistake. It mixes I/O
|
120
debian/patches/features/all/filter-Allow-to-create-sk-unattached-filters.patch
vendored
Normal file
120
debian/patches/features/all/filter-Allow-to-create-sk-unattached-filters.patch
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Sat, 31 Mar 2012 11:01:19 +0000
|
||||
Subject: filter: Allow to create sk-unattached filters
|
||||
|
||||
commit 302d663740cfaf2c364df6bb61cd339014ed714c upstream.
|
||||
|
||||
Today, BPF filters are bind to sockets. Since BPF machine becomes handy
|
||||
for other purposes, this patch allows to create unattached filter.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
include/linux/filter.h | 3 +++
|
||||
net/core/filter.c | 66 +++++++++++++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 65 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/include/linux/filter.h b/include/linux/filter.h
|
||||
index 8eeb205..92dd993 100644
|
||||
--- a/include/linux/filter.h
|
||||
+++ b/include/linux/filter.h
|
||||
@@ -153,6 +153,9 @@ static inline unsigned int sk_filter_len(const struct sk_filter *fp)
|
||||
extern int sk_filter(struct sock *sk, struct sk_buff *skb);
|
||||
extern unsigned int sk_run_filter(const struct sk_buff *skb,
|
||||
const struct sock_filter *filter);
|
||||
+extern int sk_unattached_filter_create(struct sk_filter **pfp,
|
||||
+ struct sock_fprog *fprog);
|
||||
+extern void sk_unattached_filter_destroy(struct sk_filter *fp);
|
||||
extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
|
||||
extern int sk_detach_filter(struct sock *sk);
|
||||
extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
|
||||
diff --git a/net/core/filter.c b/net/core/filter.c
|
||||
index 5dea452..cfbea88 100644
|
||||
--- a/net/core/filter.c
|
||||
+++ b/net/core/filter.c
|
||||
@@ -587,6 +587,67 @@ void sk_filter_release_rcu(struct rcu_head *rcu)
|
||||
}
|
||||
EXPORT_SYMBOL(sk_filter_release_rcu);
|
||||
|
||||
+static int __sk_prepare_filter(struct sk_filter *fp)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ fp->bpf_func = sk_run_filter;
|
||||
+
|
||||
+ err = sk_chk_filter(fp->insns, fp->len);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ bpf_jit_compile(fp);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * sk_unattached_filter_create - create an unattached filter
|
||||
+ * @fprog: the filter program
|
||||
+ * @sk: the socket to use
|
||||
+ *
|
||||
+ * Create a filter independent ofr any socket. We first run some
|
||||
+ * sanity checks on it to make sure it does not explode on us later.
|
||||
+ * If an error occurs or there is insufficient memory for the filter
|
||||
+ * a negative errno code is returned. On success the return is zero.
|
||||
+ */
|
||||
+int sk_unattached_filter_create(struct sk_filter **pfp,
|
||||
+ struct sock_fprog *fprog)
|
||||
+{
|
||||
+ struct sk_filter *fp;
|
||||
+ unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
|
||||
+ int err;
|
||||
+
|
||||
+ /* Make sure new filter is there and in the right amounts. */
|
||||
+ if (fprog->filter == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL);
|
||||
+ if (!fp)
|
||||
+ return -ENOMEM;
|
||||
+ memcpy(fp->insns, fprog->filter, fsize);
|
||||
+
|
||||
+ atomic_set(&fp->refcnt, 1);
|
||||
+ fp->len = fprog->len;
|
||||
+
|
||||
+ err = __sk_prepare_filter(fp);
|
||||
+ if (err)
|
||||
+ goto free_mem;
|
||||
+
|
||||
+ *pfp = fp;
|
||||
+ return 0;
|
||||
+free_mem:
|
||||
+ kfree(fp);
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(sk_unattached_filter_create);
|
||||
+
|
||||
+void sk_unattached_filter_destroy(struct sk_filter *fp)
|
||||
+{
|
||||
+ sk_filter_release(fp);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy);
|
||||
+
|
||||
/**
|
||||
* sk_attach_filter - attach a socket filter
|
||||
* @fprog: the filter program
|
||||
@@ -617,16 +678,13 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
|
||||
|
||||
atomic_set(&fp->refcnt, 1);
|
||||
fp->len = fprog->len;
|
||||
- fp->bpf_func = sk_run_filter;
|
||||
|
||||
- err = sk_chk_filter(fp->insns, fp->len);
|
||||
+ err = __sk_prepare_filter(fp);
|
||||
if (err) {
|
||||
sk_filter_uncharge(sk, fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
- bpf_jit_compile(fp);
|
||||
-
|
||||
old_fp = rcu_dereference_protected(sk->sk_filter,
|
||||
sock_owned_by_user(sk));
|
||||
rcu_assign_pointer(sk->sk_filter, fp);
|
2322
debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch
vendored
Normal file
2322
debian/patches/features/all/team/0001-net-introduce-ethernet-teaming-device.patch
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
vendored
Normal file
49
debian/patches/features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Wed, 16 Nov 2011 11:55:54 +0000
|
||||
Subject: [02/23] team: Do not hold rcu_read_lock when running netlink cmds
|
||||
|
||||
commit 8c0713a57482ebfadef197c856a38af3a55444c6 upstream.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 11 +++++------
|
||||
1 file changed, 5 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 60672bb..e5390c7 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -1043,8 +1043,7 @@ err_msg_put:
|
||||
|
||||
/*
|
||||
* Netlink cmd functions should be locked by following two functions.
|
||||
- * To ensure team_uninit would not be called in between, hold rcu_read_lock
|
||||
- * all the time.
|
||||
+ * Since dev gets held here, that ensures dev won't disappear in between.
|
||||
*/
|
||||
static struct team *team_nl_team_get(struct genl_info *info)
|
||||
{
|
||||
@@ -1057,10 +1056,10 @@ static struct team *team_nl_team_get(struct genl_info *info)
|
||||
return NULL;
|
||||
|
||||
ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
|
||||
- rcu_read_lock();
|
||||
- dev = dev_get_by_index_rcu(net, ifindex);
|
||||
+ dev = dev_get_by_index(net, ifindex);
|
||||
if (!dev || dev->netdev_ops != &team_netdev_ops) {
|
||||
- rcu_read_unlock();
|
||||
+ if (dev)
|
||||
+ dev_put(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1072,7 +1071,7 @@ static struct team *team_nl_team_get(struct genl_info *info)
|
||||
static void team_nl_team_put(struct team *team)
|
||||
{
|
||||
spin_unlock(&team->lock);
|
||||
- rcu_read_unlock();
|
||||
+ dev_put(team->dev);
|
||||
}
|
||||
|
||||
static int team_nl_send_generic(struct genl_info *info, struct team *team,
|
149
debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
vendored
Normal file
149
debian/patches/features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Wed, 16 Nov 2011 11:09:08 +0000
|
||||
Subject: [03/23] team: convert overall spinlock to mutex
|
||||
|
||||
commit 61dc3461b9549bc10a2f16d254250680cadafcce upstream.
|
||||
|
||||
No need to have spinlock for this purpose. So convert this to mutex and
|
||||
avoid current schedule while atomic problems in netlink code.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 32 ++++++++++++++++----------------
|
||||
include/linux/if_team.h | 2 +-
|
||||
2 files changed, 17 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index e5390c7..7db219c 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -443,9 +443,9 @@ static void __team_compute_features(struct team *team)
|
||||
|
||||
static void team_compute_features(struct team *team)
|
||||
{
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
__team_compute_features(team);
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
}
|
||||
|
||||
static int team_port_enter(struct team *team, struct team_port *port)
|
||||
@@ -647,7 +647,7 @@ static int team_init(struct net_device *dev)
|
||||
int i;
|
||||
|
||||
team->dev = dev;
|
||||
- spin_lock_init(&team->lock);
|
||||
+ mutex_init(&team->lock);
|
||||
|
||||
team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
|
||||
if (!team->pcpu_stats)
|
||||
@@ -672,13 +672,13 @@ static void team_uninit(struct net_device *dev)
|
||||
struct team_port *port;
|
||||
struct team_port *tmp;
|
||||
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
list_for_each_entry_safe(port, tmp, &team->port_list, list)
|
||||
team_port_del(team, port->dev);
|
||||
|
||||
__team_change_mode(team, NULL); /* cleanup */
|
||||
__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
}
|
||||
|
||||
static void team_destructor(struct net_device *dev)
|
||||
@@ -784,7 +784,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
|
||||
* Alhough this is reader, it's guarded by team lock. It's not possible
|
||||
* to traverse list in reverse under rcu_read_lock
|
||||
*/
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
list_for_each_entry(port, &team->port_list, list) {
|
||||
err = dev_set_mtu(port->dev, new_mtu);
|
||||
if (err) {
|
||||
@@ -793,7 +793,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
|
||||
goto unwind;
|
||||
}
|
||||
}
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
|
||||
dev->mtu = new_mtu;
|
||||
|
||||
@@ -802,7 +802,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
|
||||
unwind:
|
||||
list_for_each_entry_continue_reverse(port, &team->port_list, list)
|
||||
dev_set_mtu(port->dev, dev->mtu);
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -880,9 +880,9 @@ static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
struct team *team = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
err = team_port_add(team, port_dev);
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -891,9 +891,9 @@ static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
struct team *team = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
err = team_port_del(team, port_dev);
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1064,13 +1064,13 @@ static struct team *team_nl_team_get(struct genl_info *info)
|
||||
}
|
||||
|
||||
team = netdev_priv(dev);
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
return team;
|
||||
}
|
||||
|
||||
static void team_nl_team_put(struct team *team)
|
||||
{
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
dev_put(team->dev);
|
||||
}
|
||||
|
||||
@@ -1486,9 +1486,9 @@ static void team_port_change_check(struct team_port *port, bool linkup)
|
||||
{
|
||||
struct team *team = port->team;
|
||||
|
||||
- spin_lock(&team->lock);
|
||||
+ mutex_lock(&team->lock);
|
||||
__team_port_change_check(port, linkup);
|
||||
- spin_unlock(&team->lock);
|
||||
+ mutex_unlock(&team->lock);
|
||||
}
|
||||
|
||||
/************************************
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 14f6388..a6eac12 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -92,7 +92,7 @@ struct team {
|
||||
struct net_device *dev; /* associated netdevice */
|
||||
struct team_pcpu_stats __percpu *pcpu_stats;
|
||||
|
||||
- spinlock_t lock; /* used for overall locking, e.g. port lists write */
|
||||
+ struct mutex lock; /* used for overall locking, e.g. port lists write */
|
||||
|
||||
/*
|
||||
* port lists with port count
|
189
debian/patches/features/all/team/0004-team-replicate-options-on-register.patch
vendored
Normal file
189
debian/patches/features/all/team/0004-team-replicate-options-on-register.patch
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Wed, 16 Nov 2011 11:09:09 +0000
|
||||
Subject: [04/23] team: replicate options on register
|
||||
|
||||
commit 358b838291f618278080bbed435b755f9b46748e upstream.
|
||||
|
||||
Since multiple team instances are putting defined options into their
|
||||
option list, during register each option must be cloned before added
|
||||
into list. This resolves uncool memory corruptions when using multiple
|
||||
teams.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 76 +++++++++++++++++++++++++----
|
||||
drivers/net/team/team_mode_activebackup.c | 5 +-
|
||||
include/linux/if_team.h | 8 +--
|
||||
3 files changed, 72 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 7db219c..f309274 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -80,30 +80,78 @@ EXPORT_SYMBOL(team_port_set_team_mac);
|
||||
* Options handling
|
||||
*******************/
|
||||
|
||||
-void team_options_register(struct team *team, struct team_option *option,
|
||||
- size_t option_count)
|
||||
+struct team_option *__team_find_option(struct team *team, const char *opt_name)
|
||||
+{
|
||||
+ struct team_option *option;
|
||||
+
|
||||
+ list_for_each_entry(option, &team->option_list, list) {
|
||||
+ if (strcmp(option->name, opt_name) == 0)
|
||||
+ return option;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+int team_options_register(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count)
|
||||
{
|
||||
int i;
|
||||
+ struct team_option *dst_opts[option_count];
|
||||
+ int err;
|
||||
+
|
||||
+ memset(dst_opts, 0, sizeof(dst_opts));
|
||||
+ for (i = 0; i < option_count; i++, option++) {
|
||||
+ struct team_option *dst_opt;
|
||||
+
|
||||
+ if (__team_find_option(team, option->name)) {
|
||||
+ err = -EEXIST;
|
||||
+ goto rollback;
|
||||
+ }
|
||||
+ dst_opt = kmalloc(sizeof(*option), GFP_KERNEL);
|
||||
+ if (!dst_opt) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto rollback;
|
||||
+ }
|
||||
+ memcpy(dst_opt, option, sizeof(*option));
|
||||
+ dst_opts[i] = dst_opt;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < option_count; i++)
|
||||
+ list_add_tail(&dst_opts[i]->list, &team->option_list);
|
||||
|
||||
- for (i = 0; i < option_count; i++, option++)
|
||||
- list_add_tail(&option->list, &team->option_list);
|
||||
+ return 0;
|
||||
+
|
||||
+rollback:
|
||||
+ for (i = 0; i < option_count; i++)
|
||||
+ kfree(dst_opts[i]);
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
+
|
||||
EXPORT_SYMBOL(team_options_register);
|
||||
|
||||
static void __team_options_change_check(struct team *team,
|
||||
struct team_option *changed_option);
|
||||
|
||||
static void __team_options_unregister(struct team *team,
|
||||
- struct team_option *option,
|
||||
+ const struct team_option *option,
|
||||
size_t option_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < option_count; i++, option++)
|
||||
- list_del(&option->list);
|
||||
+ for (i = 0; i < option_count; i++, option++) {
|
||||
+ struct team_option *del_opt;
|
||||
+
|
||||
+ del_opt = __team_find_option(team, option->name);
|
||||
+ if (del_opt) {
|
||||
+ list_del(&del_opt->list);
|
||||
+ kfree(del_opt);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
-void team_options_unregister(struct team *team, struct team_option *option,
|
||||
+void team_options_unregister(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
size_t option_count)
|
||||
{
|
||||
__team_options_unregister(team, option, option_count);
|
||||
@@ -632,7 +680,7 @@ static int team_mode_option_set(struct team *team, void *arg)
|
||||
return team_change_mode(team, *str);
|
||||
}
|
||||
|
||||
-static struct team_option team_options[] = {
|
||||
+static const struct team_option team_options[] = {
|
||||
{
|
||||
.name = "mode",
|
||||
.type = TEAM_OPTION_TYPE_STRING,
|
||||
@@ -645,6 +693,7 @@ static int team_init(struct net_device *dev)
|
||||
{
|
||||
struct team *team = netdev_priv(dev);
|
||||
int i;
|
||||
+ int err;
|
||||
|
||||
team->dev = dev;
|
||||
mutex_init(&team->lock);
|
||||
@@ -660,10 +709,17 @@ static int team_init(struct net_device *dev)
|
||||
team_adjust_ops(team);
|
||||
|
||||
INIT_LIST_HEAD(&team->option_list);
|
||||
- team_options_register(team, team_options, ARRAY_SIZE(team_options));
|
||||
+ err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
|
||||
+ if (err)
|
||||
+ goto err_options_register;
|
||||
netif_carrier_off(dev);
|
||||
|
||||
return 0;
|
||||
+
|
||||
+err_options_register:
|
||||
+ free_percpu(team->pcpu_stats);
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static void team_uninit(struct net_device *dev)
|
||||
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
|
||||
index 6fe920c..b344275 100644
|
||||
--- a/drivers/net/team/team_mode_activebackup.c
|
||||
+++ b/drivers/net/team/team_mode_activebackup.c
|
||||
@@ -83,7 +83,7 @@ static int ab_active_port_set(struct team *team, void *arg)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
-static struct team_option ab_options[] = {
|
||||
+static const struct team_option ab_options[] = {
|
||||
{
|
||||
.name = "activeport",
|
||||
.type = TEAM_OPTION_TYPE_U32,
|
||||
@@ -94,8 +94,7 @@ static struct team_option ab_options[] = {
|
||||
|
||||
int ab_init(struct team *team)
|
||||
{
|
||||
- team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
|
||||
- return 0;
|
||||
+ return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
|
||||
}
|
||||
|
||||
void ab_exit(struct team *team)
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index a6eac12..828181f 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -140,11 +140,11 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
|
||||
}
|
||||
|
||||
extern int team_port_set_team_mac(struct team_port *port);
|
||||
-extern void team_options_register(struct team *team,
|
||||
- struct team_option *option,
|
||||
- size_t option_count);
|
||||
+extern int team_options_register(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count);
|
||||
extern void team_options_unregister(struct team *team,
|
||||
- struct team_option *option,
|
||||
+ const struct team_option *option,
|
||||
size_t option_count);
|
||||
extern int team_mode_register(struct team_mode *mode);
|
||||
extern int team_mode_unregister(struct team_mode *mode);
|
|
@ -0,0 +1,54 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 17 Nov 2011 04:16:04 +0000
|
||||
Subject: [05/23] team: add fix_features
|
||||
|
||||
commit 234a8fd49d086f5a3debb379bfdf4e6b51f0c0e2 upstream.
|
||||
|
||||
do fix features in similar way as bonding code does
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index f309274..5b169c1 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -953,6 +953,27 @@ static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
|
||||
return err;
|
||||
}
|
||||
|
||||
+static netdev_features_t team_fix_features(struct net_device *dev,
|
||||
+ netdev_features_t features)
|
||||
+{
|
||||
+ struct team_port *port;
|
||||
+ struct team *team = netdev_priv(dev);
|
||||
+ netdev_features_t mask;
|
||||
+
|
||||
+ mask = features;
|
||||
+ features &= ~NETIF_F_ONE_FOR_ALL;
|
||||
+ features |= NETIF_F_ALL_FOR_ALL;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+ list_for_each_entry_rcu(port, &team->port_list, list) {
|
||||
+ features = netdev_increment_features(features,
|
||||
+ port->dev->features,
|
||||
+ mask);
|
||||
+ }
|
||||
+ rcu_read_unlock();
|
||||
+ return features;
|
||||
+}
|
||||
+
|
||||
static const struct net_device_ops team_netdev_ops = {
|
||||
.ndo_init = team_init,
|
||||
.ndo_uninit = team_uninit,
|
||||
@@ -968,6 +989,7 @@ static const struct net_device_ops team_netdev_ops = {
|
||||
.ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid,
|
||||
.ndo_add_slave = team_add_slave,
|
||||
.ndo_del_slave = team_del_slave,
|
||||
+ .ndo_fix_features = team_fix_features,
|
||||
};
|
||||
|
||||
|
50
debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch
vendored
Normal file
50
debian/patches/features/all/team/0006-team-avoid-using-variable-length-array.patch
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 17 Nov 2011 04:16:05 +0000
|
||||
Subject: [06/23] team: avoid using variable-length array
|
||||
|
||||
commit 2bba19fff8d09bf19df5d5e2de7188d65de67c3e upstream.
|
||||
|
||||
Apparently using variable-length array is not correct
|
||||
(https://lkml.org/lkml/2011/10/23/25). So remove it.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 5b169c1..c48ef19 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -96,10 +96,13 @@ int team_options_register(struct team *team,
|
||||
size_t option_count)
|
||||
{
|
||||
int i;
|
||||
- struct team_option *dst_opts[option_count];
|
||||
+ struct team_option **dst_opts;
|
||||
int err;
|
||||
|
||||
- memset(dst_opts, 0, sizeof(dst_opts));
|
||||
+ dst_opts = kzalloc(sizeof(struct team_option *) * option_count,
|
||||
+ GFP_KERNEL);
|
||||
+ if (!dst_opts)
|
||||
+ return -ENOMEM;
|
||||
for (i = 0; i < option_count; i++, option++) {
|
||||
struct team_option *dst_opt;
|
||||
|
||||
@@ -119,12 +122,14 @@ int team_options_register(struct team *team,
|
||||
for (i = 0; i < option_count; i++)
|
||||
list_add_tail(&dst_opts[i]->list, &team->option_list);
|
||||
|
||||
+ kfree(dst_opts);
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
for (i = 0; i < option_count; i++)
|
||||
kfree(dst_opts[i]);
|
||||
|
||||
+ kfree(dst_opts);
|
||||
return err;
|
||||
}
|
||||
|
38
debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
vendored
Normal file
38
debian/patches/features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 17 Nov 2011 06:32:37 +0000
|
||||
Subject: [07/23] team: replace kmalloc+memcpy by kmemdup
|
||||
|
||||
commit f8a15af093b19b86d56933c8757cee298d0f32a8 upstream.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 8 ++------
|
||||
1 file changed, 2 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index c48ef19..064155d 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -104,19 +104,15 @@ int team_options_register(struct team *team,
|
||||
if (!dst_opts)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < option_count; i++, option++) {
|
||||
- struct team_option *dst_opt;
|
||||
-
|
||||
if (__team_find_option(team, option->name)) {
|
||||
err = -EEXIST;
|
||||
goto rollback;
|
||||
}
|
||||
- dst_opt = kmalloc(sizeof(*option), GFP_KERNEL);
|
||||
- if (!dst_opt) {
|
||||
+ dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
|
||||
+ if (!dst_opts[i]) {
|
||||
err = -ENOMEM;
|
||||
goto rollback;
|
||||
}
|
||||
- memcpy(dst_opt, option, sizeof(*option));
|
||||
- dst_opts[i] = dst_opt;
|
||||
}
|
||||
|
||||
for (i = 0; i < option_count; i++)
|
28
debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
vendored
Normal file
28
debian/patches/features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
From: Eric Dumazet <eric.dumazet@gmail.com>
|
||||
Date: Wed, 23 Nov 2011 07:09:32 +0000
|
||||
Subject: [08/23] net: treewide use of RCU_INIT_POINTER
|
||||
|
||||
commit 2cfa5a0471fef43fda0b7bd87e3a5e4dbadb7809 upstream.
|
||||
|
||||
rcu_assign_pointer(ptr, NULL) can be safely replaced by
|
||||
RCU_INIT_POINTER(ptr, NULL)
|
||||
|
||||
(old rcu_assign_pointer() macro was testing the NULL value and could
|
||||
omit the smp_wmb(), but this had to be removed because of compiler
|
||||
warnings)
|
||||
|
||||
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
[bwh: Restrict to drivers/net/team/]
|
||||
---
|
||||
--- a/drivers/net/team/team_mode_activebackup.c
|
||||
+++ b/drivers/net/team/team_mode_activebackup.c
|
||||
@@ -56,7 +56,7 @@ drop:
|
||||
static void ab_port_leave(struct team *team, struct team_port *port)
|
||||
{
|
||||
if (ab_priv(team)->active_port == port)
|
||||
- rcu_assign_pointer(ab_priv(team)->active_port, NULL);
|
||||
+ RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
|
||||
}
|
||||
|
||||
static int ab_active_port_get(struct team *team, void *arg)
|
132
debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
vendored
Normal file
132
debian/patches/features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 8 Dec 2011 04:11:17 +0000
|
||||
Subject: [09/23] net: introduce vlan_vid_[add/del] and use them instead of
|
||||
direct [add/kill]_vid ndo calls
|
||||
|
||||
commit 87002b03baabd2b8f6281ab6411ed88d24958de1 upstream.
|
||||
|
||||
This patch adds wrapper for ndo_vlan_rx_add_vid/ndo_vlan_rx_kill_vid
|
||||
functions. Check for NETIF_F_HW_VLAN_FILTER feature is done in this
|
||||
wrapper.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
[bwh: Drop changes to bonding, macvlan, vlan; assume
|
||||
ndo_vlan_rx_{add,kill}_vid still return void]
|
||||
---
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/netdevice.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/etherdevice.h>
|
||||
@@ -906,15 +907,26 @@
|
||||
{
|
||||
struct team *team = netdev_priv(dev);
|
||||
struct team_port *port;
|
||||
+ int err;
|
||||
|
||||
- rcu_read_lock();
|
||||
- list_for_each_entry_rcu(port, &team->port_list, list) {
|
||||
- const struct net_device_ops *ops = port->dev->netdev_ops;
|
||||
-
|
||||
- if (ops->ndo_vlan_rx_add_vid)
|
||||
- ops->ndo_vlan_rx_add_vid(port->dev, vid);
|
||||
+ /*
|
||||
+ * Alhough this is reader, it's guarded by team lock. It's not possible
|
||||
+ * to traverse list in reverse under rcu_read_lock
|
||||
+ */
|
||||
+ mutex_lock(&team->lock);
|
||||
+ list_for_each_entry(port, &team->port_list, list) {
|
||||
+ err = vlan_vid_add(port->dev, vid);
|
||||
+ if (err)
|
||||
+ goto unwind;
|
||||
}
|
||||
- rcu_read_unlock();
|
||||
+ mutex_unlock(&team->lock);
|
||||
+
|
||||
+ return;
|
||||
+
|
||||
+unwind:
|
||||
+ list_for_each_entry_continue_reverse(port, &team->port_list, list)
|
||||
+ vlan_vid_del(port->dev, vid);
|
||||
+ mutex_unlock(&team->lock);
|
||||
}
|
||||
|
||||
static void team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
|
||||
@@ -923,12 +935,8 @@
|
||||
struct team_port *port;
|
||||
|
||||
rcu_read_lock();
|
||||
- list_for_each_entry_rcu(port, &team->port_list, list) {
|
||||
- const struct net_device_ops *ops = port->dev->netdev_ops;
|
||||
-
|
||||
- if (ops->ndo_vlan_rx_kill_vid)
|
||||
- ops->ndo_vlan_rx_kill_vid(port->dev, vid);
|
||||
- }
|
||||
+ list_for_each_entry_rcu(port, &team->port_list, list)
|
||||
+ vlan_vid_del(port->dev, vid);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
--- a/include/linux/if_vlan.h
|
||||
+++ b/include/linux/if_vlan.h
|
||||
@@ -109,6 +109,9 @@
|
||||
extern bool vlan_do_receive(struct sk_buff **skb, bool last_handler);
|
||||
extern struct sk_buff *vlan_untag(struct sk_buff *skb);
|
||||
|
||||
+extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
|
||||
+extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
|
||||
+
|
||||
#else
|
||||
static inline struct net_device *
|
||||
__vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id)
|
||||
@@ -139,6 +142,15 @@
|
||||
{
|
||||
return skb;
|
||||
}
|
||||
+
|
||||
+static inline int vlan_vid_add(struct net_device *dev, unsigned short vid)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline void vlan_vid_del(struct net_device *dev, unsigned short vid)
|
||||
+{
|
||||
+}
|
||||
#endif
|
||||
|
||||
/**
|
||||
--- a/net/8021q/vlan_core.c
|
||||
+++ b/net/8021q/vlan_core.c
|
||||
@@ -179,3 +179,26 @@
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
+
|
||||
+int vlan_vid_add(struct net_device *dev, unsigned short vid)
|
||||
+{
|
||||
+ const struct net_device_ops *ops = dev->netdev_ops;
|
||||
+
|
||||
+ if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
||||
+ ops->ndo_vlan_rx_add_vid) {
|
||||
+ ops->ndo_vlan_rx_add_vid(dev, vid);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(vlan_vid_add);
|
||||
+
|
||||
+void vlan_vid_del(struct net_device *dev, unsigned short vid)
|
||||
+{
|
||||
+ const struct net_device_ops *ops = dev->netdev_ops;
|
||||
+
|
||||
+ if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
|
||||
+ ops->ndo_vlan_rx_kill_vid) {
|
||||
+ ops->ndo_vlan_rx_kill_vid(dev, vid);
|
||||
+ }
|
||||
+}
|
||||
+EXPORT_SYMBOL(vlan_vid_del);
|
81
debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
vendored
Normal file
81
debian/patches/features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 8 Dec 2011 04:11:19 +0000
|
||||
Subject: [10/23] vlan: introduce functions to do mass addition/deletion of vids by
|
||||
another device
|
||||
|
||||
commit 348a1443cc4303c72cf1ee3b26e476fec8e7b5fa upstream.
|
||||
|
||||
Introduce functions handy to copy vlan ids from one driver's list to
|
||||
another.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
[bwh: Assume ndo_vlan_rx_add_id still returns void]
|
||||
---
|
||||
--- a/include/linux/if_vlan.h
|
||||
+++ b/include/linux/if_vlan.h
|
||||
@@ -112,6 +112,10 @@
|
||||
extern int vlan_vid_add(struct net_device *dev, unsigned short vid);
|
||||
extern void vlan_vid_del(struct net_device *dev, unsigned short vid);
|
||||
|
||||
+extern int vlan_vids_add_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev);
|
||||
+extern void vlan_vids_del_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev);
|
||||
#else
|
||||
static inline struct net_device *
|
||||
__vlan_find_dev_deep(struct net_device *real_dev, u16 vlan_id)
|
||||
@@ -151,6 +155,16 @@
|
||||
static inline void vlan_vid_del(struct net_device *dev, unsigned short vid)
|
||||
{
|
||||
}
|
||||
+
|
||||
+static inline int vlan_vids_add_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static inline void vlan_vids_del_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev)
|
||||
+{
|
||||
+}
|
||||
#endif
|
||||
|
||||
/**
|
||||
--- a/net/8021q/vlan_core.c
|
||||
+++ b/net/8021q/vlan_core.c
|
||||
@@ -202,3 +202,34 @@
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(vlan_vid_del);
|
||||
+
|
||||
+int vlan_vids_add_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev)
|
||||
+{
|
||||
+ struct vlan_vid_info *vid_info;
|
||||
+
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ if (!by_dev->vlan_info)
|
||||
+ return;
|
||||
+
|
||||
+ list_for_each_entry(vid_info, &by_dev->vlan_info->vid_list, list) {
|
||||
+ vlan_vid_add(dev, vid_info->vid);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(vlan_vids_add_by_dev);
|
||||
+
|
||||
+void vlan_vids_del_by_dev(struct net_device *dev,
|
||||
+ const struct net_device *by_dev)
|
||||
+{
|
||||
+ struct vlan_vid_info *vid_info;
|
||||
+
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ if (!by_dev->vlan_info)
|
||||
+ return;
|
||||
+
|
||||
+ list_for_each_entry(vid_info, &by_dev->vlan_info->vid_list, list)
|
||||
+ vlan_vid_del(dev, vid_info->vid);
|
||||
+}
|
53
debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
vendored
Normal file
53
debian/patches/features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Thu, 8 Dec 2011 04:11:20 +0000
|
||||
Subject: [11/23] team: use vlan_vids_[addr/del]_by_dev
|
||||
|
||||
commit 57459185a19b0246866479522b77cbb9732201d1 upstream.
|
||||
|
||||
So far when vlan id was added to team device befor port was added, this
|
||||
vid was not added to port's vlan filter. Also after removal, vid stayed
|
||||
in port device's vlan filter. Benefit of new vlan functions to handle
|
||||
this work.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 79c2d1b..ed2a862 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -588,6 +588,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
||||
goto err_dev_open;
|
||||
}
|
||||
|
||||
+ err = vlan_vids_add_by_dev(port_dev, dev);
|
||||
+ if (err) {
|
||||
+ netdev_err(dev, "Failed to add vlan ids to device %s\n",
|
||||
+ portname);
|
||||
+ goto err_vids_add;
|
||||
+ }
|
||||
+
|
||||
err = netdev_set_master(port_dev, dev);
|
||||
if (err) {
|
||||
netdev_err(dev, "Device %s failed to set master\n", portname);
|
||||
@@ -615,6 +622,9 @@ err_handler_register:
|
||||
netdev_set_master(port_dev, NULL);
|
||||
|
||||
err_set_master:
|
||||
+ vlan_vids_del_by_dev(port_dev, dev);
|
||||
+
|
||||
+err_vids_add:
|
||||
dev_close(port_dev);
|
||||
|
||||
err_dev_open:
|
||||
@@ -648,6 +658,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
||||
team_adjust_ops(team);
|
||||
netdev_rx_handler_unregister(port_dev);
|
||||
netdev_set_master(port_dev, NULL);
|
||||
+ vlan_vids_del_by_dev(port_dev, dev);
|
||||
dev_close(port_dev);
|
||||
team_port_leave(team, port);
|
||||
team_port_set_orig_mac(port);
|
371
debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
vendored
Normal file
371
debian/patches/features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
vendored
Normal file
|
@ -0,0 +1,371 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 24 Jan 2012 05:16:00 +0000
|
||||
Subject: [12/23] team: send only changed options/ports via netlink
|
||||
|
||||
commit b82b9183d4f18f9b8c4bb31f223eb6c79b734eb0 upstream.
|
||||
|
||||
This patch changes event message behaviour to send only updated records
|
||||
instead of whole list. This fixes bug on which userspace receives non-actual
|
||||
data in case multiple events occur in row.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 136 +++++++++++++++++++++++++++++++----------------
|
||||
include/linux/if_team.h | 10 ++++
|
||||
2 files changed, 100 insertions(+), 46 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index ed2a862..6b678f3 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-int team_options_register(struct team *team,
|
||||
- const struct team_option *option,
|
||||
- size_t option_count)
|
||||
+int __team_options_register(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count)
|
||||
{
|
||||
int i;
|
||||
struct team_option **dst_opts;
|
||||
@@ -116,8 +116,11 @@ int team_options_register(struct team *team,
|
||||
}
|
||||
}
|
||||
|
||||
- for (i = 0; i < option_count; i++)
|
||||
+ for (i = 0; i < option_count; i++) {
|
||||
+ dst_opts[i]->changed = true;
|
||||
+ dst_opts[i]->removed = false;
|
||||
list_add_tail(&dst_opts[i]->list, &team->option_list);
|
||||
+ }
|
||||
|
||||
kfree(dst_opts);
|
||||
return 0;
|
||||
@@ -130,10 +133,22 @@ rollback:
|
||||
return err;
|
||||
}
|
||||
|
||||
-EXPORT_SYMBOL(team_options_register);
|
||||
+static void __team_options_mark_removed(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < option_count; i++, option++) {
|
||||
+ struct team_option *del_opt;
|
||||
|
||||
-static void __team_options_change_check(struct team *team,
|
||||
- struct team_option *changed_option);
|
||||
+ del_opt = __team_find_option(team, option->name);
|
||||
+ if (del_opt) {
|
||||
+ del_opt->changed = true;
|
||||
+ del_opt->removed = true;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
static void __team_options_unregister(struct team *team,
|
||||
const struct team_option *option,
|
||||
@@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team,
|
||||
}
|
||||
}
|
||||
|
||||
+static void __team_options_change_check(struct team *team);
|
||||
+
|
||||
+int team_options_register(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ err = __team_options_register(team, option, option_count);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ __team_options_change_check(team);
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(team_options_register);
|
||||
+
|
||||
void team_options_unregister(struct team *team,
|
||||
const struct team_option *option,
|
||||
size_t option_count)
|
||||
{
|
||||
+ __team_options_mark_removed(team, option, option_count);
|
||||
+ __team_options_change_check(team);
|
||||
__team_options_unregister(team, option, option_count);
|
||||
- __team_options_change_check(team, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(team_options_unregister);
|
||||
|
||||
@@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- __team_options_change_check(team, option);
|
||||
+ option->changed = true;
|
||||
+ __team_options_change_check(team);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
+ port->removed = true;
|
||||
__team_port_change_check(port, false);
|
||||
team_port_list_del_port(team, port);
|
||||
team_adjust_ops(team);
|
||||
@@ -1200,10 +1234,9 @@ err_fill:
|
||||
return err;
|
||||
}
|
||||
|
||||
-static int team_nl_fill_options_get_changed(struct sk_buff *skb,
|
||||
- u32 pid, u32 seq, int flags,
|
||||
- struct team *team,
|
||||
- struct team_option *changed_option)
|
||||
+static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
+ u32 pid, u32 seq, int flags,
|
||||
+ struct team *team, bool fillall)
|
||||
{
|
||||
struct nlattr *option_list;
|
||||
void *hdr;
|
||||
@@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
|
||||
struct nlattr *option_item;
|
||||
long arg;
|
||||
|
||||
+ /* Include only changed options if fill all mode is not on */
|
||||
+ if (!fillall && !option->changed)
|
||||
+ continue;
|
||||
option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
|
||||
if (!option_item)
|
||||
goto nla_put_failure;
|
||||
NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
|
||||
- if (option == changed_option)
|
||||
+ if (option->changed) {
|
||||
NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
|
||||
+ option->changed = false;
|
||||
+ }
|
||||
+ if (option->removed)
|
||||
+ NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
|
||||
switch (option->type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
|
||||
@@ -1255,13 +1295,13 @@ nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
-static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
- struct genl_info *info, int flags,
|
||||
- struct team *team)
|
||||
+static int team_nl_fill_options_get_all(struct sk_buff *skb,
|
||||
+ struct genl_info *info, int flags,
|
||||
+ struct team *team)
|
||||
{
|
||||
- return team_nl_fill_options_get_changed(skb, info->snd_pid,
|
||||
- info->snd_seq, NLM_F_ACK,
|
||||
- team, NULL);
|
||||
+ return team_nl_fill_options_get(skb, info->snd_pid,
|
||||
+ info->snd_seq, NLM_F_ACK,
|
||||
+ team, true);
|
||||
}
|
||||
|
||||
static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
|
||||
@@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!team)
|
||||
return -EINVAL;
|
||||
|
||||
- err = team_nl_send_generic(info, team, team_nl_fill_options_get);
|
||||
+ err = team_nl_send_generic(info, team, team_nl_fill_options_get_all);
|
||||
|
||||
team_nl_team_put(team);
|
||||
|
||||
@@ -1365,10 +1405,10 @@ team_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
-static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
|
||||
- u32 pid, u32 seq, int flags,
|
||||
- struct team *team,
|
||||
- struct team_port *changed_port)
|
||||
+static int team_nl_fill_port_list_get(struct sk_buff *skb,
|
||||
+ u32 pid, u32 seq, int flags,
|
||||
+ struct team *team,
|
||||
+ bool fillall)
|
||||
{
|
||||
struct nlattr *port_list;
|
||||
void *hdr;
|
||||
@@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
|
||||
list_for_each_entry(port, &team->port_list, list) {
|
||||
struct nlattr *port_item;
|
||||
|
||||
+ /* Include only changed ports if fill all mode is not on */
|
||||
+ if (!fillall && !port->changed)
|
||||
+ continue;
|
||||
port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
|
||||
if (!port_item)
|
||||
goto nla_put_failure;
|
||||
NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
|
||||
- if (port == changed_port)
|
||||
+ if (port->changed) {
|
||||
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
|
||||
+ port->changed = false;
|
||||
+ }
|
||||
+ if (port->removed)
|
||||
+ NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
|
||||
if (port->linkup)
|
||||
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
|
||||
NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
|
||||
@@ -1408,13 +1455,13 @@ nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
-static int team_nl_fill_port_list_get(struct sk_buff *skb,
|
||||
- struct genl_info *info, int flags,
|
||||
- struct team *team)
|
||||
+static int team_nl_fill_port_list_get_all(struct sk_buff *skb,
|
||||
+ struct genl_info *info, int flags,
|
||||
+ struct team *team)
|
||||
{
|
||||
- return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
|
||||
- info->snd_seq, NLM_F_ACK,
|
||||
- team, NULL);
|
||||
+ return team_nl_fill_port_list_get(skb, info->snd_pid,
|
||||
+ info->snd_seq, NLM_F_ACK,
|
||||
+ team, true);
|
||||
}
|
||||
|
||||
static int team_nl_cmd_port_list_get(struct sk_buff *skb,
|
||||
@@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
|
||||
if (!team)
|
||||
return -EINVAL;
|
||||
|
||||
- err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
|
||||
+ err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all);
|
||||
|
||||
team_nl_team_put(team);
|
||||
|
||||
@@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = {
|
||||
.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
|
||||
};
|
||||
|
||||
-static int team_nl_send_event_options_get(struct team *team,
|
||||
- struct team_option *changed_option)
|
||||
+static int team_nl_send_event_options_get(struct team *team)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
@@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team,
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
- err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
|
||||
- changed_option);
|
||||
+ err = team_nl_fill_options_get(skb, 0, 0, 0, team, false);
|
||||
if (err < 0)
|
||||
goto err_fill;
|
||||
|
||||
@@ -1489,18 +1534,17 @@ err_fill:
|
||||
return err;
|
||||
}
|
||||
|
||||
-static int team_nl_send_event_port_list_get(struct team_port *port)
|
||||
+static int team_nl_send_event_port_list_get(struct team *team)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
- struct net *net = dev_net(port->team->dev);
|
||||
+ struct net *net = dev_net(team->dev);
|
||||
|
||||
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
- err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
|
||||
- port->team, port);
|
||||
+ err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false);
|
||||
if (err < 0)
|
||||
goto err_fill;
|
||||
|
||||
@@ -1544,12 +1588,11 @@ static void team_nl_fini(void)
|
||||
* Change checkers
|
||||
******************/
|
||||
|
||||
-static void __team_options_change_check(struct team *team,
|
||||
- struct team_option *changed_option)
|
||||
+static void __team_options_change_check(struct team *team)
|
||||
{
|
||||
int err;
|
||||
|
||||
- err = team_nl_send_event_options_get(team, changed_option);
|
||||
+ err = team_nl_send_event_options_get(team);
|
||||
if (err)
|
||||
netdev_warn(team->dev, "Failed to send options change via netlink\n");
|
||||
}
|
||||
@@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
|
||||
{
|
||||
int err;
|
||||
|
||||
- if (port->linkup == linkup)
|
||||
+ if (!port->removed && port->linkup == linkup)
|
||||
return;
|
||||
|
||||
+ port->changed = true;
|
||||
port->linkup = linkup;
|
||||
if (linkup) {
|
||||
struct ethtool_cmd ecmd;
|
||||
@@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
|
||||
port->duplex = 0;
|
||||
|
||||
send_event:
|
||||
- err = team_nl_send_event_port_list_get(port);
|
||||
+ err = team_nl_send_event_port_list_get(port->team);
|
||||
if (err)
|
||||
netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
|
||||
port->dev->name);
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 828181f..58404b0 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -46,6 +46,10 @@ struct team_port {
|
||||
u32 speed;
|
||||
u8 duplex;
|
||||
|
||||
+ /* Custom gennetlink interface related flags */
|
||||
+ bool changed;
|
||||
+ bool removed;
|
||||
+
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
@@ -72,6 +76,10 @@ struct team_option {
|
||||
enum team_option_type type;
|
||||
int (*getter)(struct team *team, void *arg);
|
||||
int (*setter)(struct team *team, void *arg);
|
||||
+
|
||||
+ /* Custom gennetlink interface related flags */
|
||||
+ bool changed;
|
||||
+ bool removed;
|
||||
};
|
||||
|
||||
struct team_mode {
|
||||
@@ -207,6 +215,7 @@ enum {
|
||||
TEAM_ATTR_OPTION_CHANGED, /* flag */
|
||||
TEAM_ATTR_OPTION_TYPE, /* u8 */
|
||||
TEAM_ATTR_OPTION_DATA, /* dynamic */
|
||||
+ TEAM_ATTR_OPTION_REMOVED, /* flag */
|
||||
|
||||
__TEAM_ATTR_OPTION_MAX,
|
||||
TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
|
||||
@@ -227,6 +236,7 @@ enum {
|
||||
TEAM_ATTR_PORT_LINKUP, /* flag */
|
||||
TEAM_ATTR_PORT_SPEED, /* u32 */
|
||||
TEAM_ATTR_PORT_DUPLEX, /* u8 */
|
||||
+ TEAM_ATTR_PORT_REMOVED, /* flag */
|
||||
|
||||
__TEAM_ATTR_PORT_MAX,
|
||||
TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
|
|
@ -0,0 +1,108 @@
|
|||
From: "David S. Miller" <davem@davemloft.net>
|
||||
Date: Sun, 1 Apr 2012 20:25:18 -0400
|
||||
Subject: [13/23] team: Stop using NLA_PUT*().
|
||||
|
||||
commit 86ebb02dc793058ea17ad647c802b507dafff7cb upstream.
|
||||
|
||||
These macros contain a hidden goto, and are thus extremely error
|
||||
prone and make code hard to audit.
|
||||
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 50 +++++++++++++++++++++++++++++------------------
|
||||
1 file changed, 31 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 8f81805..0db6e66 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -1248,7 +1248,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
if (IS_ERR(hdr))
|
||||
return PTR_ERR(hdr);
|
||||
|
||||
- NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
|
||||
+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
|
||||
+ goto nla_put_failure;
|
||||
option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
|
||||
if (!option_list)
|
||||
return -EMSGSIZE;
|
||||
@@ -1263,24 +1264,31 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
|
||||
if (!option_item)
|
||||
goto nla_put_failure;
|
||||
- NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
|
||||
+ if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
|
||||
+ goto nla_put_failure;
|
||||
if (option->changed) {
|
||||
- NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
|
||||
+ if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
|
||||
+ goto nla_put_failure;
|
||||
option->changed = false;
|
||||
}
|
||||
- if (option->removed)
|
||||
- NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
|
||||
+ if (option->removed &&
|
||||
+ nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
|
||||
+ goto nla_put_failure;
|
||||
switch (option->type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
- NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
|
||||
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
|
||||
+ goto nla_put_failure;
|
||||
team_option_get(team, option, &arg);
|
||||
- NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
|
||||
+ if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg))
|
||||
+ goto nla_put_failure;
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_STRING:
|
||||
- NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
|
||||
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
|
||||
+ goto nla_put_failure;
|
||||
team_option_get(team, option, &arg);
|
||||
- NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
|
||||
- (char *) arg);
|
||||
+ if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
|
||||
+ (char *) arg))
|
||||
+ goto nla_put_failure;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
@@ -1420,7 +1428,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
|
||||
if (IS_ERR(hdr))
|
||||
return PTR_ERR(hdr);
|
||||
|
||||
- NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
|
||||
+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
|
||||
+ goto nla_put_failure;
|
||||
port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
|
||||
if (!port_list)
|
||||
return -EMSGSIZE;
|
||||
@@ -1434,17 +1443,20 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
|
||||
port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
|
||||
if (!port_item)
|
||||
goto nla_put_failure;
|
||||
- NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
|
||||
+ if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
|
||||
+ goto nla_put_failure;
|
||||
if (port->changed) {
|
||||
- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
|
||||
+ if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
|
||||
+ goto nla_put_failure;
|
||||
port->changed = false;
|
||||
}
|
||||
- if (port->removed)
|
||||
- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
|
||||
- if (port->linkup)
|
||||
- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
|
||||
- NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
|
||||
- NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
|
||||
+ if ((port->removed &&
|
||||
+ nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
|
||||
+ (port->linkup &&
|
||||
+ nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
|
||||
+ nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->speed) ||
|
||||
+ nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex))
|
||||
+ goto nla_put_failure;
|
||||
nla_nest_end(skb, port_item);
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Wed, 4 Apr 2012 12:16:26 +0000
|
||||
Subject: [14/23] team: add binary option type
|
||||
|
||||
commit 2615598fc100451c71b83d06bdf5faead619a40e upstream.
|
||||
|
||||
For transfering generic binary data (e.g. BPF code), introduce new
|
||||
binary option type.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 30 ++++++++++++++++++++++++++----
|
||||
include/linux/if_team.h | 8 ++++++++
|
||||
2 files changed, 34 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 0db6e66..ea96f82 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -1145,10 +1145,7 @@ team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
|
||||
},
|
||||
[TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG },
|
||||
[TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
|
||||
- [TEAM_ATTR_OPTION_DATA] = {
|
||||
- .type = NLA_BINARY,
|
||||
- .len = TEAM_STRING_MAX_LEN,
|
||||
- },
|
||||
+ [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY },
|
||||
};
|
||||
|
||||
static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
|
||||
@@ -1257,6 +1254,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
list_for_each_entry(option, &team->option_list, list) {
|
||||
struct nlattr *option_item;
|
||||
long arg;
|
||||
+ struct team_option_binary tbinary;
|
||||
|
||||
/* Include only changed options if fill all mode is not on */
|
||||
if (!fillall && !option->changed)
|
||||
@@ -1290,6 +1288,15 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
(char *) arg))
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
+ case TEAM_OPTION_TYPE_BINARY:
|
||||
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
|
||||
+ goto nla_put_failure;
|
||||
+ arg = (long) &tbinary;
|
||||
+ team_option_get(team, option, &arg);
|
||||
+ if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
|
||||
+ tbinary.data_len, tbinary.data))
|
||||
+ goto nla_put_failure;
|
||||
+ break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@@ -1374,6 +1381,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
case NLA_STRING:
|
||||
opt_type = TEAM_OPTION_TYPE_STRING;
|
||||
break;
|
||||
+ case NLA_BINARY:
|
||||
+ opt_type = TEAM_OPTION_TYPE_BINARY;
|
||||
+ break;
|
||||
default:
|
||||
goto team_put;
|
||||
}
|
||||
@@ -1382,19 +1392,31 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
list_for_each_entry(option, &team->option_list, list) {
|
||||
long arg;
|
||||
struct nlattr *opt_data_attr;
|
||||
+ struct team_option_binary tbinary;
|
||||
+ int data_len;
|
||||
|
||||
if (option->type != opt_type ||
|
||||
strcmp(option->name, opt_name))
|
||||
continue;
|
||||
opt_found = true;
|
||||
opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
|
||||
+ data_len = nla_len(opt_data_attr);
|
||||
switch (opt_type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
arg = nla_get_u32(opt_data_attr);
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_STRING:
|
||||
+ if (data_len > TEAM_STRING_MAX_LEN) {
|
||||
+ err = -EINVAL;
|
||||
+ goto team_put;
|
||||
+ }
|
||||
arg = (long) nla_data(opt_data_attr);
|
||||
break;
|
||||
+ case TEAM_OPTION_TYPE_BINARY:
|
||||
+ tbinary.data_len = data_len;
|
||||
+ tbinary.data = nla_data(opt_data_attr);
|
||||
+ arg = (long) &tbinary;
|
||||
+ break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 58404b0..41163ac 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -68,6 +68,7 @@ struct team_mode_ops {
|
||||
enum team_option_type {
|
||||
TEAM_OPTION_TYPE_U32,
|
||||
TEAM_OPTION_TYPE_STRING,
|
||||
+ TEAM_OPTION_TYPE_BINARY,
|
||||
};
|
||||
|
||||
struct team_option {
|
||||
@@ -82,6 +83,13 @@ struct team_option {
|
||||
bool removed;
|
||||
};
|
||||
|
||||
+struct team_option_binary {
|
||||
+ u32 data_len;
|
||||
+ void *data;
|
||||
+};
|
||||
+
|
||||
+#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
|
||||
+
|
||||
struct team_mode {
|
||||
struct list_head list;
|
||||
const char *kind;
|
|
@ -0,0 +1,241 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Wed, 4 Apr 2012 12:16:27 +0000
|
||||
Subject: [15/23] team: add loadbalance mode
|
||||
|
||||
commit 01d7f30a9f962573b6c91ed520c73fb30658d826 upstream.
|
||||
|
||||
This patch introduces new team mode. It's TX port is selected by
|
||||
user-set BPF hash function.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/Kconfig | 11 ++
|
||||
drivers/net/team/Makefile | 1 +
|
||||
drivers/net/team/team_mode_loadbalance.c | 188 ++++++++++++++++++++++++++++++
|
||||
3 files changed, 200 insertions(+)
|
||||
create mode 100644 drivers/net/team/team_mode_loadbalance.c
|
||||
|
||||
diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
|
||||
index 248a144..89024d5 100644
|
||||
--- a/drivers/net/team/Kconfig
|
||||
+++ b/drivers/net/team/Kconfig
|
||||
@@ -40,4 +40,15 @@ config NET_TEAM_MODE_ACTIVEBACKUP
|
||||
To compile this team mode as a module, choose M here: the module
|
||||
will be called team_mode_activebackup.
|
||||
|
||||
+config NET_TEAM_MODE_LOADBALANCE
|
||||
+ tristate "Load-balance mode support"
|
||||
+ depends on NET_TEAM
|
||||
+ ---help---
|
||||
+ This mode provides load balancing functionality. Tx port selection
|
||||
+ is done using BPF function set up from userspace (bpf_hash_func
|
||||
+ option)
|
||||
+
|
||||
+ To compile this team mode as a module, choose M here: the module
|
||||
+ will be called team_mode_loadbalance.
|
||||
+
|
||||
endif # NET_TEAM
|
||||
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
|
||||
index 85f2028..fb9f4c1 100644
|
||||
--- a/drivers/net/team/Makefile
|
||||
+++ b/drivers/net/team/Makefile
|
||||
@@ -5,3 +5,4 @@
|
||||
obj-$(CONFIG_NET_TEAM) += team.o
|
||||
obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
|
||||
obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
|
||||
+obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o
|
||||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
|
||||
new file mode 100644
|
||||
index 0000000..ed20f39
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/team/team_mode_loadbalance.c
|
||||
@@ -0,0 +1,188 @@
|
||||
+/*
|
||||
+ * drivers/net/team/team_mode_loadbalance.c - Load-balancing mode for team
|
||||
+ * Copyright (c) 2012 Jiri Pirko <jpirko@redhat.com>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/filter.h>
|
||||
+#include <linux/if_team.h>
|
||||
+
|
||||
+struct lb_priv {
|
||||
+ struct sk_filter __rcu *fp;
|
||||
+ struct sock_fprog *orig_fprog;
|
||||
+};
|
||||
+
|
||||
+static struct lb_priv *lb_priv(struct team *team)
|
||||
+{
|
||||
+ return (struct lb_priv *) &team->mode_priv;
|
||||
+}
|
||||
+
|
||||
+static bool lb_transmit(struct team *team, struct sk_buff *skb)
|
||||
+{
|
||||
+ struct sk_filter *fp;
|
||||
+ struct team_port *port;
|
||||
+ unsigned int hash;
|
||||
+ int port_index;
|
||||
+
|
||||
+ fp = rcu_dereference(lb_priv(team)->fp);
|
||||
+ if (unlikely(!fp))
|
||||
+ goto drop;
|
||||
+ hash = SK_RUN_FILTER(fp, skb);
|
||||
+ port_index = hash % team->port_count;
|
||||
+ port = team_get_port_by_index_rcu(team, port_index);
|
||||
+ if (unlikely(!port))
|
||||
+ goto drop;
|
||||
+ skb->dev = port->dev;
|
||||
+ if (dev_queue_xmit(skb))
|
||||
+ return false;
|
||||
+ return true;
|
||||
+
|
||||
+drop:
|
||||
+ dev_kfree_skb_any(skb);
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static int lb_bpf_func_get(struct team *team, void *arg)
|
||||
+{
|
||||
+ struct team_option_binary *tbinary = team_optarg_tbinary(arg);
|
||||
+
|
||||
+ memset(tbinary, 0, sizeof(*tbinary));
|
||||
+ if (!lb_priv(team)->orig_fprog)
|
||||
+ return 0;
|
||||
+
|
||||
+ tbinary->data_len = lb_priv(team)->orig_fprog->len *
|
||||
+ sizeof(struct sock_filter);
|
||||
+ tbinary->data = lb_priv(team)->orig_fprog->filter;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct sock_fprog *fprog;
|
||||
+ struct sock_filter *filter = (struct sock_filter *) data;
|
||||
+
|
||||
+ if (data_len % sizeof(struct sock_filter))
|
||||
+ return -EINVAL;
|
||||
+ fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL);
|
||||
+ if (!fprog)
|
||||
+ return -ENOMEM;
|
||||
+ fprog->filter = kmemdup(filter, data_len, GFP_KERNEL);
|
||||
+ if (!fprog->filter) {
|
||||
+ kfree(fprog);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ fprog->len = data_len / sizeof(struct sock_filter);
|
||||
+ *pfprog = fprog;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __fprog_destroy(struct sock_fprog *fprog)
|
||||
+{
|
||||
+ kfree(fprog->filter);
|
||||
+ kfree(fprog);
|
||||
+}
|
||||
+
|
||||
+static int lb_bpf_func_set(struct team *team, void *arg)
|
||||
+{
|
||||
+ struct team_option_binary *tbinary = team_optarg_tbinary(arg);
|
||||
+ struct sk_filter *fp = NULL;
|
||||
+ struct sock_fprog *fprog = NULL;
|
||||
+ int err;
|
||||
+
|
||||
+ if (tbinary->data_len) {
|
||||
+ err = __fprog_create(&fprog, tbinary->data_len,
|
||||
+ tbinary->data);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ err = sk_unattached_filter_create(&fp, fprog);
|
||||
+ if (err) {
|
||||
+ __fprog_destroy(fprog);
|
||||
+ return err;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (lb_priv(team)->orig_fprog) {
|
||||
+ /* Clear old filter data */
|
||||
+ __fprog_destroy(lb_priv(team)->orig_fprog);
|
||||
+ sk_unattached_filter_destroy(lb_priv(team)->fp);
|
||||
+ }
|
||||
+
|
||||
+ rcu_assign_pointer(lb_priv(team)->fp, fp);
|
||||
+ lb_priv(team)->orig_fprog = fprog;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct team_option lb_options[] = {
|
||||
+ {
|
||||
+ .name = "bpf_hash_func",
|
||||
+ .type = TEAM_OPTION_TYPE_BINARY,
|
||||
+ .getter = lb_bpf_func_get,
|
||||
+ .setter = lb_bpf_func_set,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+int lb_init(struct team *team)
|
||||
+{
|
||||
+ return team_options_register(team, lb_options,
|
||||
+ ARRAY_SIZE(lb_options));
|
||||
+}
|
||||
+
|
||||
+void lb_exit(struct team *team)
|
||||
+{
|
||||
+ team_options_unregister(team, lb_options,
|
||||
+ ARRAY_SIZE(lb_options));
|
||||
+}
|
||||
+
|
||||
+static int lb_port_enter(struct team *team, struct team_port *port)
|
||||
+{
|
||||
+ return team_port_set_team_mac(port);
|
||||
+}
|
||||
+
|
||||
+static void lb_port_change_mac(struct team *team, struct team_port *port)
|
||||
+{
|
||||
+ team_port_set_team_mac(port);
|
||||
+}
|
||||
+
|
||||
+static const struct team_mode_ops lb_mode_ops = {
|
||||
+ .init = lb_init,
|
||||
+ .exit = lb_exit,
|
||||
+ .transmit = lb_transmit,
|
||||
+ .port_enter = lb_port_enter,
|
||||
+ .port_change_mac = lb_port_change_mac,
|
||||
+};
|
||||
+
|
||||
+static struct team_mode lb_mode = {
|
||||
+ .kind = "loadbalance",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .priv_size = sizeof(struct lb_priv),
|
||||
+ .ops = &lb_mode_ops,
|
||||
+};
|
||||
+
|
||||
+static int __init lb_init_module(void)
|
||||
+{
|
||||
+ return team_mode_register(&lb_mode);
|
||||
+}
|
||||
+
|
||||
+static void __exit lb_cleanup_module(void)
|
||||
+{
|
||||
+ team_mode_unregister(&lb_mode);
|
||||
+}
|
||||
+
|
||||
+module_init(lb_init_module);
|
||||
+module_exit(lb_cleanup_module);
|
||||
+
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
|
||||
+MODULE_DESCRIPTION("Load-balancing mode for team");
|
||||
+MODULE_ALIAS("team-mode-loadbalance");
|
687
debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch
vendored
Normal file
687
debian/patches/features/all/team/0016-team-add-support-for-per-port-options.patch
vendored
Normal file
|
@ -0,0 +1,687 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 10 Apr 2012 05:15:42 +0000
|
||||
Subject: [16/23] team: add support for per-port options
|
||||
|
||||
commit 80f7c6683fe0e891ef1db7c967d538b5fdddd22c upstream.
|
||||
|
||||
This patch allows to create per-port options. That becomes handy for all
|
||||
sorts of stuff, for example for userspace driven link-state, 802.3ad
|
||||
implementation and so on.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 306 +++++++++++++++++++++++------
|
||||
drivers/net/team/team_mode_activebackup.c | 14 +-
|
||||
drivers/net/team/team_mode_loadbalance.c | 28 ++-
|
||||
include/linux/if_team.h | 30 +--
|
||||
4 files changed, 278 insertions(+), 100 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index ea96f82..eaf8441 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac);
|
||||
* Options handling
|
||||
*******************/
|
||||
|
||||
-struct team_option *__team_find_option(struct team *team, const char *opt_name)
|
||||
+struct team_option_inst { /* One for each option instance */
|
||||
+ struct list_head list;
|
||||
+ struct team_option *option;
|
||||
+ struct team_port *port; /* != NULL if per-port */
|
||||
+ bool changed;
|
||||
+ bool removed;
|
||||
+};
|
||||
+
|
||||
+static struct team_option *__team_find_option(struct team *team,
|
||||
+ const char *opt_name)
|
||||
{
|
||||
struct team_option *option;
|
||||
|
||||
@@ -92,9 +101,121 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-int __team_options_register(struct team *team,
|
||||
- const struct team_option *option,
|
||||
- size_t option_count)
|
||||
+static int __team_option_inst_add(struct team *team, struct team_option *option,
|
||||
+ struct team_port *port)
|
||||
+{
|
||||
+ struct team_option_inst *opt_inst;
|
||||
+
|
||||
+ opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
|
||||
+ if (!opt_inst)
|
||||
+ return -ENOMEM;
|
||||
+ opt_inst->option = option;
|
||||
+ opt_inst->port = port;
|
||||
+ opt_inst->changed = true;
|
||||
+ opt_inst->removed = false;
|
||||
+ list_add_tail(&opt_inst->list, &team->option_inst_list);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __team_option_inst_del(struct team_option_inst *opt_inst)
|
||||
+{
|
||||
+ list_del(&opt_inst->list);
|
||||
+ kfree(opt_inst);
|
||||
+}
|
||||
+
|
||||
+static void __team_option_inst_del_option(struct team *team,
|
||||
+ struct team_option *option)
|
||||
+{
|
||||
+ struct team_option_inst *opt_inst, *tmp;
|
||||
+
|
||||
+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
|
||||
+ if (opt_inst->option == option)
|
||||
+ __team_option_inst_del(opt_inst);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int __team_option_inst_add_option(struct team *team,
|
||||
+ struct team_option *option)
|
||||
+{
|
||||
+ struct team_port *port;
|
||||
+ int err;
|
||||
+
|
||||
+ if (!option->per_port)
|
||||
+ return __team_option_inst_add(team, option, 0);
|
||||
+
|
||||
+ list_for_each_entry(port, &team->port_list, list) {
|
||||
+ err = __team_option_inst_add(team, option, port);
|
||||
+ if (err)
|
||||
+ goto inst_del_option;
|
||||
+ }
|
||||
+ return 0;
|
||||
+
|
||||
+inst_del_option:
|
||||
+ __team_option_inst_del_option(team, option);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static void __team_option_inst_mark_removed_option(struct team *team,
|
||||
+ struct team_option *option)
|
||||
+{
|
||||
+ struct team_option_inst *opt_inst;
|
||||
+
|
||||
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
|
||||
+ if (opt_inst->option == option) {
|
||||
+ opt_inst->changed = true;
|
||||
+ opt_inst->removed = true;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void __team_option_inst_del_port(struct team *team,
|
||||
+ struct team_port *port)
|
||||
+{
|
||||
+ struct team_option_inst *opt_inst, *tmp;
|
||||
+
|
||||
+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
|
||||
+ if (opt_inst->option->per_port &&
|
||||
+ opt_inst->port == port)
|
||||
+ __team_option_inst_del(opt_inst);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int __team_option_inst_add_port(struct team *team,
|
||||
+ struct team_port *port)
|
||||
+{
|
||||
+ struct team_option *option;
|
||||
+ int err;
|
||||
+
|
||||
+ list_for_each_entry(option, &team->option_list, list) {
|
||||
+ if (!option->per_port)
|
||||
+ continue;
|
||||
+ err = __team_option_inst_add(team, option, port);
|
||||
+ if (err)
|
||||
+ goto inst_del_port;
|
||||
+ }
|
||||
+ return 0;
|
||||
+
|
||||
+inst_del_port:
|
||||
+ __team_option_inst_del_port(team, port);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static void __team_option_inst_mark_removed_port(struct team *team,
|
||||
+ struct team_port *port)
|
||||
+{
|
||||
+ struct team_option_inst *opt_inst;
|
||||
+
|
||||
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
|
||||
+ if (opt_inst->port == port) {
|
||||
+ opt_inst->changed = true;
|
||||
+ opt_inst->removed = true;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int __team_options_register(struct team *team,
|
||||
+ const struct team_option *option,
|
||||
+ size_t option_count)
|
||||
{
|
||||
int i;
|
||||
struct team_option **dst_opts;
|
||||
@@ -107,26 +228,32 @@ int __team_options_register(struct team *team,
|
||||
for (i = 0; i < option_count; i++, option++) {
|
||||
if (__team_find_option(team, option->name)) {
|
||||
err = -EEXIST;
|
||||
- goto rollback;
|
||||
+ goto alloc_rollback;
|
||||
}
|
||||
dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
|
||||
if (!dst_opts[i]) {
|
||||
err = -ENOMEM;
|
||||
- goto rollback;
|
||||
+ goto alloc_rollback;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < option_count; i++) {
|
||||
- dst_opts[i]->changed = true;
|
||||
- dst_opts[i]->removed = false;
|
||||
+ err = __team_option_inst_add_option(team, dst_opts[i]);
|
||||
+ if (err)
|
||||
+ goto inst_rollback;
|
||||
list_add_tail(&dst_opts[i]->list, &team->option_list);
|
||||
}
|
||||
|
||||
kfree(dst_opts);
|
||||
return 0;
|
||||
|
||||
-rollback:
|
||||
- for (i = 0; i < option_count; i++)
|
||||
+inst_rollback:
|
||||
+ for (i--; i >= 0; i--)
|
||||
+ __team_option_inst_del_option(team, dst_opts[i]);
|
||||
+
|
||||
+ i = option_count - 1;
|
||||
+alloc_rollback:
|
||||
+ for (i--; i >= 0; i--)
|
||||
kfree(dst_opts[i]);
|
||||
|
||||
kfree(dst_opts);
|
||||
@@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team,
|
||||
struct team_option *del_opt;
|
||||
|
||||
del_opt = __team_find_option(team, option->name);
|
||||
- if (del_opt) {
|
||||
- del_opt->changed = true;
|
||||
- del_opt->removed = true;
|
||||
- }
|
||||
+ if (del_opt)
|
||||
+ __team_option_inst_mark_removed_option(team, del_opt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team,
|
||||
|
||||
del_opt = __team_find_option(team, option->name);
|
||||
if (del_opt) {
|
||||
+ __team_option_inst_del_option(team, del_opt);
|
||||
list_del(&del_opt->list);
|
||||
kfree(del_opt);
|
||||
}
|
||||
@@ -193,22 +319,42 @@ void team_options_unregister(struct team *team,
|
||||
}
|
||||
EXPORT_SYMBOL(team_options_unregister);
|
||||
|
||||
-static int team_option_get(struct team *team, struct team_option *option,
|
||||
- void *arg)
|
||||
+static int team_option_port_add(struct team *team, struct team_port *port)
|
||||
{
|
||||
- return option->getter(team, arg);
|
||||
+ int err;
|
||||
+
|
||||
+ err = __team_option_inst_add_port(team, port);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ __team_options_change_check(team);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static int team_option_set(struct team *team, struct team_option *option,
|
||||
- void *arg)
|
||||
+static void team_option_port_del(struct team *team, struct team_port *port)
|
||||
+{
|
||||
+ __team_option_inst_mark_removed_port(team, port);
|
||||
+ __team_options_change_check(team);
|
||||
+ __team_option_inst_del_port(team, port);
|
||||
+}
|
||||
+
|
||||
+static int team_option_get(struct team *team,
|
||||
+ struct team_option_inst *opt_inst,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ return opt_inst->option->getter(team, ctx);
|
||||
+}
|
||||
+
|
||||
+static int team_option_set(struct team *team,
|
||||
+ struct team_option_inst *opt_inst,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
int err;
|
||||
|
||||
- err = option->setter(team, arg);
|
||||
+ err = opt_inst->option->setter(team, ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- option->changed = true;
|
||||
+ opt_inst->changed = true;
|
||||
__team_options_change_check(team);
|
||||
return err;
|
||||
}
|
||||
@@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
||||
goto err_handler_register;
|
||||
}
|
||||
|
||||
+ err = team_option_port_add(team, port);
|
||||
+ if (err) {
|
||||
+ netdev_err(dev, "Device %s failed to add per-port options\n",
|
||||
+ portname);
|
||||
+ goto err_option_port_add;
|
||||
+ }
|
||||
+
|
||||
team_port_list_add_port(team, port);
|
||||
team_adjust_ops(team);
|
||||
__team_compute_features(team);
|
||||
@@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
||||
|
||||
return 0;
|
||||
|
||||
+err_option_port_add:
|
||||
+ netdev_rx_handler_unregister(port_dev);
|
||||
+
|
||||
err_handler_register:
|
||||
netdev_set_master(port_dev, NULL);
|
||||
|
||||
@@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
||||
__team_port_change_check(port, false);
|
||||
team_port_list_del_port(team, port);
|
||||
team_adjust_ops(team);
|
||||
+ team_option_port_del(team, port);
|
||||
netdev_rx_handler_unregister(port_dev);
|
||||
netdev_set_master(port_dev, NULL);
|
||||
vlan_vids_del_by_dev(port_dev, dev);
|
||||
@@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
||||
|
||||
static const char team_no_mode_kind[] = "*NOMODE*";
|
||||
|
||||
-static int team_mode_option_get(struct team *team, void *arg)
|
||||
+static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- const char **str = arg;
|
||||
-
|
||||
- *str = team->mode ? team->mode->kind : team_no_mode_kind;
|
||||
+ ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int team_mode_option_set(struct team *team, void *arg)
|
||||
+static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- const char **str = arg;
|
||||
-
|
||||
- return team_change_mode(team, *str);
|
||||
+ return team_change_mode(team, ctx->data.str_val);
|
||||
}
|
||||
|
||||
static const struct team_option team_options[] = {
|
||||
@@ -756,6 +909,7 @@ static int team_init(struct net_device *dev)
|
||||
team_adjust_ops(team);
|
||||
|
||||
INIT_LIST_HEAD(&team->option_list);
|
||||
+ INIT_LIST_HEAD(&team->option_inst_list);
|
||||
err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
|
||||
if (err)
|
||||
goto err_options_register;
|
||||
@@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
{
|
||||
struct nlattr *option_list;
|
||||
void *hdr;
|
||||
- struct team_option *option;
|
||||
+ struct team_option_inst *opt_inst;
|
||||
+ int err;
|
||||
|
||||
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
|
||||
TEAM_CMD_OPTIONS_GET);
|
||||
@@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
if (!option_list)
|
||||
return -EMSGSIZE;
|
||||
|
||||
- list_for_each_entry(option, &team->option_list, list) {
|
||||
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
|
||||
struct nlattr *option_item;
|
||||
- long arg;
|
||||
- struct team_option_binary tbinary;
|
||||
+ struct team_option *option = opt_inst->option;
|
||||
+ struct team_gsetter_ctx ctx;
|
||||
|
||||
/* Include only changed options if fill all mode is not on */
|
||||
- if (!fillall && !option->changed)
|
||||
+ if (!fillall && !opt_inst->changed)
|
||||
continue;
|
||||
option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
|
||||
if (!option_item)
|
||||
goto nla_put_failure;
|
||||
if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
|
||||
goto nla_put_failure;
|
||||
- if (option->changed) {
|
||||
+ if (opt_inst->changed) {
|
||||
if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
|
||||
goto nla_put_failure;
|
||||
- option->changed = false;
|
||||
+ opt_inst->changed = false;
|
||||
}
|
||||
- if (option->removed &&
|
||||
+ if (opt_inst->removed &&
|
||||
nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
|
||||
goto nla_put_failure;
|
||||
+ if (opt_inst->port &&
|
||||
+ nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
|
||||
+ opt_inst->port->dev->ifindex))
|
||||
+ goto nla_put_failure;
|
||||
+ ctx.port = opt_inst->port;
|
||||
switch (option->type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
|
||||
goto nla_put_failure;
|
||||
- team_option_get(team, option, &arg);
|
||||
- if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg))
|
||||
+ err = team_option_get(team, opt_inst, &ctx);
|
||||
+ if (err)
|
||||
+ goto errout;
|
||||
+ if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA,
|
||||
+ ctx.data.u32_val))
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_STRING:
|
||||
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
|
||||
goto nla_put_failure;
|
||||
- team_option_get(team, option, &arg);
|
||||
+ err = team_option_get(team, opt_inst, &ctx);
|
||||
+ if (err)
|
||||
+ goto errout;
|
||||
if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
|
||||
- (char *) arg))
|
||||
+ ctx.data.str_val))
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_BINARY:
|
||||
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
|
||||
goto nla_put_failure;
|
||||
- arg = (long) &tbinary;
|
||||
- team_option_get(team, option, &arg);
|
||||
+ err = team_option_get(team, opt_inst, &ctx);
|
||||
+ if (err)
|
||||
+ goto errout;
|
||||
if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
|
||||
- tbinary.data_len, tbinary.data))
|
||||
+ ctx.data.bin_val.len, ctx.data.bin_val.ptr))
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
default:
|
||||
@@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
return genlmsg_end(skb, hdr);
|
||||
|
||||
nla_put_failure:
|
||||
+ err = -EMSGSIZE;
|
||||
+errout:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
- return -EMSGSIZE;
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static int team_nl_fill_options_get_all(struct sk_buff *skb,
|
||||
@@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
|
||||
- struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
|
||||
+ struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
|
||||
+ struct nlattr *attr_port_ifindex;
|
||||
enum team_option_type opt_type;
|
||||
- struct team_option *option;
|
||||
+ int opt_port_ifindex = 0; /* != 0 for per-port options */
|
||||
+ struct team_option_inst *opt_inst;
|
||||
char *opt_name;
|
||||
bool opt_found = false;
|
||||
|
||||
@@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
err = -EINVAL;
|
||||
goto team_put;
|
||||
}
|
||||
- err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
|
||||
+ err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX,
|
||||
nl_option, team_nl_option_policy);
|
||||
if (err)
|
||||
goto team_put;
|
||||
- if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
|
||||
- !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
|
||||
- !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
|
||||
+ if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
|
||||
+ !opt_attrs[TEAM_ATTR_OPTION_TYPE] ||
|
||||
+ !opt_attrs[TEAM_ATTR_OPTION_DATA]) {
|
||||
err = -EINVAL;
|
||||
goto team_put;
|
||||
}
|
||||
- switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
|
||||
+ switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
|
||||
case NLA_U32:
|
||||
opt_type = TEAM_OPTION_TYPE_U32;
|
||||
break;
|
||||
@@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
goto team_put;
|
||||
}
|
||||
|
||||
- opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
|
||||
- list_for_each_entry(option, &team->option_list, list) {
|
||||
- long arg;
|
||||
+ opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
|
||||
+ attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
|
||||
+ if (attr_port_ifindex)
|
||||
+ opt_port_ifindex = nla_get_u32(attr_port_ifindex);
|
||||
+
|
||||
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
|
||||
+ struct team_option *option = opt_inst->option;
|
||||
struct nlattr *opt_data_attr;
|
||||
- struct team_option_binary tbinary;
|
||||
+ struct team_gsetter_ctx ctx;
|
||||
int data_len;
|
||||
+ int tmp_ifindex;
|
||||
|
||||
+ tmp_ifindex = opt_inst->port ?
|
||||
+ opt_inst->port->dev->ifindex : 0;
|
||||
if (option->type != opt_type ||
|
||||
- strcmp(option->name, opt_name))
|
||||
+ strcmp(option->name, opt_name) ||
|
||||
+ tmp_ifindex != opt_port_ifindex)
|
||||
continue;
|
||||
opt_found = true;
|
||||
- opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
|
||||
+ opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA];
|
||||
data_len = nla_len(opt_data_attr);
|
||||
+ ctx.port = opt_inst->port;
|
||||
switch (opt_type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
- arg = nla_get_u32(opt_data_attr);
|
||||
+ ctx.data.u32_val = nla_get_u32(opt_data_attr);
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_STRING:
|
||||
if (data_len > TEAM_STRING_MAX_LEN) {
|
||||
err = -EINVAL;
|
||||
goto team_put;
|
||||
}
|
||||
- arg = (long) nla_data(opt_data_attr);
|
||||
+ ctx.data.str_val = nla_data(opt_data_attr);
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_BINARY:
|
||||
- tbinary.data_len = data_len;
|
||||
- tbinary.data = nla_data(opt_data_attr);
|
||||
- arg = (long) &tbinary;
|
||||
+ ctx.data.bin_val.len = data_len;
|
||||
+ ctx.data.bin_val.ptr = nla_data(opt_data_attr);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
- err = team_option_set(team, option, &arg);
|
||||
+ err = team_option_set(team, opt_inst, &ctx);
|
||||
if (err)
|
||||
goto team_put;
|
||||
}
|
||||
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
|
||||
index f4d960e..6cde1ab 100644
|
||||
--- a/drivers/net/team/team_mode_activebackup.c
|
||||
+++ b/drivers/net/team/team_mode_activebackup.c
|
||||
@@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port)
|
||||
RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
|
||||
}
|
||||
|
||||
-static int ab_active_port_get(struct team *team, void *arg)
|
||||
+static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- u32 *ifindex = arg;
|
||||
-
|
||||
- *ifindex = 0;
|
||||
if (ab_priv(team)->active_port)
|
||||
- *ifindex = ab_priv(team)->active_port->dev->ifindex;
|
||||
+ ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex;
|
||||
+ else
|
||||
+ ctx->data.u32_val = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int ab_active_port_set(struct team *team, void *arg)
|
||||
+static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- u32 *ifindex = arg;
|
||||
struct team_port *port;
|
||||
|
||||
list_for_each_entry_rcu(port, &team->port_list, list) {
|
||||
- if (port->dev->ifindex == *ifindex) {
|
||||
+ if (port->dev->ifindex == ctx->data.u32_val) {
|
||||
rcu_assign_pointer(ab_priv(team)->active_port, port);
|
||||
return 0;
|
||||
}
|
||||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
|
||||
index ed20f39..167cdb4 100644
|
||||
--- a/drivers/net/team/team_mode_loadbalance.c
|
||||
+++ b/drivers/net/team/team_mode_loadbalance.c
|
||||
@@ -52,22 +52,21 @@ drop:
|
||||
return false;
|
||||
}
|
||||
|
||||
-static int lb_bpf_func_get(struct team *team, void *arg)
|
||||
+static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- struct team_option_binary *tbinary = team_optarg_tbinary(arg);
|
||||
-
|
||||
- memset(tbinary, 0, sizeof(*tbinary));
|
||||
- if (!lb_priv(team)->orig_fprog)
|
||||
+ if (!lb_priv(team)->orig_fprog) {
|
||||
+ ctx->data.bin_val.len = 0;
|
||||
+ ctx->data.bin_val.ptr = NULL;
|
||||
return 0;
|
||||
-
|
||||
- tbinary->data_len = lb_priv(team)->orig_fprog->len *
|
||||
- sizeof(struct sock_filter);
|
||||
- tbinary->data = lb_priv(team)->orig_fprog->filter;
|
||||
+ }
|
||||
+ ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len *
|
||||
+ sizeof(struct sock_filter);
|
||||
+ ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
|
||||
- void *data)
|
||||
+ const void *data)
|
||||
{
|
||||
struct sock_fprog *fprog;
|
||||
struct sock_filter *filter = (struct sock_filter *) data;
|
||||
@@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog)
|
||||
kfree(fprog);
|
||||
}
|
||||
|
||||
-static int lb_bpf_func_set(struct team *team, void *arg)
|
||||
+static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
- struct team_option_binary *tbinary = team_optarg_tbinary(arg);
|
||||
struct sk_filter *fp = NULL;
|
||||
struct sock_fprog *fprog = NULL;
|
||||
int err;
|
||||
|
||||
- if (tbinary->data_len) {
|
||||
- err = __fprog_create(&fprog, tbinary->data_len,
|
||||
- tbinary->data);
|
||||
+ if (ctx->data.bin_val.len) {
|
||||
+ err = __fprog_create(&fprog, ctx->data.bin_val.len,
|
||||
+ ctx->data.bin_val.ptr);
|
||||
if (err)
|
||||
return err;
|
||||
err = sk_unattached_filter_create(&fp, fprog);
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 41163ac..6f27c84 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -71,25 +71,27 @@ enum team_option_type {
|
||||
TEAM_OPTION_TYPE_BINARY,
|
||||
};
|
||||
|
||||
+struct team_gsetter_ctx {
|
||||
+ union {
|
||||
+ u32 u32_val;
|
||||
+ const char *str_val;
|
||||
+ struct {
|
||||
+ const void *ptr;
|
||||
+ u32 len;
|
||||
+ } bin_val;
|
||||
+ } data;
|
||||
+ struct team_port *port;
|
||||
+};
|
||||
+
|
||||
struct team_option {
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
+ bool per_port;
|
||||
enum team_option_type type;
|
||||
- int (*getter)(struct team *team, void *arg);
|
||||
- int (*setter)(struct team *team, void *arg);
|
||||
-
|
||||
- /* Custom gennetlink interface related flags */
|
||||
- bool changed;
|
||||
- bool removed;
|
||||
+ int (*getter)(struct team *team, struct team_gsetter_ctx *ctx);
|
||||
+ int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
|
||||
};
|
||||
|
||||
-struct team_option_binary {
|
||||
- u32 data_len;
|
||||
- void *data;
|
||||
-};
|
||||
-
|
||||
-#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
|
||||
-
|
||||
struct team_mode {
|
||||
struct list_head list;
|
||||
const char *kind;
|
||||
@@ -118,6 +120,7 @@ struct team {
|
||||
struct list_head port_list;
|
||||
|
||||
struct list_head option_list;
|
||||
+ struct list_head option_inst_list; /* list of option instances */
|
||||
|
||||
const struct team_mode *mode;
|
||||
struct team_mode_ops ops;
|
||||
@@ -224,6 +227,7 @@ enum {
|
||||
TEAM_ATTR_OPTION_TYPE, /* u8 */
|
||||
TEAM_ATTR_OPTION_DATA, /* dynamic */
|
||||
TEAM_ATTR_OPTION_REMOVED, /* flag */
|
||||
+ TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */
|
||||
|
||||
__TEAM_ATTR_OPTION_MAX,
|
||||
TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
|
|
@ -0,0 +1,137 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 10 Apr 2012 05:15:43 +0000
|
||||
Subject: [17/23] team: add bool option type
|
||||
|
||||
commit 14f066bab19946545130a7379f420af860a02ae8 upstream.
|
||||
|
||||
Add another (hopefully last) option type. Use NLA_FLAG to implement
|
||||
that.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 40 +++++++++++++++++++++++++++++-----------
|
||||
include/linux/if_team.h | 2 ++
|
||||
2 files changed, 31 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index eaf8441..2645fae 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -1463,6 +1463,16 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
|
||||
ctx.data.bin_val.len, ctx.data.bin_val.ptr))
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
+ case TEAM_OPTION_TYPE_BOOL:
|
||||
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
|
||||
+ goto nla_put_failure;
|
||||
+ err = team_option_get(team, opt_inst, &ctx);
|
||||
+ if (err)
|
||||
+ goto errout;
|
||||
+ if (ctx.data.bool_val &&
|
||||
+ nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
|
||||
+ goto nla_put_failure;
|
||||
+ break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@@ -1524,6 +1534,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
|
||||
struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
|
||||
struct nlattr *attr_port_ifindex;
|
||||
+ struct nlattr *attr_data;
|
||||
enum team_option_type opt_type;
|
||||
int opt_port_ifindex = 0; /* != 0 for per-port options */
|
||||
struct team_option_inst *opt_inst;
|
||||
@@ -1539,8 +1550,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
if (err)
|
||||
goto team_put;
|
||||
if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
|
||||
- !opt_attrs[TEAM_ATTR_OPTION_TYPE] ||
|
||||
- !opt_attrs[TEAM_ATTR_OPTION_DATA]) {
|
||||
+ !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
|
||||
err = -EINVAL;
|
||||
goto team_put;
|
||||
}
|
||||
@@ -1554,10 +1564,19 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
case NLA_BINARY:
|
||||
opt_type = TEAM_OPTION_TYPE_BINARY;
|
||||
break;
|
||||
+ case NLA_FLAG:
|
||||
+ opt_type = TEAM_OPTION_TYPE_BOOL;
|
||||
+ break;
|
||||
default:
|
||||
goto team_put;
|
||||
}
|
||||
|
||||
+ attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
|
||||
+ if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
|
||||
+ err = -EINVAL;
|
||||
+ goto team_put;
|
||||
+ }
|
||||
+
|
||||
opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
|
||||
attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
|
||||
if (attr_port_ifindex)
|
||||
@@ -1565,9 +1584,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
|
||||
list_for_each_entry(opt_inst, &team->option_inst_list, list) {
|
||||
struct team_option *option = opt_inst->option;
|
||||
- struct nlattr *opt_data_attr;
|
||||
struct team_gsetter_ctx ctx;
|
||||
- int data_len;
|
||||
int tmp_ifindex;
|
||||
|
||||
tmp_ifindex = opt_inst->port ?
|
||||
@@ -1577,23 +1594,24 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
|
||||
tmp_ifindex != opt_port_ifindex)
|
||||
continue;
|
||||
opt_found = true;
|
||||
- opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA];
|
||||
- data_len = nla_len(opt_data_attr);
|
||||
ctx.port = opt_inst->port;
|
||||
switch (opt_type) {
|
||||
case TEAM_OPTION_TYPE_U32:
|
||||
- ctx.data.u32_val = nla_get_u32(opt_data_attr);
|
||||
+ ctx.data.u32_val = nla_get_u32(attr_data);
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_STRING:
|
||||
- if (data_len > TEAM_STRING_MAX_LEN) {
|
||||
+ if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
|
||||
err = -EINVAL;
|
||||
goto team_put;
|
||||
}
|
||||
- ctx.data.str_val = nla_data(opt_data_attr);
|
||||
+ ctx.data.str_val = nla_data(attr_data);
|
||||
break;
|
||||
case TEAM_OPTION_TYPE_BINARY:
|
||||
- ctx.data.bin_val.len = data_len;
|
||||
- ctx.data.bin_val.ptr = nla_data(opt_data_attr);
|
||||
+ ctx.data.bin_val.len = nla_len(attr_data);
|
||||
+ ctx.data.bin_val.ptr = nla_data(attr_data);
|
||||
+ break;
|
||||
+ case TEAM_OPTION_TYPE_BOOL:
|
||||
+ ctx.data.bool_val = attr_data ? true : false;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 6f27c84..78c84fd 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -69,6 +69,7 @@ enum team_option_type {
|
||||
TEAM_OPTION_TYPE_U32,
|
||||
TEAM_OPTION_TYPE_STRING,
|
||||
TEAM_OPTION_TYPE_BINARY,
|
||||
+ TEAM_OPTION_TYPE_BOOL,
|
||||
};
|
||||
|
||||
struct team_gsetter_ctx {
|
||||
@@ -79,6 +80,7 @@ struct team_gsetter_ctx {
|
||||
const void *ptr;
|
||||
u32 len;
|
||||
} bin_val;
|
||||
+ bool bool_val;
|
||||
} data;
|
||||
struct team_port *port;
|
||||
};
|
184
debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
vendored
Normal file
184
debian/patches/features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 10 Apr 2012 05:15:44 +0000
|
||||
Subject: [18/23] team: add user_linkup and user_linkup_enabled per-port
|
||||
option
|
||||
|
||||
commit 71472ec12c61dd305ab4d11822af7ecc4f9717f9 upstream.
|
||||
|
||||
Allows userspace to setup linkup for ports. Default is to take linkup
|
||||
directly from ethtool state.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 72 +++++++++++++++++++++++++++++++++++++++++------
|
||||
include/linux/if_team.h | 26 +++++++++++------
|
||||
2 files changed, 81 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 2645fae..e639abe 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -76,6 +76,11 @@ int team_port_set_team_mac(struct team_port *port)
|
||||
}
|
||||
EXPORT_SYMBOL(team_port_set_team_mac);
|
||||
|
||||
+static void team_refresh_port_linkup(struct team_port *port)
|
||||
+{
|
||||
+ port->linkup = port->user.linkup_enabled ? port->user.linkup :
|
||||
+ port->state.linkup;
|
||||
+}
|
||||
|
||||
/*******************
|
||||
* Options handling
|
||||
@@ -880,6 +885,40 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
return team_change_mode(team, ctx->data.str_val);
|
||||
}
|
||||
|
||||
+static int team_user_linkup_option_get(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ ctx->data.bool_val = ctx->port->user.linkup;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int team_user_linkup_option_set(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ ctx->port->user.linkup = ctx->data.bool_val;
|
||||
+ team_refresh_port_linkup(ctx->port);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int team_user_linkup_en_option_get(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ struct team_port *port = ctx->port;
|
||||
+
|
||||
+ ctx->data.bool_val = port->user.linkup_enabled;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int team_user_linkup_en_option_set(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ struct team_port *port = ctx->port;
|
||||
+
|
||||
+ port->user.linkup_enabled = ctx->data.bool_val;
|
||||
+ team_refresh_port_linkup(ctx->port);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const struct team_option team_options[] = {
|
||||
{
|
||||
.name = "mode",
|
||||
@@ -887,6 +926,20 @@ static const struct team_option team_options[] = {
|
||||
.getter = team_mode_option_get,
|
||||
.setter = team_mode_option_set,
|
||||
},
|
||||
+ {
|
||||
+ .name = "user_linkup",
|
||||
+ .type = TEAM_OPTION_TYPE_BOOL,
|
||||
+ .per_port = true,
|
||||
+ .getter = team_user_linkup_option_get,
|
||||
+ .setter = team_user_linkup_option_set,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "user_linkup_enabled",
|
||||
+ .type = TEAM_OPTION_TYPE_BOOL,
|
||||
+ .per_port = true,
|
||||
+ .getter = team_user_linkup_en_option_get,
|
||||
+ .setter = team_user_linkup_en_option_set,
|
||||
+ },
|
||||
};
|
||||
|
||||
static int team_init(struct net_device *dev)
|
||||
@@ -1670,10 +1723,10 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
|
||||
}
|
||||
if ((port->removed &&
|
||||
nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
|
||||
- (port->linkup &&
|
||||
+ (port->state.linkup &&
|
||||
nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
|
||||
- nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->speed) ||
|
||||
- nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex))
|
||||
+ nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
|
||||
+ nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
|
||||
goto nla_put_failure;
|
||||
nla_nest_end(skb, port_item);
|
||||
}
|
||||
@@ -1833,23 +1886,24 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
|
||||
{
|
||||
int err;
|
||||
|
||||
- if (!port->removed && port->linkup == linkup)
|
||||
+ if (!port->removed && port->state.linkup == linkup)
|
||||
return;
|
||||
|
||||
port->changed = true;
|
||||
- port->linkup = linkup;
|
||||
+ port->state.linkup = linkup;
|
||||
+ team_refresh_port_linkup(port);
|
||||
if (linkup) {
|
||||
struct ethtool_cmd ecmd;
|
||||
|
||||
err = __ethtool_get_settings(port->dev, &ecmd);
|
||||
if (!err) {
|
||||
- port->speed = ethtool_cmd_speed(&ecmd);
|
||||
- port->duplex = ecmd.duplex;
|
||||
+ port->state.speed = ethtool_cmd_speed(&ecmd);
|
||||
+ port->state.duplex = ecmd.duplex;
|
||||
goto send_event;
|
||||
}
|
||||
}
|
||||
- port->speed = 0;
|
||||
- port->duplex = 0;
|
||||
+ port->state.speed = 0;
|
||||
+ port->state.duplex = 0;
|
||||
|
||||
send_event:
|
||||
err = team_nl_send_event_port_list_get(port->team);
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 78c84fd..5fd5ab1 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -33,6 +33,24 @@ struct team_port {
|
||||
struct team *team;
|
||||
int index;
|
||||
|
||||
+ bool linkup; /* either state.linkup or user.linkup */
|
||||
+
|
||||
+ struct {
|
||||
+ bool linkup;
|
||||
+ u32 speed;
|
||||
+ u8 duplex;
|
||||
+ } state;
|
||||
+
|
||||
+ /* Values set by userspace */
|
||||
+ struct {
|
||||
+ bool linkup;
|
||||
+ bool linkup_enabled;
|
||||
+ } user;
|
||||
+
|
||||
+ /* Custom gennetlink interface related flags */
|
||||
+ bool changed;
|
||||
+ bool removed;
|
||||
+
|
||||
/*
|
||||
* A place for storing original values of the device before it
|
||||
* become a port.
|
||||
@@ -42,14 +60,6 @@ struct team_port {
|
||||
unsigned int mtu;
|
||||
} orig;
|
||||
|
||||
- bool linkup;
|
||||
- u32 speed;
|
||||
- u8 duplex;
|
||||
-
|
||||
- /* Custom gennetlink interface related flags */
|
||||
- bool changed;
|
||||
- bool removed;
|
||||
-
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
27
debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
vendored
Normal file
27
debian/patches/features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 10 Apr 2012 05:15:45 +0000
|
||||
Subject: [19/23] team: ab: walk through port list non-rcu
|
||||
|
||||
commit 679b16073008cc536e85e2773e67234b596fb62e upstream.
|
||||
|
||||
Since team->lock is being held, _rcu variant make no sense.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team_mode_activebackup.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
|
||||
index 6cde1ab..a715c40 100644
|
||||
--- a/drivers/net/team/team_mode_activebackup.c
|
||||
+++ b/drivers/net/team/team_mode_activebackup.c
|
||||
@@ -72,7 +72,7 @@ static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
struct team_port *port;
|
||||
|
||||
- list_for_each_entry_rcu(port, &team->port_list, list) {
|
||||
+ list_for_each_entry(port, &team->port_list, list) {
|
||||
if (port->dev->ifindex == ctx->data.u32_val) {
|
||||
rcu_assign_pointer(ab_priv(team)->active_port, port);
|
||||
return 0;
|
|
@ -0,0 +1,66 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Tue, 10 Apr 2012 05:15:46 +0000
|
||||
Subject: [20/23] team: add missed "statics"
|
||||
|
||||
commit cade455596504fae8e134a27189713ddf7c6d04d upstream.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 2 +-
|
||||
drivers/net/team/team_mode_activebackup.c | 4 ++--
|
||||
drivers/net/team/team_mode_loadbalance.c | 4 ++--
|
||||
3 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index e639abe..153a62d 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -65,7 +65,7 @@ static int __set_port_mac(struct net_device *port_dev,
|
||||
return dev_set_mac_address(port_dev, &addr);
|
||||
}
|
||||
|
||||
-int team_port_set_orig_mac(struct team_port *port)
|
||||
+static int team_port_set_orig_mac(struct team_port *port)
|
||||
{
|
||||
return __set_port_mac(port->dev, port->orig.dev_addr);
|
||||
}
|
||||
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
|
||||
index a715c40..fd6bd03 100644
|
||||
--- a/drivers/net/team/team_mode_activebackup.c
|
||||
+++ b/drivers/net/team/team_mode_activebackup.c
|
||||
@@ -90,12 +90,12 @@ static const struct team_option ab_options[] = {
|
||||
},
|
||||
};
|
||||
|
||||
-int ab_init(struct team *team)
|
||||
+static int ab_init(struct team *team)
|
||||
{
|
||||
return team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
|
||||
}
|
||||
|
||||
-void ab_exit(struct team *team)
|
||||
+static void ab_exit(struct team *team)
|
||||
{
|
||||
team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
|
||||
}
|
||||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
|
||||
index 167cdb4..2b506b2 100644
|
||||
--- a/drivers/net/team/team_mode_loadbalance.c
|
||||
+++ b/drivers/net/team/team_mode_loadbalance.c
|
||||
@@ -130,13 +130,13 @@ static const struct team_option lb_options[] = {
|
||||
},
|
||||
};
|
||||
|
||||
-int lb_init(struct team *team)
|
||||
+static int lb_init(struct team *team)
|
||||
{
|
||||
return team_options_register(team, lb_options,
|
||||
ARRAY_SIZE(lb_options));
|
||||
}
|
||||
|
||||
-void lb_exit(struct team *team)
|
||||
+static void lb_exit(struct team *team)
|
||||
{
|
||||
team_options_unregister(team, lb_options,
|
||||
ARRAY_SIZE(lb_options));
|
41
debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
vendored
Normal file
41
debian/patches/features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Fri, 20 Apr 2012 04:42:04 +0000
|
||||
Subject: [21/23] team: lb: let userspace care about port macs
|
||||
|
||||
commit 4c78bb845bd2aaf1f7136e75314c7d034cfd120f upstream.
|
||||
|
||||
Better to leave this for userspace
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team_mode_loadbalance.c | 12 ------------
|
||||
1 file changed, 12 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
|
||||
index 2b506b2..438d5b8 100644
|
||||
--- a/drivers/net/team/team_mode_loadbalance.c
|
||||
+++ b/drivers/net/team/team_mode_loadbalance.c
|
||||
@@ -142,22 +142,10 @@ static void lb_exit(struct team *team)
|
||||
ARRAY_SIZE(lb_options));
|
||||
}
|
||||
|
||||
-static int lb_port_enter(struct team *team, struct team_port *port)
|
||||
-{
|
||||
- return team_port_set_team_mac(port);
|
||||
-}
|
||||
-
|
||||
-static void lb_port_change_mac(struct team *team, struct team_port *port)
|
||||
-{
|
||||
- team_port_set_team_mac(port);
|
||||
-}
|
||||
-
|
||||
static const struct team_mode_ops lb_mode_ops = {
|
||||
.init = lb_init,
|
||||
.exit = lb_exit,
|
||||
.transmit = lb_transmit,
|
||||
- .port_enter = lb_port_enter,
|
||||
- .port_change_mac = lb_port_change_mac,
|
||||
};
|
||||
|
||||
static struct team_mode lb_mode = {
|
212
debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch
vendored
Normal file
212
debian/patches/features/all/team/0022-team-allow-to-enable-disable-ports.patch
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Fri, 20 Apr 2012 04:42:05 +0000
|
||||
Subject: [22/23] team: allow to enable/disable ports
|
||||
|
||||
commit 19a0b58e506b06fd41659d8734bba6a3e87980f4 upstream.
|
||||
|
||||
This patch changes content of hashlist (used to get port struct by
|
||||
computed index (0...en_port_count-1)). Now the hash list contains only
|
||||
enabled ports so userspace will be able to say what ports can be used
|
||||
for tx/rx. This becomes handy when userspace will need to disable ports
|
||||
which does not belong to active aggregator. By default, newly added port
|
||||
is enabled.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 51 ++++++++++++++++++++----------
|
||||
drivers/net/team/team_mode_loadbalance.c | 2 +-
|
||||
drivers/net/team/team_mode_roundrobin.c | 2 +-
|
||||
include/linux/if_team.h | 15 +++++----
|
||||
4 files changed, 45 insertions(+), 25 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index 153a62d..fe7ca40 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -559,6 +559,8 @@ static int team_change_mode(struct team *team, const char *kind)
|
||||
* Rx path frame handler
|
||||
************************/
|
||||
|
||||
+static bool team_port_enabled(struct team_port *port);
|
||||
+
|
||||
/* note: already called with rcu_read_lock */
|
||||
static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
|
||||
{
|
||||
@@ -575,8 +577,12 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
|
||||
|
||||
port = team_port_get_rcu(skb->dev);
|
||||
team = port->team;
|
||||
-
|
||||
- res = team->ops.receive(team, port, skb);
|
||||
+ if (!team_port_enabled(port)) {
|
||||
+ /* allow exact match delivery for disabled ports */
|
||||
+ res = RX_HANDLER_EXACT;
|
||||
+ } else {
|
||||
+ res = team->ops.receive(team, port, skb);
|
||||
+ }
|
||||
if (res == RX_HANDLER_ANOTHER) {
|
||||
struct team_pcpu_stats *pcpu_stats;
|
||||
|
||||
@@ -612,17 +618,25 @@ static bool team_port_find(const struct team *team,
|
||||
return false;
|
||||
}
|
||||
|
||||
+static bool team_port_enabled(struct team_port *port)
|
||||
+{
|
||||
+ return port->index != -1;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
- * Add/delete port to the team port list. Write guarded by rtnl_lock.
|
||||
- * Takes care of correct port->index setup (might be racy).
|
||||
+ * Enable/disable port by adding to enabled port hashlist and setting
|
||||
+ * port->index (Might be racy so reader could see incorrect ifindex when
|
||||
+ * processing a flying packet, but that is not a problem). Write guarded
|
||||
+ * by team->lock.
|
||||
*/
|
||||
-static void team_port_list_add_port(struct team *team,
|
||||
- struct team_port *port)
|
||||
+static void team_port_enable(struct team *team,
|
||||
+ struct team_port *port)
|
||||
{
|
||||
- port->index = team->port_count++;
|
||||
+ if (team_port_enabled(port))
|
||||
+ return;
|
||||
+ port->index = team->en_port_count++;
|
||||
hlist_add_head_rcu(&port->hlist,
|
||||
team_port_index_hash(team, port->index));
|
||||
- list_add_tail_rcu(&port->list, &team->port_list);
|
||||
}
|
||||
|
||||
static void __reconstruct_port_hlist(struct team *team, int rm_index)
|
||||
@@ -630,7 +644,7 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index)
|
||||
int i;
|
||||
struct team_port *port;
|
||||
|
||||
- for (i = rm_index + 1; i < team->port_count; i++) {
|
||||
+ for (i = rm_index + 1; i < team->en_port_count; i++) {
|
||||
port = team_get_port_by_index(team, i);
|
||||
hlist_del_rcu(&port->hlist);
|
||||
port->index--;
|
||||
@@ -639,15 +653,17 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index)
|
||||
}
|
||||
}
|
||||
|
||||
-static void team_port_list_del_port(struct team *team,
|
||||
- struct team_port *port)
|
||||
+static void team_port_disable(struct team *team,
|
||||
+ struct team_port *port)
|
||||
{
|
||||
int rm_index = port->index;
|
||||
|
||||
+ if (!team_port_enabled(port))
|
||||
+ return;
|
||||
hlist_del_rcu(&port->hlist);
|
||||
- list_del_rcu(&port->list);
|
||||
__reconstruct_port_hlist(team, rm_index);
|
||||
- team->port_count--;
|
||||
+ team->en_port_count--;
|
||||
+ port->index = -1;
|
||||
}
|
||||
|
||||
#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
|
||||
@@ -800,7 +816,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
||||
goto err_option_port_add;
|
||||
}
|
||||
|
||||
- team_port_list_add_port(team, port);
|
||||
+ port->index = -1;
|
||||
+ team_port_enable(team, port);
|
||||
+ list_add_tail_rcu(&port->list, &team->port_list);
|
||||
team_adjust_ops(team);
|
||||
__team_compute_features(team);
|
||||
__team_port_change_check(port, !!netif_carrier_ok(port_dev));
|
||||
@@ -849,7 +867,8 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
||||
|
||||
port->removed = true;
|
||||
__team_port_change_check(port, false);
|
||||
- team_port_list_del_port(team, port);
|
||||
+ team_port_disable(team, port);
|
||||
+ list_del_rcu(&port->list);
|
||||
team_adjust_ops(team);
|
||||
team_option_port_del(team, port);
|
||||
netdev_rx_handler_unregister(port_dev);
|
||||
@@ -956,7 +975,7 @@ static int team_init(struct net_device *dev)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
|
||||
- INIT_HLIST_HEAD(&team->port_hlist[i]);
|
||||
+ INIT_HLIST_HEAD(&team->en_port_hlist[i]);
|
||||
INIT_LIST_HEAD(&team->port_list);
|
||||
|
||||
team_adjust_ops(team);
|
||||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
|
||||
index 438d5b8..86e8183 100644
|
||||
--- a/drivers/net/team/team_mode_loadbalance.c
|
||||
+++ b/drivers/net/team/team_mode_loadbalance.c
|
||||
@@ -38,7 +38,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb)
|
||||
if (unlikely(!fp))
|
||||
goto drop;
|
||||
hash = SK_RUN_FILTER(fp, skb);
|
||||
- port_index = hash % team->port_count;
|
||||
+ port_index = hash % team->en_port_count;
|
||||
port = team_get_port_by_index_rcu(team, port_index);
|
||||
if (unlikely(!port))
|
||||
goto drop;
|
||||
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
|
||||
index a0e8f80..6abfbdc 100644
|
||||
--- a/drivers/net/team/team_mode_roundrobin.c
|
||||
+++ b/drivers/net/team/team_mode_roundrobin.c
|
||||
@@ -50,7 +50,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb)
|
||||
struct team_port *port;
|
||||
int port_index;
|
||||
|
||||
- port_index = rr_priv(team)->sent_packets++ % team->port_count;
|
||||
+ port_index = rr_priv(team)->sent_packets++ % team->en_port_count;
|
||||
port = team_get_port_by_index_rcu(team, port_index);
|
||||
port = __get_first_port_up(team, port);
|
||||
if (unlikely(!port))
|
||||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
|
||||
index 5fd5ab1..8185f57 100644
|
||||
--- a/include/linux/if_team.h
|
||||
+++ b/include/linux/if_team.h
|
||||
@@ -28,10 +28,10 @@ struct team;
|
||||
|
||||
struct team_port {
|
||||
struct net_device *dev;
|
||||
- struct hlist_node hlist; /* node in hash list */
|
||||
+ struct hlist_node hlist; /* node in enabled ports hash list */
|
||||
struct list_head list; /* node in ordinary list */
|
||||
struct team *team;
|
||||
- int index;
|
||||
+ int index; /* index of enabled port. If disabled, it's set to -1 */
|
||||
|
||||
bool linkup; /* either state.linkup or user.linkup */
|
||||
|
||||
@@ -125,11 +125,12 @@ struct team {
|
||||
struct mutex lock; /* used for overall locking, e.g. port lists write */
|
||||
|
||||
/*
|
||||
- * port lists with port count
|
||||
+ * List of enabled ports and their count
|
||||
*/
|
||||
- int port_count;
|
||||
- struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES];
|
||||
- struct list_head port_list;
|
||||
+ int en_port_count;
|
||||
+ struct hlist_head en_port_hlist[TEAM_PORT_HASHENTRIES];
|
||||
+
|
||||
+ struct list_head port_list; /* list of all ports */
|
||||
|
||||
struct list_head option_list;
|
||||
struct list_head option_inst_list; /* list of option instances */
|
||||
@@ -142,7 +143,7 @@ struct team {
|
||||
static inline struct hlist_head *team_port_index_hash(struct team *team,
|
||||
int port_index)
|
||||
{
|
||||
- return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
|
||||
+ return &team->en_port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
|
||||
}
|
||||
|
||||
static inline struct team_port *team_get_port_by_index(struct team *team,
|
54
debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch
vendored
Normal file
54
debian/patches/features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
From: Jiri Pirko <jpirko@redhat.com>
|
||||
Date: Fri, 20 Apr 2012 04:42:06 +0000
|
||||
Subject: [23/23] team: add per-port option for enabling/disabling ports
|
||||
|
||||
commit acd69962341a956b5bcc5b4178b70fa527d7ce11 upstream.
|
||||
|
||||
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/team/team.c | 24 ++++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
||||
index fe7ca40..c61ae35 100644
|
||||
--- a/drivers/net/team/team.c
|
||||
+++ b/drivers/net/team/team.c
|
||||
@@ -904,6 +904,23 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
|
||||
return team_change_mode(team, ctx->data.str_val);
|
||||
}
|
||||
|
||||
+static int team_port_en_option_get(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ ctx->data.bool_val = team_port_enabled(ctx->port);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int team_port_en_option_set(struct team *team,
|
||||
+ struct team_gsetter_ctx *ctx)
|
||||
+{
|
||||
+ if (ctx->data.bool_val)
|
||||
+ team_port_enable(team, ctx->port);
|
||||
+ else
|
||||
+ team_port_disable(team, ctx->port);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int team_user_linkup_option_get(struct team *team,
|
||||
struct team_gsetter_ctx *ctx)
|
||||
{
|
||||
@@ -946,6 +963,13 @@ static const struct team_option team_options[] = {
|
||||
.setter = team_mode_option_set,
|
||||
},
|
||||
{
|
||||
+ .name = "enabled",
|
||||
+ .type = TEAM_OPTION_TYPE_BOOL,
|
||||
+ .per_port = true,
|
||||
+ .getter = team_port_en_option_get,
|
||||
+ .setter = team_port_en_option_set,
|
||||
+ },
|
||||
+ {
|
||||
.name = "user_linkup",
|
||||
.type = TEAM_OPTION_TYPE_BOOL,
|
||||
.per_port = true,
|
|
@ -309,3 +309,31 @@ bugfix/all/mm-fix-vma_resv_map-null-pointer.patch
|
|||
bugfix/all/hugepages-fix-use-after-free-bug-in-quota-handling.patch
|
||||
|
||||
bugfix/all/fix-scsi_wait_scan.patch
|
||||
|
||||
features/all/define-netdev_features_t.patch
|
||||
features/all/filter-Allow-to-create-sk-unattached-filters.patch
|
||||
|
||||
# team driver from 3.5ish
|
||||
features/all/team/0001-net-introduce-ethernet-teaming-device.patch
|
||||
features/all/team/0002-team-Do-not-hold-rcu_read_lock-when-running-netlink-.patch
|
||||
features/all/team/0003-team-convert-overall-spinlock-to-mutex.patch
|
||||
features/all/team/0004-team-replicate-options-on-register.patch
|
||||
features/all/team/0005-team-add-fix_features.patch
|
||||
features/all/team/0006-team-avoid-using-variable-length-array.patch
|
||||
features/all/team/0007-team-replace-kmalloc-memcpy-by-kmemdup.patch
|
||||
features/all/team/0008-net-treewide-use-of-RCU_INIT_POINTER.patch
|
||||
features/all/team/0009-net-introduce-vlan_vid_-add-del-and-use-them-instead.patch
|
||||
features/all/team/0010-vlan-introduce-functions-to-do-mass-addition-deletio.patch
|
||||
features/all/team/0011-team-use-vlan_vids_-addr-del-_by_dev.patch
|
||||
features/all/team/0012-team-send-only-changed-options-ports-via-netlink.patch
|
||||
features/all/team/0013-team-Stop-using-NLA_PUT.patch
|
||||
features/all/team/0014-team-add-binary-option-type.patch
|
||||
features/all/team/0015-team-add-loadbalance-mode.patch
|
||||
features/all/team/0016-team-add-support-for-per-port-options.patch
|
||||
features/all/team/0017-team-add-bool-option-type.patch
|
||||
features/all/team/0018-team-add-user_linkup-and-user_linkup_enabled-per-por.patch
|
||||
features/all/team/0019-team-ab-walk-through-port-list-non-rcu.patch
|
||||
features/all/team/0020-team-add-missed-statics.patch
|
||||
features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch
|
||||
features/all/team/0022-team-allow-to-enable-disable-ports.patch
|
||||
features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch
|
||||
|
|
Loading…
Reference in New Issue