netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch net-next 00/19] team: couple of patches
@ 2012-06-19 15:54 Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 01/19] team: make team_mode struct const Jiri Pirko
                   ` (19 more replies)
  0 siblings, 20 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

The main impact is this patchset provides an implementation of userspace driven
TX loadbalancing for team driver.

Also couple of typos are fixed, minor issues fixes.

Jiri Pirko (19):
  team: make team_mode struct const
  team: for nomode use dummy struct team_mode
  team: add mode priv to port
  team: lb: push hash counting into separate function
  team: allow read/write-only options
  team: introduce array options
  team: comments: s/net\/drivers\/team/drivers\/net\/team/
  team: push array_index and port into separate structure
  team: allow async option changes
  team: fix error path in team_nl_fill_options_get()
  team: fix error path in team_nl_fill_port_list_get()
  team: allow to specify one option instance to be send to userspace
  team: pass NULL to __team_option_inst_add() instead of 0
  team: add port_[enabled/disabled] mode callbacks
  team: lb: introduce infrastructure for userspace driven tx
    loadbalancing
  team: implement multipart netlink messages for options transfers
  team: ensure correct order of netlink messages delivery
  team: allow to send multiple set events in one message
  team: use rcu_dereference_bh() in tx path

 drivers/net/team/team.c                   |  546 ++++++++++++++++++-----------
 drivers/net/team/team_mode_activebackup.c |    6 +-
 drivers/net/team/team_mode_loadbalance.c  |  536 ++++++++++++++++++++++++++--
 drivers/net/team/team_mode_roundrobin.c   |    4 +-
 include/linux/if_team.h                   |   22 +-
 5 files changed, 885 insertions(+), 229 deletions(-)

-- 
1.7.10.2

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [patch net-next 01/19] team: make team_mode struct const
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 02/19] team: for nomode use dummy struct team_mode Jiri Pirko
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c                   |   55 ++++++++++++++++++++---------
 drivers/net/team/team_mode_activebackup.c |    2 +-
 drivers/net/team/team_mode_loadbalance.c  |    2 +-
 drivers/net/team/team_mode_roundrobin.c   |    2 +-
 include/linux/if_team.h                   |    5 ++-
 5 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index c61ae35..bdf87a9 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -371,13 +371,18 @@ static int team_option_set(struct team *team,
 static LIST_HEAD(mode_list);
 static DEFINE_SPINLOCK(mode_list_lock);
 
-static struct team_mode *__find_mode(const char *kind)
+struct team_mode_item {
+	struct list_head list;
+	const struct team_mode *mode;
+};
+
+static struct team_mode_item *__find_mode(const char *kind)
 {
-	struct team_mode *mode;
+	struct team_mode_item *mitem;
 
-	list_for_each_entry(mode, &mode_list, list) {
-		if (strcmp(mode->kind, kind) == 0)
-			return mode;
+	list_for_each_entry(mitem, &mode_list, list) {
+		if (strcmp(mitem->mode->kind, kind) == 0)
+			return mitem;
 	}
 	return NULL;
 }
@@ -392,49 +397,65 @@ static bool is_good_mode_name(const char *name)
 	return true;
 }
 
-int team_mode_register(struct team_mode *mode)
+int team_mode_register(const struct team_mode *mode)
 {
 	int err = 0;
+	struct team_mode_item *mitem;
 
 	if (!is_good_mode_name(mode->kind) ||
 	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
 		return -EINVAL;
+
+	mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
+	if (!mitem)
+		return -ENOMEM;
+
 	spin_lock(&mode_list_lock);
 	if (__find_mode(mode->kind)) {
 		err = -EEXIST;
+		kfree(mitem);
 		goto unlock;
 	}
-	list_add_tail(&mode->list, &mode_list);
+	mitem->mode = mode;
+	list_add_tail(&mitem->list, &mode_list);
 unlock:
 	spin_unlock(&mode_list_lock);
 	return err;
 }
 EXPORT_SYMBOL(team_mode_register);
 
-int team_mode_unregister(struct team_mode *mode)
+void team_mode_unregister(const struct team_mode *mode)
 {
+	struct team_mode_item *mitem;
+
 	spin_lock(&mode_list_lock);
-	list_del_init(&mode->list);
+	mitem = __find_mode(mode->kind);
+	if (mitem) {
+		list_del_init(&mitem->list);
+		kfree(mitem);
+	}
 	spin_unlock(&mode_list_lock);
-	return 0;
 }
 EXPORT_SYMBOL(team_mode_unregister);
 
-static struct team_mode *team_mode_get(const char *kind)
+static const struct team_mode *team_mode_get(const char *kind)
 {
-	struct team_mode *mode;
+	struct team_mode_item *mitem;
+	const struct team_mode *mode = NULL;
 
 	spin_lock(&mode_list_lock);
-	mode = __find_mode(kind);
-	if (!mode) {
+	mitem = __find_mode(kind);
+	if (!mitem) {
 		spin_unlock(&mode_list_lock);
 		request_module("team-mode-%s", kind);
 		spin_lock(&mode_list_lock);
-		mode = __find_mode(kind);
+		mitem = __find_mode(kind);
 	}
-	if (mode)
+	if (mitem) {
+		mode = mitem->mode;
 		if (!try_module_get(mode->owner))
 			mode = NULL;
+	}
 
 	spin_unlock(&mode_list_lock);
 	return mode;
@@ -523,7 +544,7 @@ static int __team_change_mode(struct team *team,
 
 static int team_change_mode(struct team *team, const char *kind)
 {
-	struct team_mode *new_mode;
+	const struct team_mode *new_mode;
 	struct net_device *dev = team->dev;
 	int err;
 
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
index fd6bd03..acd925f 100644
--- a/drivers/net/team/team_mode_activebackup.c
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -108,7 +108,7 @@ static const struct team_mode_ops ab_mode_ops = {
 	.port_leave		= ab_port_leave,
 };
 
-static struct team_mode ab_mode = {
+static const struct team_mode ab_mode = {
 	.kind		= "activebackup",
 	.owner		= THIS_MODULE,
 	.priv_size	= sizeof(struct ab_priv),
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index 86e8183..6452428 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -148,7 +148,7 @@ static const struct team_mode_ops lb_mode_ops = {
 	.transmit		= lb_transmit,
 };
 
-static struct team_mode lb_mode = {
+static const struct team_mode lb_mode = {
 	.kind		= "loadbalance",
 	.owner		= THIS_MODULE,
 	.priv_size	= sizeof(struct lb_priv),
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
index 6abfbdc..daafca2 100644
--- a/drivers/net/team/team_mode_roundrobin.c
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -81,7 +81,7 @@ static const struct team_mode_ops rr_mode_ops = {
 	.port_change_mac	= rr_port_change_mac,
 };
 
-static struct team_mode rr_mode = {
+static const struct team_mode rr_mode = {
 	.kind		= "roundrobin",
 	.owner		= THIS_MODULE,
 	.priv_size	= sizeof(struct rr_priv),
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 8185f57..d45fcd5 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -105,7 +105,6 @@ struct team_option {
 };
 
 struct team_mode {
-	struct list_head list;
 	const char *kind;
 	struct module *owner;
 	size_t priv_size;
@@ -178,8 +177,8 @@ extern int team_options_register(struct team *team,
 extern void team_options_unregister(struct team *team,
 				    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);
+extern int team_mode_register(const struct team_mode *mode);
+extern void team_mode_unregister(const struct team_mode *mode);
 
 #endif /* __KERNEL__ */
 
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 02/19] team: for nomode use dummy struct team_mode
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 01/19] team: make team_mode struct const Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 03/19] team: add mode priv to port Jiri Pirko
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

That leaves team->mode and all its values valid so no checks would be
needed (for example in team_mode_option_get()).

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index bdf87a9..343f4ff 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -479,6 +479,20 @@ rx_handler_result_t team_dummy_receive(struct team *team,
 	return RX_HANDLER_ANOTHER;
 }
 
+static const struct team_mode __team_no_mode = {
+	.kind		= "*NOMODE*",
+};
+
+static bool team_is_mode_set(struct team *team)
+{
+	return team->mode != &__team_no_mode;
+}
+
+static void team_set_no_mode(struct team *team)
+{
+	team->mode = &__team_no_mode;
+}
+
 static void team_adjust_ops(struct team *team)
 {
 	/*
@@ -487,13 +501,13 @@ static void team_adjust_ops(struct team *team)
 	 */
 
 	if (list_empty(&team->port_list) ||
-	    !team->mode || !team->mode->ops->transmit)
+	    !team_is_mode_set(team) || !team->mode->ops->transmit)
 		team->ops.transmit = team_dummy_transmit;
 	else
 		team->ops.transmit = team->mode->ops->transmit;
 
 	if (list_empty(&team->port_list) ||
-	    !team->mode || !team->mode->ops->receive)
+	    !team_is_mode_set(team) || !team->mode->ops->receive)
 		team->ops.receive = team_dummy_receive;
 	else
 		team->ops.receive = team->mode->ops->receive;
@@ -508,7 +522,7 @@ static int __team_change_mode(struct team *team,
 			      const struct team_mode *new_mode)
 {
 	/* Check if mode was previously set and do cleanup if so */
-	if (team->mode) {
+	if (team_is_mode_set(team)) {
 		void (*exit_op)(struct team *team) = team->ops.exit;
 
 		/* Clear ops area so no callback is called any longer */
@@ -518,7 +532,7 @@ static int __team_change_mode(struct team *team,
 		if (exit_op)
 			exit_op(team);
 		team_mode_put(team->mode);
-		team->mode = NULL;
+		team_set_no_mode(team);
 		/* zero private data area */
 		memset(&team->mode_priv, 0,
 		       sizeof(struct team) - offsetof(struct team, mode_priv));
@@ -553,7 +567,7 @@ static int team_change_mode(struct team *team, const char *kind)
 		return -EBUSY;
 	}
 
-	if (team->mode && strcmp(team->mode->kind, kind) == 0) {
+	if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
 		netdev_err(dev, "Unable to change to the same mode the team is in\n");
 		return -EINVAL;
 	}
@@ -912,11 +926,9 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
  * Net device ops
  *****************/
 
-static const char team_no_mode_kind[] = "*NOMODE*";
-
 static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
 {
-	ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind;
+	ctx->data.str_val = team->mode->kind;
 	return 0;
 }
 
@@ -1014,6 +1026,7 @@ static int team_init(struct net_device *dev)
 
 	team->dev = dev;
 	mutex_init(&team->lock);
+	team_set_no_mode(team);
 
 	team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
 	if (!team->pcpu_stats)
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 03/19] team: add mode priv to port
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 01/19] team: make team_mode struct const Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 02/19] team: for nomode use dummy struct team_mode Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 04/19] team: lb: push hash counting into separate function Jiri Pirko
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    3 ++-
 include/linux/if_team.h |    2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 343f4ff..dea2d8a 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -793,7 +793,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
 		return -EBUSY;
 	}
 
-	port = kzalloc(sizeof(struct team_port), GFP_KERNEL);
+	port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
+		       GFP_KERNEL);
 	if (!port)
 		return -ENOMEM;
 
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index d45fcd5..54af95f 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -61,6 +61,7 @@ struct team_port {
 	} orig;
 
 	struct rcu_head rcu;
+	long mode_priv[0];
 };
 
 struct team_mode_ops {
@@ -108,6 +109,7 @@ struct team_mode {
 	const char *kind;
 	struct module *owner;
 	size_t priv_size;
+	size_t port_priv_size;
 	const struct team_mode_ops *ops;
 };
 
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 04/19] team: lb: push hash counting into separate function
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (2 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 03/19] team: add mode priv to port Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 05/19] team: allow read/write-only options Jiri Pirko
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Also squash hash into one byte

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team_mode_loadbalance.c |   23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index 6452428..a475b13 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -27,18 +27,27 @@ 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)
+static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv,
+				     struct sk_buff *skb)
 {
 	struct sk_filter *fp;
+	uint32_t lhash;
+	unsigned char *c;
+
+	fp = rcu_dereference(lb_priv->fp);
+	if (unlikely(!fp))
+		return 0;
+	lhash = SK_RUN_FILTER(fp, skb);
+	c = (char *) &lhash;
+	return c[0] ^ c[1] ^ c[2] ^ c[3];
+}
+
+static bool lb_transmit(struct team *team, struct sk_buff *skb)
+{
 	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->en_port_count;
+	port_index = lb_get_skb_hash(lb_priv(team), skb) % team->en_port_count;
 	port = team_get_port_by_index_rcu(team, port_index);
 	if (unlikely(!port))
 		goto drop;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 05/19] team: allow read/write-only options
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (3 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 04/19] team: lb: push hash counting into separate function Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 06/19] team: introduce array options Jiri Pirko
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index dea2d8a..f50b8ca 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -346,6 +346,8 @@ static int team_option_get(struct team *team,
 			   struct team_option_inst *opt_inst,
 			   struct team_gsetter_ctx *ctx)
 {
+	if (!opt_inst->option->getter)
+		return -EOPNOTSUPP;
 	return opt_inst->option->getter(team, ctx);
 }
 
@@ -355,6 +357,8 @@ static int team_option_set(struct team *team,
 {
 	int err;
 
+	if (!opt_inst->option->setter)
+		return -EOPNOTSUPP;
 	err = opt_inst->option->setter(team, ctx);
 	if (err)
 		return err;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 06/19] team: introduce array options
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (4 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 05/19] team: allow read/write-only options Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 07/19] team: comments: s/net\/drivers\/team/drivers\/net\/team/ Jiri Pirko
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   75 ++++++++++++++++++++++++++++++++---------------
 include/linux/if_team.h |    3 ++
 2 files changed, 55 insertions(+), 23 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index f50b8ca..32cb290 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -90,6 +90,7 @@ 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 */
+	u32 array_index;
 	bool changed;
 	bool removed;
 };
@@ -106,22 +107,6 @@ static struct team_option *__team_find_option(struct team *team,
 	return NULL;
 }
 
-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);
@@ -139,14 +124,42 @@ static void __team_option_inst_del_option(struct team *team,
 	}
 }
 
+static int __team_option_inst_add(struct team *team, struct team_option *option,
+				  struct team_port *port)
+{
+	struct team_option_inst *opt_inst;
+	unsigned int array_size;
+	unsigned int i;
+
+	array_size = option->array_size;
+	if (!array_size)
+		array_size = 1; /* No array but still need one instance */
+
+	for (i = 0; i < array_size; i++) {
+		opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
+		if (!opt_inst)
+			return -ENOMEM;
+		opt_inst->option = option;
+		opt_inst->port = port;
+		opt_inst->array_index = i;
+		opt_inst->changed = true;
+		opt_inst->removed = false;
+		list_add_tail(&opt_inst->list, &team->option_inst_list);
+	}
+	return 0;
+}
+
 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);
+	if (!option->per_port) {
+		err = __team_option_inst_add(team, option, 0);
+		if (err)
+			goto inst_del_option;
+	}
 
 	list_for_each_entry(port, &team->port_list, list) {
 		err = __team_option_inst_add(team, option, port);
@@ -1567,6 +1580,11 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 				opt_inst->port->dev->ifindex))
 			goto nla_put_failure;
 		ctx.port = opt_inst->port;
+		if (opt_inst->option->array_size &&
+		    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
+				opt_inst->array_index))
+			goto nla_put_failure;
+		ctx.array_index = opt_inst->array_index;
 		switch (option->type) {
 		case TEAM_OPTION_TYPE_U32:
 			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
@@ -1668,10 +1686,12 @@ 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;
 		struct nlattr *attr_data;
 		enum team_option_type opt_type;
 		int opt_port_ifindex = 0; /* != 0 for per-port options */
+		u32 opt_array_index = 0;
+		bool opt_is_array = false;
 		struct team_option_inst *opt_inst;
 		char *opt_name;
 		bool opt_found = false;
@@ -1713,9 +1733,15 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 		}
 
 		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);
+		attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
+		if (attr)
+			opt_port_ifindex = nla_get_u32(attr);
+
+		attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
+		if (attr) {
+			opt_is_array = true;
+			opt_array_index = nla_get_u32(attr);
+		}
 
 		list_for_each_entry(opt_inst, &team->option_inst_list, list) {
 			struct team_option *option = opt_inst->option;
@@ -1726,10 +1752,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 				      opt_inst->port->dev->ifindex : 0;
 			if (option->type != opt_type ||
 			    strcmp(option->name, opt_name) ||
-			    tmp_ifindex != opt_port_ifindex)
+			    tmp_ifindex != opt_port_ifindex ||
+			    (option->array_size && !opt_is_array) ||
+			    opt_inst->array_index != opt_array_index)
 				continue;
 			opt_found = true;
 			ctx.port = opt_inst->port;
+			ctx.array_index = opt_inst->array_index;
 			switch (opt_type) {
 			case TEAM_OPTION_TYPE_U32:
 				ctx.data.u32_val = nla_get_u32(attr_data);
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 54af95f..b1719e2 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -93,6 +93,7 @@ struct team_gsetter_ctx {
 		} bin_val;
 		bool bool_val;
 	} data;
+	u32 array_index;
 	struct team_port *port;
 };
 
@@ -100,6 +101,7 @@ struct team_option {
 	struct list_head list;
 	const char *name;
 	bool per_port;
+	unsigned int array_size; /* != 0 means the option is array */
 	enum team_option_type type;
 	int (*getter)(struct team *team, struct team_gsetter_ctx *ctx);
 	int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
@@ -242,6 +244,7 @@ enum {
 	TEAM_ATTR_OPTION_DATA,		/* dynamic */
 	TEAM_ATTR_OPTION_REMOVED,	/* flag */
 	TEAM_ATTR_OPTION_PORT_IFINDEX,	/* u32 */ /* for per-port options */
+	TEAM_ATTR_OPTION_ARRAY_INDEX,	/* u32 */ /* for array options */
 
 	__TEAM_ATTR_OPTION_MAX,
 	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 07/19] team: comments: s/net\/drivers\/team/drivers\/net\/team/
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (5 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 06/19] team: introduce array options Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 08/19] team: push array_index and port into separate structure Jiri Pirko
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c                   |    2 +-
 drivers/net/team/team_mode_activebackup.c |    2 +-
 drivers/net/team/team_mode_roundrobin.c   |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 32cb290..7ec53f8 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1,5 +1,5 @@
 /*
- * net/drivers/team/team.c - Network team device driver
+ * drivers/net/team/team.c - Network team device driver
  * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
index acd925f..bcc7d6d 100644
--- a/drivers/net/team/team_mode_activebackup.c
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -1,5 +1,5 @@
 /*
- * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team
+ * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team
  * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
index daafca2..52dd0ec 100644
--- a/drivers/net/team/team_mode_roundrobin.c
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -1,5 +1,5 @@
 /*
- * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team
+ * drivers/net/team/team_mode_roundrobin.c - Round-robin mode for team
  * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 08/19] team: push array_index and port into separate structure
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (6 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 07/19] team: comments: s/net\/drivers\/team/drivers\/net\/team/ Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 09/19] team: allow async option changes Jiri Pirko
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Introduce struct team_option_inst_info and push option instance info
there. It can be then easily passed to gsetter context and used for
feature async option changes.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   68 ++++++++++++++++++++++++++++++-----------------
 include/linux/if_team.h |    9 +++++--
 2 files changed, 50 insertions(+), 27 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 7ec53f8..cff8e25 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -89,8 +89,7 @@ static void team_refresh_port_linkup(struct team_port *port)
 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 */
-	u32 array_index;
+	struct team_option_inst_info info;
 	bool changed;
 	bool removed;
 };
@@ -130,6 +129,7 @@ static int __team_option_inst_add(struct team *team, struct team_option *option,
 	struct team_option_inst *opt_inst;
 	unsigned int array_size;
 	unsigned int i;
+	int err;
 
 	array_size = option->array_size;
 	if (!array_size)
@@ -140,11 +140,17 @@ static int __team_option_inst_add(struct team *team, struct team_option *option,
 		if (!opt_inst)
 			return -ENOMEM;
 		opt_inst->option = option;
-		opt_inst->port = port;
-		opt_inst->array_index = i;
+		opt_inst->info.port = port;
+		opt_inst->info.array_index = i;
 		opt_inst->changed = true;
 		opt_inst->removed = false;
 		list_add_tail(&opt_inst->list, &team->option_inst_list);
+		if (option->init) {
+			err = option->init(team, &opt_inst->info);
+			if (err)
+				return err;
+		}
+
 	}
 	return 0;
 }
@@ -193,7 +199,7 @@ static void __team_option_inst_del_port(struct team *team,
 
 	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
 		if (opt_inst->option->per_port &&
-		    opt_inst->port == port)
+		    opt_inst->info.port == port)
 			__team_option_inst_del(opt_inst);
 	}
 }
@@ -224,7 +230,7 @@ static void __team_option_inst_mark_removed_port(struct team *team,
 	struct team_option_inst *opt_inst;
 
 	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
-		if (opt_inst->port == port) {
+		if (opt_inst->info.port == port) {
 			opt_inst->changed = true;
 			opt_inst->removed = true;
 		}
@@ -958,39 +964,47 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
 static int team_port_en_option_get(struct team *team,
 				   struct team_gsetter_ctx *ctx)
 {
-	ctx->data.bool_val = team_port_enabled(ctx->port);
+	struct team_port *port = ctx->info->port;
+
+	ctx->data.bool_val = team_port_enabled(port);
 	return 0;
 }
 
 static int team_port_en_option_set(struct team *team,
 				   struct team_gsetter_ctx *ctx)
 {
+	struct team_port *port = ctx->info->port;
+
 	if (ctx->data.bool_val)
-		team_port_enable(team, ctx->port);
+		team_port_enable(team, port);
 	else
-		team_port_disable(team, ctx->port);
+		team_port_disable(team, port);
 	return 0;
 }
 
 static int team_user_linkup_option_get(struct team *team,
 				       struct team_gsetter_ctx *ctx)
 {
-	ctx->data.bool_val = ctx->port->user.linkup;
+	struct team_port *port = ctx->info->port;
+
+	ctx->data.bool_val = 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);
+	struct team_port *port = ctx->info->port;
+
+	port->user.linkup = ctx->data.bool_val;
+	team_refresh_port_linkup(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;
+	struct team_port *port = ctx->info->port;
 
 	ctx->data.bool_val = port->user.linkup_enabled;
 	return 0;
@@ -999,10 +1013,10 @@ static int team_user_linkup_en_option_get(struct team *team,
 static int team_user_linkup_en_option_set(struct team *team,
 					  struct team_gsetter_ctx *ctx)
 {
-	struct team_port *port = ctx->port;
+	struct team_port *port = ctx->info->port;
 
 	port->user.linkup_enabled = ctx->data.bool_val;
-	team_refresh_port_linkup(ctx->port);
+	team_refresh_port_linkup(port);
 	return 0;
 }
 
@@ -1557,6 +1571,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
 		struct nlattr *option_item;
 		struct team_option *option = opt_inst->option;
+		struct team_option_inst_info *opt_inst_info;
 		struct team_gsetter_ctx ctx;
 
 		/* Include only changed options if fill all mode is not on */
@@ -1575,16 +1590,18 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 		if (opt_inst->removed &&
 		    nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
 			goto nla_put_failure;
-		if (opt_inst->port &&
+
+		opt_inst_info = &opt_inst->info;
+		if (opt_inst_info->port &&
 		    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
-				opt_inst->port->dev->ifindex))
+				opt_inst_info->port->dev->ifindex))
 			goto nla_put_failure;
-		ctx.port = opt_inst->port;
 		if (opt_inst->option->array_size &&
 		    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
-				opt_inst->array_index))
+				opt_inst_info->array_index))
 			goto nla_put_failure;
-		ctx.array_index = opt_inst->array_index;
+		ctx.info = opt_inst_info;
+
 		switch (option->type) {
 		case TEAM_OPTION_TYPE_U32:
 			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
@@ -1746,19 +1763,20 @@ 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 team_gsetter_ctx ctx;
+			struct team_option_inst_info *opt_inst_info;
 			int tmp_ifindex;
 
-			tmp_ifindex = opt_inst->port ?
-				      opt_inst->port->dev->ifindex : 0;
+			opt_inst_info = &opt_inst->info;
+			tmp_ifindex = opt_inst_info->port ?
+				      opt_inst_info->port->dev->ifindex : 0;
 			if (option->type != opt_type ||
 			    strcmp(option->name, opt_name) ||
 			    tmp_ifindex != opt_port_ifindex ||
 			    (option->array_size && !opt_is_array) ||
-			    opt_inst->array_index != opt_array_index)
+			    opt_inst_info->array_index != opt_array_index)
 				continue;
 			opt_found = true;
-			ctx.port = opt_inst->port;
-			ctx.array_index = opt_inst->array_index;
+			ctx.info = opt_inst_info;
 			switch (opt_type) {
 			case TEAM_OPTION_TYPE_U32:
 				ctx.data.u32_val = nla_get_u32(attr_data);
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index b1719e2..30854cb 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -83,6 +83,11 @@ enum team_option_type {
 	TEAM_OPTION_TYPE_BOOL,
 };
 
+struct team_option_inst_info {
+	u32 array_index;
+	struct team_port *port; /* != NULL if per-port */
+};
+
 struct team_gsetter_ctx {
 	union {
 		u32 u32_val;
@@ -93,8 +98,7 @@ struct team_gsetter_ctx {
 		} bin_val;
 		bool bool_val;
 	} data;
-	u32 array_index;
-	struct team_port *port;
+	struct team_option_inst_info *info;
 };
 
 struct team_option {
@@ -103,6 +107,7 @@ struct team_option {
 	bool per_port;
 	unsigned int array_size; /* != 0 means the option is array */
 	enum team_option_type type;
+	int (*init)(struct team *team, struct team_option_inst_info *info);
 	int (*getter)(struct team *team, struct team_gsetter_ctx *ctx);
 	int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
 };
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 09/19] team: allow async option changes
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (7 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 08/19] team: push array_index and port into separate structure Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 10/19] team: fix error path in team_nl_fill_options_get() Jiri Pirko
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

This patch adds two exported functions. One allows to mark option
instance as changed and the second processes change check and does
transfer of changed options to userspace.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   18 ++++++++++++++++++
 include/linux/if_team.h |    3 +++
 2 files changed, 21 insertions(+)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index cff8e25..7988ba0 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -82,6 +82,7 @@ static void team_refresh_port_linkup(struct team_port *port)
 						   port->state.linkup;
 }
 
+
 /*******************
  * Options handling
  *******************/
@@ -387,6 +388,22 @@ static int team_option_set(struct team *team,
 	return err;
 }
 
+void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
+{
+	struct team_option_inst *opt_inst;
+
+	opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
+	opt_inst->changed = true;
+}
+EXPORT_SYMBOL(team_option_inst_set_change);
+
+void team_options_change_check(struct team *team)
+{
+	__team_options_change_check(team);
+}
+EXPORT_SYMBOL(team_options_change_check);
+
+
 /****************
  * Mode handling
  ****************/
@@ -2051,6 +2068,7 @@ static void team_port_change_check(struct team_port *port, bool linkup)
 	mutex_unlock(&team->lock);
 }
 
+
 /************************************
  * Net device notifier event handler
  ************************************/
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 30854cb..2f29725 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -112,6 +112,9 @@ struct team_option {
 	int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
 };
 
+extern void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info);
+extern void team_options_change_check(struct team *team);
+
 struct team_mode {
 	const char *kind;
 	struct module *owner;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 10/19] team: fix error path in team_nl_fill_options_get()
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (8 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 09/19] team: allow async option changes Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 11/19] team: fix error path in team_nl_fill_port_list_get() Jiri Pirko
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

genlmsg_cancel() needs to be called in case nest fails

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 7988ba0..82ded18 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1583,7 +1583,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 		goto nla_put_failure;
 	option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
 	if (!option_list)
-		return -EMSGSIZE;
+		goto nla_put_failure;
 
 	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
 		struct nlattr *option_item;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 11/19] team: fix error path in team_nl_fill_port_list_get()
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (9 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 10/19] team: fix error path in team_nl_fill_options_get() Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 12/19] team: allow to specify one option instance to be send to userspace Jiri Pirko
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

genlmsg_cancel() needs to be called in case nest fails

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 82ded18..e977f44 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1849,7 +1849,7 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
 		goto nla_put_failure;
 	port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
 	if (!port_list)
-		return -EMSGSIZE;
+		goto nla_put_failure;
 
 	list_for_each_entry(port, &team->port_list, list) {
 		struct nlattr *port_item;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 12/19] team: allow to specify one option instance to be send to userspace
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (10 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 11/19] team: fix error path in team_nl_fill_port_list_get() Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 13/19] team: pass NULL to __team_option_inst_add() instead of 0 Jiri Pirko
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

No need to walk through option instance list and look for ->changed ==
true when called knows exactly what one option instance changed.

Also use lists to pass option instances needed to be present in netlink
message.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |  210 ++++++++++++++++++++++++++++-------------------
 1 file changed, 124 insertions(+), 86 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index e977f44..da72f41 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -89,6 +89,7 @@ static void team_refresh_port_linkup(struct team_port *port)
 
 struct team_option_inst { /* One for each option instance */
 	struct list_head list;
+	struct list_head tmp_list;
 	struct team_option *option;
 	struct team_option_inst_info info;
 	bool changed;
@@ -319,6 +320,8 @@ static void __team_options_unregister(struct team *team,
 }
 
 static void __team_options_change_check(struct team *team);
+static void __team_option_inst_change(struct team *team,
+				      struct team_option_inst *opt_inst);
 
 int team_options_register(struct team *team,
 			  const struct team_option *option,
@@ -383,8 +386,7 @@ static int team_option_set(struct team *team,
 	if (err)
 		return err;
 
-	opt_inst->changed = true;
-	__team_options_change_check(team);
+	__team_option_inst_change(team, opt_inst);
 	return err;
 }
 
@@ -1565,9 +1567,95 @@ err_fill:
 	return err;
 }
 
+static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
+				       struct team_option_inst *opt_inst)
+{
+	struct nlattr *option_item;
+	struct team_option *option = opt_inst->option;
+	struct team_option_inst_info *opt_inst_info;
+	struct team_gsetter_ctx ctx;
+	int err;
+
+	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 (opt_inst->changed) {
+		if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
+			goto nla_put_failure;
+		opt_inst->changed = false;
+	}
+	if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
+		goto nla_put_failure;
+
+	opt_inst_info = &opt_inst->info;
+	if (opt_inst_info->port &&
+	    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
+			opt_inst_info->port->dev->ifindex))
+		goto nla_put_failure;
+	if (opt_inst->option->array_size &&
+	    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
+			opt_inst_info->array_index))
+		goto nla_put_failure;
+	ctx.info = opt_inst_info;
+
+	switch (option->type) {
+	case TEAM_OPTION_TYPE_U32:
+		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
+			goto nla_put_failure;
+		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;
+		err = team_option_get(team, opt_inst, &ctx);
+		if (err)
+			goto errout;
+		if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
+				   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;
+		err = team_option_get(team, opt_inst, &ctx);
+		if (err)
+			goto errout;
+		if (nla_put(skb, TEAM_ATTR_OPTION_DATA, 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();
+	}
+	nla_nest_end(skb, option_item);
+	return 0;
+
+nla_put_failure:
+	err = -EMSGSIZE;
+errout:
+	return err;
+}
+
 static int team_nl_fill_options_get(struct sk_buff *skb,
 				    u32 pid, u32 seq, int flags,
-				    struct team *team, bool fillall)
+				    struct team *team,
+				    struct list_head *sel_opt_inst_list)
 {
 	struct nlattr *option_list;
 	void *hdr;
@@ -1585,85 +1673,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 	if (!option_list)
 		goto nla_put_failure;
 
-	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
-		struct nlattr *option_item;
-		struct team_option *option = opt_inst->option;
-		struct team_option_inst_info *opt_inst_info;
-		struct team_gsetter_ctx ctx;
-
-		/* Include only changed options if fill all mode is not on */
-		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 (opt_inst->changed) {
-			if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
-				goto nla_put_failure;
-			opt_inst->changed = false;
-		}
-		if (opt_inst->removed &&
-		    nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
-			goto nla_put_failure;
-
-		opt_inst_info = &opt_inst->info;
-		if (opt_inst_info->port &&
-		    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
-				opt_inst_info->port->dev->ifindex))
-			goto nla_put_failure;
-		if (opt_inst->option->array_size &&
-		    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
-				opt_inst_info->array_index))
-			goto nla_put_failure;
-		ctx.info = opt_inst_info;
-
-		switch (option->type) {
-		case TEAM_OPTION_TYPE_U32:
-			if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
-				goto nla_put_failure;
-			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;
-			err = team_option_get(team, opt_inst, &ctx);
-			if (err)
-				goto errout;
-			if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
-					   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;
-			err = team_option_get(team, opt_inst, &ctx);
-			if (err)
-				goto errout;
-			if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
-				    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();
-		}
-		nla_nest_end(skb, option_item);
+	list_for_each_entry(opt_inst, sel_opt_inst_list, tmp_list) {
+		err = team_nl_fill_one_option_get(skb, team, opt_inst);
+		if (err)
+			goto errout;
 	}
 
 	nla_nest_end(skb, option_list);
@@ -1680,9 +1693,14 @@ static int team_nl_fill_options_get_all(struct sk_buff *skb,
 					struct genl_info *info, int flags,
 					struct team *team)
 {
+	struct team_option_inst *opt_inst;
+	LIST_HEAD(sel_opt_inst_list);
+
+	list_for_each_entry(opt_inst, &team->option_inst_list, list)
+		list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
 	return team_nl_fill_options_get(skb, info->snd_pid,
 					info->snd_seq, NLM_F_ACK,
-					team, true);
+					team, &sel_opt_inst_list);
 }
 
 static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
@@ -1941,7 +1959,8 @@ 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)
+static int team_nl_send_event_options_get(struct team *team,
+					  struct list_head *sel_opt_inst_list)
 {
 	struct sk_buff *skb;
 	int err;
@@ -1951,7 +1970,7 @@ static int team_nl_send_event_options_get(struct team *team)
 	if (!skb)
 		return -ENOMEM;
 
-	err = team_nl_fill_options_get(skb, 0, 0, 0, team, false);
+	err = team_nl_fill_options_get(skb, 0, 0, 0, team, sel_opt_inst_list);
 	if (err < 0)
 		goto err_fill;
 
@@ -2021,12 +2040,31 @@ static void team_nl_fini(void)
 static void __team_options_change_check(struct team *team)
 {
 	int err;
+	struct team_option_inst *opt_inst;
+	LIST_HEAD(sel_opt_inst_list);
 
-	err = team_nl_send_event_options_get(team);
+	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+		if (opt_inst->changed)
+			list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
+	}
+	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
 	if (err)
 		netdev_warn(team->dev, "Failed to send options change via netlink\n");
 }
 
+static void __team_option_inst_change(struct team *team,
+				      struct team_option_inst *sel_opt_inst)
+{
+	int err;
+	LIST_HEAD(sel_opt_inst_list);
+
+	sel_opt_inst->changed = true;
+	list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list);
+	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
+	if (err)
+		netdev_warn(team->dev, "Failed to send option change via netlink\n");
+}
+
 /* rtnl lock is held */
 static void __team_port_change_check(struct team_port *port, bool linkup)
 {
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 13/19] team: pass NULL to __team_option_inst_add() instead of 0
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (11 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 12/19] team: allow to specify one option instance to be send to userspace Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 14/19] team: add port_[enabled/disabled] mode callbacks Jiri Pirko
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index da72f41..eb18ac9 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -164,7 +164,7 @@ static int __team_option_inst_add_option(struct team *team,
 	int err;
 
 	if (!option->per_port) {
-		err = __team_option_inst_add(team, option, 0);
+		err = __team_option_inst_add(team, option, NULL);
 		if (err)
 			goto inst_del_option;
 	}
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 14/19] team: add port_[enabled/disabled] mode callbacks
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (12 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 13/19] team: pass NULL to __team_option_inst_add() instead of 0 Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 15/19] team: lb: introduce infrastructure for userspace driven tx loadbalancing Jiri Pirko
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |    4 ++++
 include/linux/if_team.h |    2 ++
 2 files changed, 6 insertions(+)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index eb18ac9..bc76f94 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -714,6 +714,8 @@ static void team_port_enable(struct team *team,
 	port->index = team->en_port_count++;
 	hlist_add_head_rcu(&port->hlist,
 			   team_port_index_hash(team, port->index));
+	if (team->ops.port_enabled)
+		team->ops.port_enabled(team, port);
 }
 
 static void __reconstruct_port_hlist(struct team *team, int rm_index)
@@ -737,6 +739,8 @@ static void team_port_disable(struct team *team,
 
 	if (!team_port_enabled(port))
 		return;
+	if (team->ops.port_disabled)
+		team->ops.port_disabled(team, port);
 	hlist_del_rcu(&port->hlist);
 	__reconstruct_port_hlist(team, rm_index);
 	team->en_port_count--;
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 2f29725..c193886 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -74,6 +74,8 @@ struct team_mode_ops {
 	int (*port_enter)(struct team *team, struct team_port *port);
 	void (*port_leave)(struct team *team, struct team_port *port);
 	void (*port_change_mac)(struct team *team, struct team_port *port);
+	void (*port_enabled)(struct team *team, struct team_port *port);
+	void (*port_disabled)(struct team *team, struct team_port *port);
 };
 
 enum team_option_type {
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 15/19] team: lb: introduce infrastructure for userspace driven tx loadbalancing
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (13 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 14/19] team: add port_[enabled/disabled] mode callbacks Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 16/19] team: implement multipart netlink messages for options transfers Jiri Pirko
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team_mode_loadbalance.c |  517 +++++++++++++++++++++++++++++-
 1 file changed, 500 insertions(+), 17 deletions(-)

diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index a475b13..33e30ed 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -17,18 +17,163 @@
 #include <linux/filter.h>
 #include <linux/if_team.h>
 
+struct lb_priv;
+
+typedef struct team_port *lb_select_tx_port_func_t(struct team *,
+						   struct lb_priv *,
+						   struct sk_buff *,
+						   unsigned char);
+
+#define LB_TX_HASHTABLE_SIZE 256 /* hash is a char */
+
+struct lb_stats {
+	u64 tx_bytes;
+};
+
+struct lb_pcpu_stats {
+	struct lb_stats hash_stats[LB_TX_HASHTABLE_SIZE];
+	struct u64_stats_sync syncp;
+};
+
+struct lb_stats_info {
+	struct lb_stats stats;
+	struct lb_stats last_stats;
+	struct team_option_inst_info *opt_inst_info;
+};
+
+struct lb_port_mapping {
+	struct team_port __rcu *port;
+	struct team_option_inst_info *opt_inst_info;
+};
+
+struct lb_priv_ex {
+	struct team *team;
+	struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE];
+	struct sock_fprog *orig_fprog;
+	struct {
+		unsigned int refresh_interval; /* in tenths of second */
+		struct delayed_work refresh_dw;
+		struct lb_stats_info info[LB_TX_HASHTABLE_SIZE];
+	} stats;
+};
+
 struct lb_priv {
 	struct sk_filter __rcu *fp;
-	struct sock_fprog *orig_fprog;
+	lb_select_tx_port_func_t __rcu *select_tx_port_func;
+	struct lb_pcpu_stats __percpu *pcpu_stats;
+	struct lb_priv_ex *ex; /* priv extension */
 };
 
-static struct lb_priv *lb_priv(struct team *team)
+static struct lb_priv *get_lb_priv(struct team *team)
 {
 	return (struct lb_priv *) &team->mode_priv;
 }
 
-static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv,
-				     struct sk_buff *skb)
+struct lb_port_priv {
+	struct lb_stats __percpu *pcpu_stats;
+	struct lb_stats_info stats_info;
+};
+
+static struct lb_port_priv *get_lb_port_priv(struct team_port *port)
+{
+	return (struct lb_port_priv *) &port->mode_priv;
+}
+
+#define LB_HTPM_PORT_BY_HASH(lp_priv, hash) \
+	(lb_priv)->ex->tx_hash_to_port_mapping[hash].port
+
+#define LB_HTPM_OPT_INST_INFO_BY_HASH(lp_priv, hash) \
+	(lb_priv)->ex->tx_hash_to_port_mapping[hash].opt_inst_info
+
+static void lb_tx_hash_to_port_mapping_null_port(struct team *team,
+						 struct team_port *port)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	bool changed = false;
+	int i;
+
+	for (i = 0; i < LB_TX_HASHTABLE_SIZE; i++) {
+		struct lb_port_mapping *pm;
+
+		pm = &lb_priv->ex->tx_hash_to_port_mapping[i];
+		if (pm->port == port) {
+			rcu_assign_pointer(pm->port, NULL);
+			team_option_inst_set_change(pm->opt_inst_info);
+			changed = true;
+		}
+	}
+	if (changed)
+		team_options_change_check(team);
+}
+
+/* Basic tx selection based solely by hash */
+static struct team_port *lb_hash_select_tx_port(struct team *team,
+						struct lb_priv *lb_priv,
+						struct sk_buff *skb,
+						unsigned char hash)
+{
+	int port_index;
+
+	port_index = hash % team->en_port_count;
+	return team_get_port_by_index_rcu(team, port_index);
+}
+
+/* Hash to port mapping select tx port */
+static struct team_port *lb_htpm_select_tx_port(struct team *team,
+						struct lb_priv *lb_priv,
+						struct sk_buff *skb,
+						unsigned char hash)
+{
+	return rcu_dereference(LB_HTPM_PORT_BY_HASH(lb_priv, hash));
+}
+
+struct lb_select_tx_port {
+	char *name;
+	lb_select_tx_port_func_t *func;
+};
+
+static const struct lb_select_tx_port lb_select_tx_port_list[] = {
+	{
+		.name = "hash",
+		.func = lb_hash_select_tx_port,
+	},
+	{
+		.name = "hash_to_port_mapping",
+		.func = lb_htpm_select_tx_port,
+	},
+};
+#define LB_SELECT_TX_PORT_LIST_COUNT ARRAY_SIZE(lb_select_tx_port_list)
+
+static char *lb_select_tx_port_get_name(lb_select_tx_port_func_t *func)
+{
+	int i;
+
+	for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) {
+		const struct lb_select_tx_port *item;
+
+		item = &lb_select_tx_port_list[i];
+		if (item->func == func)
+			return item->name;
+	}
+	return NULL;
+}
+
+static lb_select_tx_port_func_t *lb_select_tx_port_get_func(const char *name)
+{
+	int i;
+
+	for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) {
+		const struct lb_select_tx_port *item;
+
+		item = &lb_select_tx_port_list[i];
+		if (!strcmp(item->name, name))
+			return item->func;
+	}
+	return NULL;
+}
+
+static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv,
+				    struct sk_buff *skb)
 {
 	struct sk_filter *fp;
 	uint32_t lhash;
@@ -42,18 +187,40 @@ static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv,
 	return c[0] ^ c[1] ^ c[2] ^ c[3];
 }
 
+static void lb_update_tx_stats(unsigned int tx_bytes, struct lb_priv *lb_priv,
+			       struct lb_port_priv *lb_port_priv,
+			       unsigned char hash)
+{
+	struct lb_pcpu_stats *pcpu_stats;
+	struct lb_stats *port_stats;
+	struct lb_stats *hash_stats;
+
+	pcpu_stats = this_cpu_ptr(lb_priv->pcpu_stats);
+	port_stats = this_cpu_ptr(lb_port_priv->pcpu_stats);
+	hash_stats = &pcpu_stats->hash_stats[hash];
+	u64_stats_update_begin(&pcpu_stats->syncp);
+	port_stats->tx_bytes += tx_bytes;
+	hash_stats->tx_bytes += tx_bytes;
+	u64_stats_update_end(&pcpu_stats->syncp);
+}
+
 static bool lb_transmit(struct team *team, struct sk_buff *skb)
 {
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	lb_select_tx_port_func_t *select_tx_port_func;
 	struct team_port *port;
-	int port_index;
+	unsigned char hash;
+	unsigned int tx_bytes = skb->len;
 
-	port_index = lb_get_skb_hash(lb_priv(team), skb) % team->en_port_count;
-	port = team_get_port_by_index_rcu(team, port_index);
+	hash = lb_get_skb_hash(lb_priv, skb);
+	select_tx_port_func = rcu_dereference(lb_priv->select_tx_port_func);
+	port = select_tx_port_func(team, lb_priv, skb, hash);
 	if (unlikely(!port))
 		goto drop;
 	skb->dev = port->dev;
 	if (dev_queue_xmit(skb))
 		return false;
+	lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash);
 	return true;
 
 drop:
@@ -63,14 +230,16 @@ drop:
 
 static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
 {
-	if (!lb_priv(team)->orig_fprog) {
+	struct lb_priv *lb_priv = get_lb_priv(team);
+
+	if (!lb_priv->ex->orig_fprog) {
 		ctx->data.bin_val.len = 0;
 		ctx->data.bin_val.ptr = NULL;
 		return 0;
 	}
-	ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len *
+	ctx->data.bin_val.len = lb_priv->ex->orig_fprog->len *
 				sizeof(struct sock_filter);
-	ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter;
+	ctx->data.bin_val.ptr = lb_priv->ex->orig_fprog->filter;
 	return 0;
 }
 
@@ -103,6 +272,7 @@ static void __fprog_destroy(struct sock_fprog *fprog)
 
 static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
 {
+	struct lb_priv *lb_priv = get_lb_priv(team);
 	struct sk_filter *fp = NULL;
 	struct sock_fprog *fprog = NULL;
 	int err;
@@ -119,14 +289,232 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
 		}
 	}
 
-	if (lb_priv(team)->orig_fprog) {
+	if (lb_priv->ex->orig_fprog) {
 		/* Clear old filter data */
-		__fprog_destroy(lb_priv(team)->orig_fprog);
-		sk_unattached_filter_destroy(lb_priv(team)->fp);
+		__fprog_destroy(lb_priv->ex->orig_fprog);
+		sk_unattached_filter_destroy(lb_priv->fp);
 	}
 
-	rcu_assign_pointer(lb_priv(team)->fp, fp);
-	lb_priv(team)->orig_fprog = fprog;
+	rcu_assign_pointer(lb_priv->fp, fp);
+	lb_priv->ex->orig_fprog = fprog;
+	return 0;
+}
+
+static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	char *name;
+
+	name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func);
+	BUG_ON(!name);
+	ctx->data.str_val = name;
+	return 0;
+}
+
+static int lb_tx_method_set(struct team *team, struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	lb_select_tx_port_func_t *func;
+
+	func = lb_select_tx_port_get_func(ctx->data.str_val);
+	if (!func)
+		return -EINVAL;
+	rcu_assign_pointer(lb_priv->select_tx_port_func, func);
+	return 0;
+}
+
+static int lb_tx_hash_to_port_mapping_init(struct team *team,
+					   struct team_option_inst_info *info)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	unsigned char hash = info->array_index;
+
+	LB_HTPM_OPT_INST_INFO_BY_HASH(lb_priv, hash) = info;
+	return 0;
+}
+
+static int lb_tx_hash_to_port_mapping_get(struct team *team,
+					  struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	struct team_port *port;
+	unsigned char hash = ctx->info->array_index;
+
+	port = LB_HTPM_PORT_BY_HASH(lb_priv, hash);
+	ctx->data.u32_val = port ? port->dev->ifindex : 0;
+	return 0;
+}
+
+static int lb_tx_hash_to_port_mapping_set(struct team *team,
+					  struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	struct team_port *port;
+	unsigned char hash = ctx->info->array_index;
+
+	list_for_each_entry(port, &team->port_list, list) {
+		if (ctx->data.u32_val == port->dev->ifindex) {
+			rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash),
+					   port);
+			return 0;
+		}
+	}
+	return -ENODEV;
+}
+
+static int lb_hash_stats_init(struct team *team,
+			      struct team_option_inst_info *info)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	unsigned char hash = info->array_index;
+
+	lb_priv->ex->stats.info[hash].opt_inst_info = info;
+	return 0;
+}
+
+static int lb_hash_stats_get(struct team *team, struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	unsigned char hash = ctx->info->array_index;
+
+	ctx->data.bin_val.ptr = &lb_priv->ex->stats.info[hash].stats;
+	ctx->data.bin_val.len = sizeof(struct lb_stats);
+	return 0;
+}
+
+static int lb_port_stats_init(struct team *team,
+			      struct team_option_inst_info *info)
+{
+	struct team_port *port = info->port;
+	struct lb_port_priv *lb_port_priv = get_lb_port_priv(port);
+
+	lb_port_priv->stats_info.opt_inst_info = info;
+	return 0;
+}
+
+static int lb_port_stats_get(struct team *team, struct team_gsetter_ctx *ctx)
+{
+	struct team_port *port = ctx->info->port;
+	struct lb_port_priv *lb_port_priv = get_lb_port_priv(port);
+
+	ctx->data.bin_val.ptr = &lb_port_priv->stats_info.stats;
+	ctx->data.bin_val.len = sizeof(struct lb_stats);
+	return 0;
+}
+
+static void __lb_stats_info_refresh_prepare(struct lb_stats_info *s_info)
+{
+	memcpy(&s_info->last_stats, &s_info->stats, sizeof(struct lb_stats));
+	memset(&s_info->stats, 0, sizeof(struct lb_stats));
+}
+
+static bool __lb_stats_info_refresh_check(struct lb_stats_info *s_info,
+					  struct team *team)
+{
+	if (memcmp(&s_info->last_stats, &s_info->stats,
+	    sizeof(struct lb_stats))) {
+		team_option_inst_set_change(s_info->opt_inst_info);
+		return true;
+	}
+	return false;
+}
+
+static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats,
+				   struct lb_stats *cpu_stats,
+				   struct u64_stats_sync *syncp)
+{
+	unsigned int start;
+	struct lb_stats tmp;
+
+	do {
+		start = u64_stats_fetch_begin_bh(syncp);
+		tmp.tx_bytes = cpu_stats->tx_bytes;
+	} while (u64_stats_fetch_retry_bh(syncp, start));
+	acc_stats->tx_bytes += tmp.tx_bytes;
+}
+
+static void lb_stats_refresh(struct work_struct *work)
+{
+	struct team *team;
+	struct lb_priv *lb_priv;
+	struct lb_priv_ex *lb_priv_ex;
+	struct lb_pcpu_stats *pcpu_stats;
+	struct lb_stats *stats;
+	struct lb_stats_info *s_info;
+	struct team_port *port;
+	bool changed = false;
+	int i;
+	int j;
+
+	lb_priv_ex = container_of(work, struct lb_priv_ex,
+				  stats.refresh_dw.work);
+
+	team = lb_priv_ex->team;
+	lb_priv = get_lb_priv(team);
+
+	if (!mutex_trylock(&team->lock)) {
+		schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, 0);
+		return;
+	}
+
+	for (j = 0; j < LB_TX_HASHTABLE_SIZE; j++) {
+		s_info = &lb_priv->ex->stats.info[j];
+		__lb_stats_info_refresh_prepare(s_info);
+		for_each_possible_cpu(i) {
+			pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i);
+			stats = &pcpu_stats->hash_stats[j];
+			__lb_one_cpu_stats_add(&s_info->stats, stats,
+					       &pcpu_stats->syncp);
+		}
+		changed |= __lb_stats_info_refresh_check(s_info, team);
+	}
+
+	list_for_each_entry(port, &team->port_list, list) {
+		struct lb_port_priv *lb_port_priv = get_lb_port_priv(port);
+
+		s_info = &lb_port_priv->stats_info;
+		__lb_stats_info_refresh_prepare(s_info);
+		for_each_possible_cpu(i) {
+			pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i);
+			stats = per_cpu_ptr(lb_port_priv->pcpu_stats, i);
+			__lb_one_cpu_stats_add(&s_info->stats, stats,
+					       &pcpu_stats->syncp);
+		}
+		changed |= __lb_stats_info_refresh_check(s_info, team);
+	}
+
+	if (changed)
+		team_options_change_check(team);
+
+	schedule_delayed_work(&lb_priv_ex->stats.refresh_dw,
+			      (lb_priv_ex->stats.refresh_interval * HZ) / 10);
+
+	mutex_unlock(&team->lock);
+}
+
+static int lb_stats_refresh_interval_get(struct team *team,
+					 struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+
+	ctx->data.u32_val = lb_priv->ex->stats.refresh_interval;
+	return 0;
+}
+
+static int lb_stats_refresh_interval_set(struct team *team,
+					 struct team_gsetter_ctx *ctx)
+{
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	unsigned int interval;
+
+	interval = ctx->data.u32_val;
+	if (lb_priv->ex->stats.refresh_interval == interval)
+		return 0;
+	lb_priv->ex->stats.refresh_interval = interval;
+	if (interval)
+		schedule_delayed_work(&lb_priv->ex->stats.refresh_dw, 0);
+	else
+		cancel_delayed_work(&lb_priv->ex->stats.refresh_dw);
 	return 0;
 }
 
@@ -137,23 +525,117 @@ static const struct team_option lb_options[] = {
 		.getter = lb_bpf_func_get,
 		.setter = lb_bpf_func_set,
 	},
+	{
+		.name = "lb_tx_method",
+		.type = TEAM_OPTION_TYPE_STRING,
+		.getter = lb_tx_method_get,
+		.setter = lb_tx_method_set,
+	},
+	{
+		.name = "lb_tx_hash_to_port_mapping",
+		.array_size = LB_TX_HASHTABLE_SIZE,
+		.type = TEAM_OPTION_TYPE_U32,
+		.init = lb_tx_hash_to_port_mapping_init,
+		.getter = lb_tx_hash_to_port_mapping_get,
+		.setter = lb_tx_hash_to_port_mapping_set,
+	},
+	{
+		.name = "lb_hash_stats",
+		.array_size = LB_TX_HASHTABLE_SIZE,
+		.type = TEAM_OPTION_TYPE_BINARY,
+		.init = lb_hash_stats_init,
+		.getter = lb_hash_stats_get,
+	},
+	{
+		.name = "lb_port_stats",
+		.per_port = true,
+		.type = TEAM_OPTION_TYPE_BINARY,
+		.init = lb_port_stats_init,
+		.getter = lb_port_stats_get,
+	},
+	{
+		.name = "lb_stats_refresh_interval",
+		.type = TEAM_OPTION_TYPE_U32,
+		.getter = lb_stats_refresh_interval_get,
+		.setter = lb_stats_refresh_interval_set,
+	},
 };
 
 static int lb_init(struct team *team)
 {
-	return team_options_register(team, lb_options,
-				     ARRAY_SIZE(lb_options));
+	struct lb_priv *lb_priv = get_lb_priv(team);
+	lb_select_tx_port_func_t *func;
+	int err;
+
+	/* set default tx port selector */
+	func = lb_select_tx_port_get_func("hash");
+	BUG_ON(!func);
+	rcu_assign_pointer(lb_priv->select_tx_port_func, func);
+
+	lb_priv->ex = kzalloc(sizeof(*lb_priv->ex), GFP_KERNEL);
+	if (!lb_priv->ex)
+		return -ENOMEM;
+	lb_priv->ex->team = team;
+
+	lb_priv->pcpu_stats = alloc_percpu(struct lb_pcpu_stats);
+	if (!lb_priv->pcpu_stats) {
+		err = -ENOMEM;
+		goto err_alloc_pcpu_stats;
+	}
+
+	INIT_DELAYED_WORK(&lb_priv->ex->stats.refresh_dw, lb_stats_refresh);
+
+	err = team_options_register(team, lb_options, ARRAY_SIZE(lb_options));
+	if (err)
+		goto err_options_register;
+	return 0;
+
+err_options_register:
+	free_percpu(lb_priv->pcpu_stats);
+err_alloc_pcpu_stats:
+	kfree(lb_priv->ex);
+	return err;
 }
 
 static void lb_exit(struct team *team)
 {
+	struct lb_priv *lb_priv = get_lb_priv(team);
+
 	team_options_unregister(team, lb_options,
 				ARRAY_SIZE(lb_options));
+	cancel_delayed_work_sync(&lb_priv->ex->stats.refresh_dw);
+	free_percpu(lb_priv->pcpu_stats);
+	kfree(lb_priv->ex);
+}
+
+static int lb_port_enter(struct team *team, struct team_port *port)
+{
+	struct lb_port_priv *lb_port_priv = get_lb_port_priv(port);
+
+	lb_port_priv->pcpu_stats = alloc_percpu(struct lb_stats);
+	if (!lb_port_priv->pcpu_stats)
+		return -ENOMEM;
+	return 0;
+}
+
+static void lb_port_leave(struct team *team, struct team_port *port)
+{
+	struct lb_port_priv *lb_port_priv = get_lb_port_priv(port);
+
+	free_percpu(lb_port_priv->pcpu_stats);
+}
+
+static void lb_port_disabled(struct team *team, struct team_port *port)
+{
+	lb_tx_hash_to_port_mapping_null_port(team, port);
 }
 
 static const struct team_mode_ops lb_mode_ops = {
 	.init			= lb_init,
 	.exit			= lb_exit,
+	.port_enter		= lb_port_enter,
+	.port_leave		= lb_port_leave,
+	.port_disabled		= lb_port_disabled,
 	.transmit		= lb_transmit,
 };
 
@@ -161,6 +643,7 @@ static const struct team_mode lb_mode = {
 	.kind		= "loadbalance",
 	.owner		= THIS_MODULE,
 	.priv_size	= sizeof(struct lb_priv),
+	.port_priv_size	= sizeof(struct lb_port_priv),
 	.ops		= &lb_mode_ops,
 };
 
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 16/19] team: implement multipart netlink messages for options transfers
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (14 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 15/19] team: lb: introduce infrastructure for userspace driven tx loadbalancing Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 17/19] team: ensure correct order of netlink messages delivery Jiri Pirko
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |  198 +++++++++++++++++++++++++++--------------------
 1 file changed, 116 insertions(+), 82 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index bc76f94..9e9d3e5 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1571,102 +1571,128 @@ err_fill:
 	return err;
 }
 
+typedef int team_nl_send_func_t(struct sk_buff *skb,
+				struct team *team, u32 pid);
+
+static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 pid)
+{
+	return genlmsg_unicast(dev_net(team->dev), skb, pid);
+}
+
 static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
 				       struct team_option_inst *opt_inst)
 {
 	struct nlattr *option_item;
 	struct team_option *option = opt_inst->option;
-	struct team_option_inst_info *opt_inst_info;
+	struct team_option_inst_info *opt_inst_info = &opt_inst->info;
 	struct team_gsetter_ctx ctx;
 	int err;
 
+	ctx.info = opt_inst_info;
+	err = team_option_get(team, opt_inst, &ctx);
+	if (err)
+		return err;
+
 	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 (opt_inst->changed) {
-		if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
-			goto nla_put_failure;
-		opt_inst->changed = false;
-	}
-	if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
-		goto nla_put_failure;
+		return -EMSGSIZE;
 
-	opt_inst_info = &opt_inst->info;
+	if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
+		goto nest_cancel;
 	if (opt_inst_info->port &&
 	    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
 			opt_inst_info->port->dev->ifindex))
-		goto nla_put_failure;
+		goto nest_cancel;
 	if (opt_inst->option->array_size &&
 	    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
 			opt_inst_info->array_index))
-		goto nla_put_failure;
-	ctx.info = opt_inst_info;
+		goto nest_cancel;
 
 	switch (option->type) {
 	case TEAM_OPTION_TYPE_U32:
 		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
-			goto nla_put_failure;
-		err = team_option_get(team, opt_inst, &ctx);
-		if (err)
-			goto errout;
+			goto nest_cancel;
 		if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
-			goto nla_put_failure;
+			goto nest_cancel;
 		break;
 	case TEAM_OPTION_TYPE_STRING:
 		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
-			goto nla_put_failure;
-		err = team_option_get(team, opt_inst, &ctx);
-		if (err)
-			goto errout;
+			goto nest_cancel;
 		if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
 				   ctx.data.str_val))
-			goto nla_put_failure;
+			goto nest_cancel;
 		break;
 	case TEAM_OPTION_TYPE_BINARY:
 		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
-			goto nla_put_failure;
-		err = team_option_get(team, opt_inst, &ctx);
-		if (err)
-			goto errout;
+			goto nest_cancel;
 		if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
 			    ctx.data.bin_val.ptr))
-			goto nla_put_failure;
+			goto nest_cancel;
 		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;
+			goto nest_cancel;
 		if (ctx.data.bool_val &&
 		    nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
-			goto nla_put_failure;
+			goto nest_cancel;
 		break;
 	default:
 		BUG();
 	}
+	if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
+		goto nest_cancel;
+	if (opt_inst->changed) {
+		if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
+			goto nest_cancel;
+		opt_inst->changed = false;
+	}
 	nla_nest_end(skb, option_item);
 	return 0;
 
-nla_put_failure:
-	err = -EMSGSIZE;
-errout:
-	return err;
+nest_cancel:
+	nla_nest_cancel(skb, option_item);
+	return -EMSGSIZE;
+}
+
+static int __send_and_alloc_skb(struct sk_buff **pskb,
+				struct team *team, u32 pid,
+				team_nl_send_func_t *send_func)
+{
+	int err;
+
+	if (*pskb) {
+		err = send_func(*pskb, team, pid);
+		if (err)
+			return err;
+	}
+	*pskb = genlmsg_new(NLMSG_DEFAULT_SIZE - GENL_HDRLEN, GFP_KERNEL);
+	if (!*pskb)
+		return -ENOMEM;
+	return 0;
 }
 
-static int team_nl_fill_options_get(struct sk_buff *skb,
-				    u32 pid, u32 seq, int flags,
-				    struct team *team,
+static int team_nl_send_options_get(struct team *team, u32 pid, u32 seq,
+				    int flags, team_nl_send_func_t *send_func,
 				    struct list_head *sel_opt_inst_list)
 {
 	struct nlattr *option_list;
+	struct nlmsghdr *nlh;
 	void *hdr;
 	struct team_option_inst *opt_inst;
 	int err;
+	struct sk_buff *skb = NULL;
+	bool incomplete;
+	int i;
 
-	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+	opt_inst = list_first_entry(sel_opt_inst_list,
+				    struct team_option_inst, tmp_list);
+
+start_again:
+	err = __send_and_alloc_skb(&skb, team, pid, send_func);
+	if (err)
+		return err;
+
+	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI,
 			  TEAM_CMD_OPTIONS_GET);
 	if (IS_ERR(hdr))
 		return PTR_ERR(hdr);
@@ -1677,46 +1703,62 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
 	if (!option_list)
 		goto nla_put_failure;
 
-	list_for_each_entry(opt_inst, sel_opt_inst_list, tmp_list) {
+	i = 0;
+	incomplete = false;
+	list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
 		err = team_nl_fill_one_option_get(skb, team, opt_inst);
-		if (err)
+		if (err) {
+			if (err == -EMSGSIZE) {
+				if (!i)
+					goto errout;
+				incomplete = true;
+				break;
+			}
 			goto errout;
+		}
+		i++;
 	}
 
 	nla_nest_end(skb, option_list);
-	return genlmsg_end(skb, hdr);
+	genlmsg_end(skb, hdr);
+	if (incomplete)
+		goto start_again;
+
+send_done:
+	nlh = nlmsg_put(skb, pid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
+	if (!nlh) {
+		err = __send_and_alloc_skb(&skb, team, pid, send_func);
+		if (err)
+			goto errout;
+		goto send_done;
+	}
+
+	return send_func(skb, team, pid);
 
 nla_put_failure:
 	err = -EMSGSIZE;
 errout:
 	genlmsg_cancel(skb, hdr);
+	nlmsg_free(skb);
 	return err;
 }
 
-static int team_nl_fill_options_get_all(struct sk_buff *skb,
-					struct genl_info *info, int flags,
-					struct team *team)
-{
-	struct team_option_inst *opt_inst;
-	LIST_HEAD(sel_opt_inst_list);
-
-	list_for_each_entry(opt_inst, &team->option_inst_list, list)
-		list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
-	return team_nl_fill_options_get(skb, info->snd_pid,
-					info->snd_seq, NLM_F_ACK,
-					team, &sel_opt_inst_list);
-}
-
 static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
 {
 	struct team *team;
+	struct team_option_inst *opt_inst;
 	int err;
+	LIST_HEAD(sel_opt_inst_list);
 
 	team = team_nl_team_get(info);
 	if (!team)
 		return -EINVAL;
 
-	err = team_nl_send_generic(info, team, team_nl_fill_options_get_all);
+	list_for_each_entry(opt_inst, &team->option_inst_list, list)
+		list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
+	err = team_nl_send_options_get(team, info->snd_pid, info->snd_seq,
+				       NLM_F_ACK, team_nl_send_unicast,
+				       &sel_opt_inst_list);
 
 	team_nl_team_put(team);
 
@@ -1963,28 +2005,18 @@ static struct genl_multicast_group team_change_event_mcgrp = {
 	.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
 };
 
+static int team_nl_send_multicast(struct sk_buff *skb,
+				  struct team *team, u32 pid)
+{
+	return genlmsg_multicast_netns(dev_net(team->dev), skb, 0,
+				       team_change_event_mcgrp.id, GFP_KERNEL);
+}
+
 static int team_nl_send_event_options_get(struct team *team,
 					  struct list_head *sel_opt_inst_list)
 {
-	struct sk_buff *skb;
-	int err;
-	struct net *net = dev_net(team->dev);
-
-	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	err = team_nl_fill_options_get(skb, 0, 0, 0, team, sel_opt_inst_list);
-	if (err < 0)
-		goto err_fill;
-
-	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
-				      GFP_KERNEL);
-	return err;
-
-err_fill:
-	nlmsg_free(skb);
-	return err;
+	return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
+					sel_opt_inst_list);
 }
 
 static int team_nl_send_event_port_list_get(struct team *team)
@@ -2053,7 +2085,8 @@ static void __team_options_change_check(struct team *team)
 	}
 	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
 	if (err)
-		netdev_warn(team->dev, "Failed to send options change via netlink\n");
+		netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
+			    err);
 }
 
 static void __team_option_inst_change(struct team *team,
@@ -2066,7 +2099,8 @@ static void __team_option_inst_change(struct team *team,
 	list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list);
 	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
 	if (err)
-		netdev_warn(team->dev, "Failed to send option change via netlink\n");
+		netdev_warn(team->dev, "Failed to send option change via netlink (err %d)\n",
+			    err);
 }
 
 /* rtnl lock is held */
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 17/19] team: ensure correct order of netlink messages delivery
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (15 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 16/19] team: implement multipart netlink messages for options transfers Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 18/19] team: allow to send multiple set events in one message Jiri Pirko
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

currently, when port is created and per-port options are present, there
options are sent to userspace with ifindex of port which userspace does
not know about. Port add message goes right after.

This patch corrects message ordering so userspace would not be confused.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   25 +++++--------------------
 1 file changed, 5 insertions(+), 20 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 9e9d3e5..a7b391d 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -347,24 +347,6 @@ void team_options_unregister(struct team *team,
 }
 EXPORT_SYMBOL(team_options_unregister);
 
-static int team_option_port_add(struct team *team, struct team_port *port)
-{
-	int err;
-
-	err = __team_option_inst_add_port(team, port);
-	if (err)
-		return err;
-	__team_options_change_check(team);
-	return 0;
-}
-
-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)
@@ -891,7 +873,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
 		goto err_handler_register;
 	}
 
-	err = team_option_port_add(team, port);
+	err = __team_option_inst_add_port(team, port);
 	if (err) {
 		netdev_err(dev, "Device %s failed to add per-port options\n",
 			   portname);
@@ -904,6 +886,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
 	team_adjust_ops(team);
 	__team_compute_features(team);
 	__team_port_change_check(port, !!netif_carrier_ok(port_dev));
+	__team_options_change_check(team);
 
 	netdev_info(dev, "Port device %s added\n", portname);
 
@@ -947,12 +930,14 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
 		return -ENOENT;
 	}
 
+	__team_option_inst_mark_removed_port(team, port);
+	__team_options_change_check(team);
+	__team_option_inst_del_port(team, port);
 	port->removed = true;
 	__team_port_change_check(port, false);
 	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);
 	netdev_set_master(port_dev, NULL);
 	vlan_vids_del_by_dev(port_dev, dev);
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 18/19] team: allow to send multiple set events in one message
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (16 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 17/19] team: ensure correct order of netlink messages delivery Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 15:54 ` [patch net-next 19/19] team: use rcu_dereference_bh() in tx path Jiri Pirko
  2012-06-19 22:01 ` [patch net-next 00/19] team: couple of patches David Miller
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

When multiple sets are done, event message is generated for each. This
patch accumulates these messages into one.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team.c |   33 +++++++++------------------------
 1 file changed, 9 insertions(+), 24 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index a7b391d..3a4a74b 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -320,8 +320,6 @@ static void __team_options_unregister(struct team *team,
 }
 
 static void __team_options_change_check(struct team *team);
-static void __team_option_inst_change(struct team *team,
-				      struct team_option_inst *opt_inst);
 
 int team_options_register(struct team *team,
 			  const struct team_option *option,
@@ -360,16 +358,9 @@ static int team_option_set(struct team *team,
 			   struct team_option_inst *opt_inst,
 			   struct team_gsetter_ctx *ctx)
 {
-	int err;
-
 	if (!opt_inst->option->setter)
 		return -EOPNOTSUPP;
-	err = opt_inst->option->setter(team, ctx);
-	if (err)
-		return err;
-
-	__team_option_inst_change(team, opt_inst);
-	return err;
+	return opt_inst->option->setter(team, ctx);
 }
 
 void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
@@ -1750,12 +1741,16 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
 	return err;
 }
 
+static int team_nl_send_event_options_get(struct team *team,
+					  struct list_head *sel_opt_inst_list);
+
 static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 {
 	struct team *team;
 	int err = 0;
 	int i;
 	struct nlattr *nl_option;
+	LIST_HEAD(opt_inst_list);
 
 	team = team_nl_team_get(info);
 	if (!team)
@@ -1867,6 +1862,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 			err = team_option_set(team, opt_inst, &ctx);
 			if (err)
 				goto team_put;
+			opt_inst->changed = true;
+			list_add(&opt_inst->tmp_list, &opt_inst_list);
 		}
 		if (!opt_found) {
 			err = -ENOENT;
@@ -1874,6 +1871,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 
+	err = team_nl_send_event_options_get(team, &opt_inst_list);
+
 team_put:
 	team_nl_team_put(team);
 
@@ -2074,20 +2073,6 @@ static void __team_options_change_check(struct team *team)
 			    err);
 }
 
-static void __team_option_inst_change(struct team *team,
-				      struct team_option_inst *sel_opt_inst)
-{
-	int err;
-	LIST_HEAD(sel_opt_inst_list);
-
-	sel_opt_inst->changed = true;
-	list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list);
-	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
-	if (err)
-		netdev_warn(team->dev, "Failed to send option change via netlink (err %d)\n",
-			    err);
-}
-
 /* rtnl lock is held */
 static void __team_port_change_check(struct team_port *port, bool linkup)
 {
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [patch net-next 19/19] team: use rcu_dereference_bh() in tx path
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (17 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 18/19] team: allow to send multiple set events in one message Jiri Pirko
@ 2012-06-19 15:54 ` Jiri Pirko
  2012-06-19 22:01 ` [patch net-next 00/19] team: couple of patches David Miller
  19 siblings, 0 replies; 22+ messages in thread
From: Jiri Pirko @ 2012-06-19 15:54 UTC (permalink / raw)
  To: netdev; +Cc: davem, eric.dumazet, jbrouer

Should be used instead of rcu_dereference, since rcu_read_lock_bh is
held.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/team/team_mode_activebackup.c |    2 +-
 drivers/net/team/team_mode_loadbalance.c  |    6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
index bcc7d6d..2fe02a8 100644
--- a/drivers/net/team/team_mode_activebackup.c
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -40,7 +40,7 @@ static bool ab_transmit(struct team *team, struct sk_buff *skb)
 {
 	struct team_port *active_port;
 
-	active_port = rcu_dereference(ab_priv(team)->active_port);
+	active_port = rcu_dereference_bh(ab_priv(team)->active_port);
 	if (unlikely(!active_port))
 		goto drop;
 	skb->dev = active_port->dev;
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index 33e30ed..45cc095 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -124,7 +124,7 @@ static struct team_port *lb_htpm_select_tx_port(struct team *team,
 						struct sk_buff *skb,
 						unsigned char hash)
 {
-	return rcu_dereference(LB_HTPM_PORT_BY_HASH(lb_priv, hash));
+	return rcu_dereference_bh(LB_HTPM_PORT_BY_HASH(lb_priv, hash));
 }
 
 struct lb_select_tx_port {
@@ -179,7 +179,7 @@ static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv,
 	uint32_t lhash;
 	unsigned char *c;
 
-	fp = rcu_dereference(lb_priv->fp);
+	fp = rcu_dereference_bh(lb_priv->fp);
 	if (unlikely(!fp))
 		return 0;
 	lhash = SK_RUN_FILTER(fp, skb);
@@ -213,7 +213,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb)
 	unsigned int tx_bytes = skb->len;
 
 	hash = lb_get_skb_hash(lb_priv, skb);
-	select_tx_port_func = rcu_dereference(lb_priv->select_tx_port_func);
+	select_tx_port_func = rcu_dereference_bh(lb_priv->select_tx_port_func);
 	port = select_tx_port_func(team, lb_priv, skb, hash);
 	if (unlikely(!port))
 		goto drop;
-- 
1.7.10.2

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [patch net-next 00/19] team: couple of patches
  2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
                   ` (18 preceding siblings ...)
  2012-06-19 15:54 ` [patch net-next 19/19] team: use rcu_dereference_bh() in tx path Jiri Pirko
@ 2012-06-19 22:01 ` David Miller
  2012-06-20  6:40   ` Jesper Dangaard Brouer
  19 siblings, 1 reply; 22+ messages in thread
From: David Miller @ 2012-06-19 22:01 UTC (permalink / raw)
  To: jpirko; +Cc: netdev, eric.dumazet, jbrouer

From: Jiri Pirko <jpirko@redhat.com>
Date: Tue, 19 Jun 2012 17:54:02 +0200

> The main impact is this patchset provides an implementation of userspace driven
> TX loadbalancing for team driver.
> 
> Also couple of typos are fixed, minor issues fixes.

Looks good, pushed, thanks Jiri.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [patch net-next 00/19] team: couple of patches
  2012-06-19 22:01 ` [patch net-next 00/19] team: couple of patches David Miller
@ 2012-06-20  6:40   ` Jesper Dangaard Brouer
  0 siblings, 0 replies; 22+ messages in thread
From: Jesper Dangaard Brouer @ 2012-06-20  6:40 UTC (permalink / raw)
  To: David Miller; +Cc: jpirko, netdev, eric.dumazet

On Tue, 2012-06-19 at 15:01 -0700, David Miller wrote:
> From: Jiri Pirko <jpirko@redhat.com>
> Date: Tue, 19 Jun 2012 17:54:02 +0200
> 
> > The main impact is this patchset provides an implementation of userspace driven
> > TX loadbalancing for team driver.
> > 
> > Also couple of typos are fixed, minor issues fixes.
> 
> Looks good, pushed, thanks Jiri.

Just for the record, I have also reviewed the patch series (before
submission), and ACK the series.

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Sr. Network Kernel Developer at Red Hat
  Author of http://www.iptv-analyzer.org
  LinkedIn: http://www.linkedin.com/in/brouer

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2012-06-20  6:43 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-19 15:54 [patch net-next 00/19] team: couple of patches Jiri Pirko
2012-06-19 15:54 ` [patch net-next 01/19] team: make team_mode struct const Jiri Pirko
2012-06-19 15:54 ` [patch net-next 02/19] team: for nomode use dummy struct team_mode Jiri Pirko
2012-06-19 15:54 ` [patch net-next 03/19] team: add mode priv to port Jiri Pirko
2012-06-19 15:54 ` [patch net-next 04/19] team: lb: push hash counting into separate function Jiri Pirko
2012-06-19 15:54 ` [patch net-next 05/19] team: allow read/write-only options Jiri Pirko
2012-06-19 15:54 ` [patch net-next 06/19] team: introduce array options Jiri Pirko
2012-06-19 15:54 ` [patch net-next 07/19] team: comments: s/net\/drivers\/team/drivers\/net\/team/ Jiri Pirko
2012-06-19 15:54 ` [patch net-next 08/19] team: push array_index and port into separate structure Jiri Pirko
2012-06-19 15:54 ` [patch net-next 09/19] team: allow async option changes Jiri Pirko
2012-06-19 15:54 ` [patch net-next 10/19] team: fix error path in team_nl_fill_options_get() Jiri Pirko
2012-06-19 15:54 ` [patch net-next 11/19] team: fix error path in team_nl_fill_port_list_get() Jiri Pirko
2012-06-19 15:54 ` [patch net-next 12/19] team: allow to specify one option instance to be send to userspace Jiri Pirko
2012-06-19 15:54 ` [patch net-next 13/19] team: pass NULL to __team_option_inst_add() instead of 0 Jiri Pirko
2012-06-19 15:54 ` [patch net-next 14/19] team: add port_[enabled/disabled] mode callbacks Jiri Pirko
2012-06-19 15:54 ` [patch net-next 15/19] team: lb: introduce infrastructure for userspace driven tx loadbalancing Jiri Pirko
2012-06-19 15:54 ` [patch net-next 16/19] team: implement multipart netlink messages for options transfers Jiri Pirko
2012-06-19 15:54 ` [patch net-next 17/19] team: ensure correct order of netlink messages delivery Jiri Pirko
2012-06-19 15:54 ` [patch net-next 18/19] team: allow to send multiple set events in one message Jiri Pirko
2012-06-19 15:54 ` [patch net-next 19/19] team: use rcu_dereference_bh() in tx path Jiri Pirko
2012-06-19 22:01 ` [patch net-next 00/19] team: couple of patches David Miller
2012-06-20  6:40   ` Jesper Dangaard Brouer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).