netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] bonding: L2DA mode
@ 2013-11-21 14:55 Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 1/4] bonding: L2DA mode added Anton Nayshtut
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut

L2 Destination Address based (L2DA) mode allows bonding to send packets using
different slaves according to packets L2 Destination Address.

In L2DA mode, the bonding maintains a default slave and DA/slave map.

Upon a packet transmission, the bonding examines DA of the packet and tries to
find a corresponding slave within the map. If found, the slave is used for the
packet transmission. Otherwise, the default slave is used. If the default slave
is unable to transmit at this moment, the bonding tries to fall back to an
arbitrary slave that can transmit.

Both the default slave and the map can be controlled via sysfs or by ioctls.

Anton Nayshtut (4):
  bonding: L2DA mode added
  bonding: L2DA mode intergated
  bonding: L2DA command IOCTL
  bonding: L2DA query IOCTL

 drivers/net/bonding/Makefile       |   2 +-
 drivers/net/bonding/bond_l2da.c    | 425 +++++++++++++++++++++++++++++++++++++
 drivers/net/bonding/bond_l2da.h    |  56 +++++
 drivers/net/bonding/bond_main.c    | 172 ++++++++++++++-
 drivers/net/bonding/bond_options.c |  17 +-
 drivers/net/bonding/bond_sysfs.c   | 223 ++++++++++++++++++-
 drivers/net/bonding/bonding.h      |   7 +
 include/uapi/linux/if_bonding.h    |  32 +++
 include/uapi/linux/sockios.h       |   4 +-
 net/core/dev_ioctl.c               |   4 +
 net/socket.c                       |   4 +
 11 files changed, 937 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/bonding/bond_l2da.c
 create mode 100644 drivers/net/bonding/bond_l2da.h

-- 
1.8.3.1

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

* [PATCH 1/4] bonding: L2DA mode added
  2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
@ 2013-11-21 14:55 ` Anton Nayshtut
  2013-11-21 15:32   ` Eric Dumazet
  2013-11-21 14:55 ` [PATCH 2/4] bonding: L2DA mode intergated Anton Nayshtut
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum, Boris Lapshin

This patches introduces L2DA bonding module with all the data structures and
interfaces. It's not integrated yet.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/Makefile    |   2 +-
 drivers/net/bonding/bond_l2da.c | 425 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/bonding/bond_l2da.h |  56 ++++++
 drivers/net/bonding/bonding.h   |   2 +
 4 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/bonding/bond_l2da.c
 create mode 100644 drivers/net/bonding/bond_l2da.h

diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 5a5d720..3eecd54 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_BONDING) += bonding.o
 
-bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o bond_l2da.o
 
 proc-$(CONFIG_PROC_FS) += bond_procfs.o
 bonding-objs += $(proc-y)
diff --git a/drivers/net/bonding/bond_l2da.c b/drivers/net/bonding/bond_l2da.c
new file mode 100644
index 0000000..01d0d40
--- /dev/null
+++ b/drivers/net/bonding/bond_l2da.c
@@ -0,0 +1,425 @@
+/*  Copyright(c) 2013 Wilocity Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "bonding.h"
+#include "bond_l2da.h"
+#include <linux/etherdevice.h>
+
+struct l2da_bond_matrix_entry {
+	struct hlist_node hnode;
+	unsigned char     h_dest[ETH_ALEN];
+	struct slave     *slave;
+};
+
+
+#define BOND_L2DA_INFO(bond)     ((bond)->l2da_info)
+#define SLAVE_L2DA_INFO(bond)    ((slave)->l2da_info)
+#define SLAVE_BY_L2DA_INFO(info) container_of(info, struct slave, l2da_info)
+
+#define SLAVE_CAN_XMIT(slave)    (IS_UP((slave)->dev) && \
+				  ((slave)->link == BOND_LINK_UP) && \
+				  bond_is_active_slave(slave))
+
+/**
+ * _bond_l2da_slave_name - returns slave name
+ * @slave: slave struct to work on
+ *
+ * Returns @slave network device name, or "null" if it can't be found.
+ */
+static inline const char *_bond_l2da_slave_name(struct slave *slave)
+{
+	if (slave && slave->dev)
+		return netdev_name(slave->dev);
+	return "null";
+}
+
+/**
+ * _bond_l2da_hash_val - hash function for L2DA map hash table
+ * @da: DA to be used as a hash key
+ *
+ * Returns hash value for @da
+ */
+static inline u32 _bond_l2da_hash_val(const unsigned char *da)
+{
+	return da[ETH_ALEN - 2];
+}
+
+/**
+ * _bond_l2da_find_entry_unsafe - searches for DA:iface mapping within the map
+ * @bond_info: L2DA bonding struct to work on
+ * @da: DA to be used as a key
+ *
+ * Returns map entry for @da, or %NULL if it can't be found.
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static struct l2da_bond_matrix_entry *
+_bond_l2da_find_entry_unsafe(struct l2da_bond_info *bond_info,
+			     const unsigned char *da)
+{
+	struct l2da_bond_matrix_entry *entry = NULL;
+	u32 hash_val = 0;
+	BUG_ON(da == NULL);
+
+	hash_val = _bond_l2da_hash_val(da);
+	hash_for_each_possible(bond_info->da_matrix, entry, hnode, hash_val) {
+		if (!compare_ether_addr(entry->h_dest, da))
+			return entry;
+	}
+	return NULL;
+}
+
+/**
+ * _bond_l2da_select_fallback_slave_unsafe - selects a random fallback slave
+ *                                           (if needed)
+ * @bond: bonding struct to work on
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static void
+_bond_l2da_select_fallback_slave_unsafe(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct slave *slave;
+	struct slave *fallback_slave = bond_info->fallback_slave;
+	struct list_head *iter;
+
+	/* No need for fallback slave while default slave is OK */
+	if (bond_info->default_slave &&
+	    SLAVE_CAN_XMIT(bond_info->default_slave)) {
+		fallback_slave = NULL;
+		goto out;
+	}
+
+	/* Current fallback slave is OK */
+	if (bond_info->fallback_slave &&
+	    SLAVE_CAN_XMIT(bond_info->fallback_slave))
+		goto out;
+
+	/* Select new fallback slave */
+	fallback_slave = NULL;
+	bond_for_each_slave(bond, slave, iter) {
+		if (slave != bond_info->default_slave &&
+		    SLAVE_CAN_XMIT(slave)) {
+			fallback_slave = slave;
+			goto out;
+		}
+	}
+
+out:
+	if (fallback_slave != bond_info->fallback_slave) {
+		pr_info("bond_l2da fallback slave set to %s\n",
+				_bond_l2da_slave_name(fallback_slave));
+		bond_info->fallback_slave = fallback_slave;
+	}
+}
+
+/**
+ * _bond_l2da_remove_entries_unsafe - removes all iface mappings from the map
+ * @bond_info: L2DA bonding struct to work on
+ * @slave: slave whose mappings have to be removed
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static void _bond_l2da_remove_entries_unsafe(struct l2da_bond_info *bond_info,
+					     struct slave *slave)
+{
+	struct l2da_bond_matrix_entry *entry = NULL;
+	struct hlist_node *tmp;
+	int bkt;
+	hash_for_each_safe(bond_info->da_matrix, bkt, tmp, entry, hnode) {
+		/* NULL slave means "remove all" */
+		if (!slave || entry->slave == slave) {
+			hash_del(&entry->hnode);
+			kfree(entry);
+		}
+	}
+}
+
+/**
+ * bond_l2da_initialize - initializes a bond's L2DA context
+ * @bond: bonding struct to work on
+ */
+int bond_l2da_initialize(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	memset(bond_info, 0, sizeof(*bond_info));
+	hash_init(bond_info->da_matrix);
+	rwlock_init(&bond_info->lock);
+	bond_info->default_slave = NULL;
+	pr_info("bond_l2da initialized\n");
+	return 0;
+}
+
+/**
+ * bond_l2da_deinitialize - deinitializes a bond's L2DA context
+ * @bond: bonding struct to work on
+ */
+void bond_l2da_deinitialize(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	/* Kick off any bonds registered  */
+	write_lock_bh(&bond_info->lock);
+	_bond_l2da_remove_entries_unsafe(bond_info, NULL);
+	write_unlock_bh(&bond_info->lock);
+	BUG_ON(!hash_empty(bond_info->da_matrix));
+	memset(bond_info, 0, sizeof(*bond_info)); /* for debugging purposes */
+	pr_info("bond_l2da de-initialized\n");
+}
+
+/**
+ * bond_l2da_slave_init - initializes a slave
+ * @bond: bonding struct to work on
+ * @slave: slave struct to work on
+ *
+ * Assigns default and fallback slaves (if needed).
+ */
+int bond_l2da_slave_init(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	if (!bond_info->default_slave) {
+		bond_info->default_slave = slave;
+		pr_info("bond_l2da default slave initially set to %s\n",
+			_bond_l2da_slave_name(slave));
+	}
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+	return 0;
+}
+
+/**
+ * bond_l2da_slave_deinit - deinitializes a slave
+ * @slave: slave struct to work on
+ *
+ * Removes all matrix entries for this slave, re-assigns default and fallback
+ * slaves (if needed).
+ */
+void bond_l2da_slave_deinit(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	if (slave == bond_info->default_slave) {
+		/* default slave has gone, so let's use some other slave as
+		 * a new default
+		 */
+		bond_info->default_slave = bond_first_slave(bond);
+		pr_info("bond_l2da default slave set to %s\n",
+			_bond_l2da_slave_name(bond_info->default_slave));
+	}
+	_bond_l2da_remove_entries_unsafe(bond_info, slave);
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_xmit - transmits skb in L2DA mode
+ * @skb: skb to transmit
+ * @dev: bonding net device
+ */
+int bond_l2da_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bonding *bond = netdev_priv(dev);
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct ethhdr *eth_data;
+	struct l2da_bond_matrix_entry *entry;
+	int res = 1;
+
+	skb_reset_mac_header(skb);
+	eth_data = eth_hdr(skb);
+
+	read_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, eth_data->h_dest);
+	if (entry && entry->slave && SLAVE_CAN_XMIT(entry->slave)) {
+		/* if a slave configured  for this DA and it's OK - use it */
+		res = bond_dev_queue_xmit(bond, skb, entry->slave->dev);
+	} else if (bond_info->default_slave &&
+		 SLAVE_CAN_XMIT(bond_info->default_slave)) {
+		/* otherwise, if default slave configured and OK - use it */
+		res = bond_dev_queue_xmit(bond, skb,
+					  bond_info->default_slave->dev);
+	} else if (bond_info->fallback_slave) {
+		/* otherwise, if fallback slave selected - use it */
+		res = bond_dev_queue_xmit(bond, skb,
+					  bond_info->fallback_slave->dev);
+	}
+	read_unlock_bh(&bond_info->lock);
+
+	if (res) {
+		/* no suitable interface, frame not sent */
+		kfree_skb(skb);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * bond_l2da_set_default_slave - sends default slave
+ * @bond: bonding struct to work on
+ * @slave: slave struct whose link status changed
+ */
+void bond_l2da_set_default_slave(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	bond_info->default_slave = slave;
+	pr_info("bond_l2da default slave set to %s\n",
+			_bond_l2da_slave_name(slave));
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_get_default_slave_name - gets name of currently configured default
+ *                                    slave
+ * @bond: bonding struct to work on
+ * @buf: destination buffer
+ * @size: destination buffer size
+ */
+int bond_l2da_get_default_slave_name(struct bonding *bond, char *buf, int size)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+
+	if (!buf || size < IFNAMSIZ)
+		return -EINVAL;
+
+	*buf = 0;
+
+	read_lock_bh(&bond_info->lock);
+	if (bond_info->default_slave) {
+		strncpy(buf, netdev_name(bond_info->default_slave->dev),
+			IFNAMSIZ);
+	}
+	read_unlock_bh(&bond_info->lock);
+	return 0;
+}
+
+/**
+ * bond_l2da_set_da_slave - adds DA:slave mapping
+ * @bond: bonding struct to work on
+ * @da: desired L2 destination address to map
+ * @slave: slave to be used for sending packets to desired destination address
+ */
+int bond_l2da_set_da_slave(struct bonding *bond, const unsigned char *da,
+		struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+	struct slave *prev_slave = NULL;
+
+	write_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, da);
+	if (entry) {
+		prev_slave = entry->slave;
+		entry->slave = slave;
+	} else {
+		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+		if (entry) {
+			entry->slave = slave;
+			memcpy(entry->h_dest, da, ETH_ALEN);
+			hash_add(bond_info->da_matrix, &entry->hnode,
+					_bond_l2da_hash_val(da));
+		}
+	}
+	write_unlock_bh(&bond_info->lock);
+
+	if (!entry) {
+		pr_err("bond_l2da: pair node cannot be allocated for [%pM:%s]\n",
+			da, _bond_l2da_slave_name(slave));
+		return -ENOMEM;
+	}
+
+	pr_info("bond_l2da: pair %s [%pM:%s]\n",
+		prev_slave ? "changed" : "added",
+		da, _bond_l2da_slave_name(slave));
+
+	return 0;
+}
+
+/**
+ * bond_l2da_del_da - removes DA mapping
+ * @bond: bonding struct to work on
+ * @da: L2 destination address whose mapping has to be removed
+ */
+int bond_l2da_del_da(struct bonding *bond, const unsigned char *da)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+
+	write_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, da);
+	if (entry)
+		hash_del(&entry->hnode);
+	write_unlock_bh(&bond_info->lock);
+
+	if (!entry) {
+		pr_err("bond_l2da: pair node cannot be found for %pM\n",
+			da);
+		return -ENOENT;
+	}
+
+	pr_info("bond_l2da: pair deleted [%pM:%s]\n",
+			da, _bond_l2da_slave_name(entry->slave));
+	kfree(entry);
+	return 0;
+}
+
+/**
+ * bond_l2da_handle_link_change - handle a slave's link status change indication
+ * @bond: bonding struct to work on
+ * @slave: slave struct whose link status changed
+ *
+ * Handle re-selection of fallback slave (if needed).
+ */
+void bond_l2da_handle_link_change(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+
+	write_lock_bh(&bond_info->lock);
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_call_foreach - iterates over L2DA map
+ * @bond: bonding struct to work on
+ * @clb: callback function to be called for every mapping entry found
+ * @ctx: user context to be passed to callback
+ *
+ * Callback function can return non-zero value to stop iteration.
+ */
+void bond_l2da_call_foreach(struct bonding *bond,
+		int (*clb)(const unsigned char *da, struct slave *slave,
+			   void *ctx),
+		void *ctx)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+	int bkt;
+
+	BUG_ON(!clb);
+
+	read_lock_bh(&bond_info->lock);
+	hash_for_each(bond_info->da_matrix, bkt, entry, hnode) {
+		if (clb(entry->h_dest, entry->slave, ctx))
+			break;
+	}
+	read_unlock_bh(&bond_info->lock);
+}
+
diff --git a/drivers/net/bonding/bond_l2da.h b/drivers/net/bonding/bond_l2da.h
new file mode 100644
index 0000000..4a3b88f
--- /dev/null
+++ b/drivers/net/bonding/bond_l2da.h
@@ -0,0 +1,56 @@
+/* Copyright(c) 2013 Wilocity Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+#ifndef __BOND_L2DA_H__
+#define __BOND_L2DA_H__
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/hashtable.h>
+#include <linux/rwlock.h>
+
+#define BOND_L2A_HASHTABLE_BITS 3
+
+/* L2DA Exported structures to the main bonding code */
+struct l2da_bond_info {
+	DECLARE_HASHTABLE(da_matrix, BOND_L2A_HASHTABLE_BITS); /*DA:iface map*/
+	struct slave    *default_slave; /* Default slave */
+	struct slave    *fallback_slave; /* A random fallback slave to be used
+					  * in case default should be used while
+					  * it cannot transmit
+					  */
+	rwlock_t         lock;
+};
+
+/* l2DA Exported functions to the main bonding code */
+int bond_l2da_initialize(struct bonding *bond);
+void bond_l2da_deinitialize(struct bonding *bond);
+int bond_l2da_slave_init(struct bonding *bond, struct slave *slave);
+void bond_l2da_slave_deinit(struct bonding *bond, struct slave *slave);
+int bond_l2da_xmit(struct sk_buff *skb, struct net_device *dev);
+void bond_l2da_set_default_slave(struct bonding *bond, struct slave *slave);
+int bond_l2da_get_default_slave_name(struct bonding *bond, char *buf, int size);
+int bond_l2da_set_da_slave(struct bonding *bond, const unsigned char *da,
+		struct slave *slave);
+int bond_l2da_del_da(struct bonding *bond, const unsigned char *da);
+void bond_l2da_handle_link_change(struct bonding *bond, struct slave *slave);
+void bond_l2da_call_foreach(struct bonding *bond,
+		int (*clb)(const unsigned char *da,
+			   struct slave *slave, void *ctx),
+		void *ctx);
+
+#endif /* __BOND_L2DA_H__ */
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ca31286..2069584 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -25,6 +25,7 @@
 #include <linux/etherdevice.h>
 #include "bond_3ad.h"
 #include "bond_alb.h"
+#include "bond_l2da.h"
 
 #define DRV_VERSION	"3.7.1"
 #define DRV_RELDATE	"April 27, 2011"
@@ -229,6 +230,7 @@ struct bonding {
 	u32      rr_tx_counter;
 	struct   ad_bond_info ad_info;
 	struct   alb_bond_info alb_info;
+	struct   l2da_bond_info l2da_info;
 	struct   bond_params params;
 	struct   workqueue_struct *wq;
 	struct   delayed_work mii_work;
-- 
1.8.3.1

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

* [PATCH 2/4] bonding: L2DA mode intergated
  2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 1/4] bonding: L2DA mode added Anton Nayshtut
@ 2013-11-21 14:55 ` Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 3/4] bonding: L2DA command IOCTL Anton Nayshtut
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum, Boris Lapshin

L2DA bonding module integration, including module parameters, sysfs entries and
module calls.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c    |  51 ++++++++-
 drivers/net/bonding/bond_options.c |  17 ++-
 drivers/net/bonding/bond_sysfs.c   | 223 ++++++++++++++++++++++++++++++++++++-
 drivers/net/bonding/bonding.h      |   5 +
 include/uapi/linux/if_bonding.h    |   1 +
 5 files changed, 290 insertions(+), 7 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 4dd5ee2..33733be 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -138,7 +138,7 @@ module_param(mode, charp, 0);
 MODULE_PARM_DESC(mode, "Mode of operation; 0 for balance-rr, "
 		       "1 for active-backup, 2 for balance-xor, "
 		       "3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, "
-		       "6 for balance-alb");
+		       "6 for balance-alb, 7 for l2da");
 module_param(primary, charp, 0);
 MODULE_PARM_DESC(primary, "Primary network device to use");
 module_param(primary_reselect, charp, 0);
@@ -218,6 +218,7 @@ const struct bond_parm_tbl bond_mode_tbl[] = {
 {	"802.3ad",		BOND_MODE_8023AD},
 {	"balance-tlb",		BOND_MODE_TLB},
 {	"balance-alb",		BOND_MODE_ALB},
+{	"l2da",			BOND_MODE_L2DA },
 {	NULL,			-1},
 };
 
@@ -282,9 +283,10 @@ const char *bond_mode_name(int mode)
 		[BOND_MODE_8023AD] = "IEEE 802.3ad Dynamic link aggregation",
 		[BOND_MODE_TLB] = "transmit load balancing",
 		[BOND_MODE_ALB] = "adaptive load balancing",
+		[BOND_MODE_L2DA] = "l2 DA matrix",
 	};
 
-	if (mode < BOND_MODE_ROUNDROBIN || mode > BOND_MODE_ALB)
+	if (mode < BOND_MODE_ROUNDROBIN || mode > BOND_MODE_L2DA)
 		return "unknown";
 
 	return names[mode];
@@ -1546,6 +1548,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 		bond_set_active_slave(new_slave);
 		bond_set_slave_inactive_flags(new_slave);
 		break;
+	case BOND_MODE_L2DA:
+		bond_set_slave_active_flags(new_slave);
+		bond_l2da_slave_init(bond, new_slave);
+		break;
 	default:
 		pr_debug("This slave is always active in trunk mode\n");
 
@@ -1760,7 +1766,8 @@ static int __bond_release_one(struct net_device *bond_dev,
 		write_unlock_bh(&bond->lock);
 		bond_alb_deinit_slave(bond, slave);
 		write_lock_bh(&bond->lock);
-	}
+	} else if (bond_is_l2da(bond))
+		bond_l2da_slave_deinit(bond, slave);
 
 	if (all) {
 		rcu_assign_pointer(bond->curr_active_slave, NULL);
@@ -2058,6 +2065,9 @@ static void bond_miimon_commit(struct bonding *bond)
 				bond_alb_handle_link_change(bond, slave,
 							    BOND_LINK_UP);
 
+			if (bond_is_l2da(bond))
+				bond_l2da_handle_link_change(bond, slave);
+
 			if (!bond->curr_active_slave ||
 			    (slave == bond->primary_slave))
 				goto do_failover;
@@ -2085,6 +2095,9 @@ static void bond_miimon_commit(struct bonding *bond)
 				bond_alb_handle_link_change(bond, slave,
 							    BOND_LINK_DOWN);
 
+			if (bond_is_l2da(bond))
+				bond_l2da_handle_link_change(bond, slave);
+
 			if (slave == bond->curr_active_slave)
 				goto do_failover;
 
@@ -3778,6 +3791,8 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
 	case BOND_MODE_ALB:
 	case BOND_MODE_TLB:
 		return bond_alb_xmit(skb, dev);
+	case BOND_MODE_L2DA:
+		return bond_l2da_xmit(skb, dev);
 	default:
 		/* Should never happen, mode already checked */
 		pr_err("%s: Error: Unknown bonding mode %d\n",
@@ -3969,6 +3984,9 @@ static void bond_uninit(struct net_device *bond_dev)
 	list_del(&bond->bond_list);
 
 	bond_debug_unregister(bond);
+
+	if (bond_is_l2da(bond))
+		bond_l2da_deinitialize(bond);
 }
 
 /*------------------------- Module initialization ---------------------------*/
@@ -4041,7 +4059,8 @@ static int bond_check_params(struct bond_params *params)
 	}
 
 	if (lacp_rate) {
-		if (bond_mode != BOND_MODE_8023AD) {
+		if (bond_mode != BOND_MODE_8023AD ||
+			bond_mode != BOND_MODE_L2DA) {
 			pr_info("lacp_rate param is irrelevant in mode %s\n",
 				bond_mode_name(bond_mode));
 		} else {
@@ -4128,6 +4147,19 @@ static int bond_check_params(struct bond_params *params)
 		all_slaves_active = 0;
 	}
 
+	if (bond_mode == BOND_MODE_L2DA) {
+		if (!all_slaves_active) {
+			pr_warning("Warning: all_slaves_active must be specified, otherwise bonding will not be able to route packets that is essential for l2da operation\n");
+			pr_warning("Forcing all_slaves_active to 1\n");
+			all_slaves_active = 1;
+		}
+		if (!miimon) {
+			pr_warning("Warning: miimon must be specified, otherwise bonding will not detect link failure and link speed which are essential for l2da operation\n");
+			pr_warning("Forcing miimon to 100msec\n");
+			miimon = 100;
+		}
+	}
+
 	if (resend_igmp < 0 || resend_igmp > 255) {
 		pr_warning("Warning: resend_igmp (%d) should be between "
 			   "0 and 255, resetting to %d\n",
@@ -4372,6 +4404,7 @@ static int bond_init(struct net_device *bond_dev)
 	struct bonding *bond = netdev_priv(bond_dev);
 	struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id);
 	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+	int ret;
 
 	pr_debug("Begin bond_init for %s\n", bond_dev->name);
 
@@ -4384,6 +4417,16 @@ static int bond_init(struct net_device *bond_dev)
 	spin_lock_init(&(bond_info->tx_hashtbl_lock));
 	spin_lock_init(&(bond_info->rx_hashtbl_lock));
 
+	if (bond_is_l2da(bond)) {
+		ret = bond_l2da_initialize(bond);
+		if (ret) {
+			pr_err("%s: %s mode cannot be initialized.\n",
+					bond->dev->name,
+					bond_mode_tbl[BOND_MODE_L2DA].modename);
+			return ret;
+		}
+	}
+
 	bond->wq = create_singlethread_workqueue(bond_dev->name);
 	if (!bond->wq)
 		return -ENOMEM;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 9a5223c..b413665 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -45,12 +45,27 @@ int bond_option_mode_set(struct bonding *bond, int mode)
 		return -EPERM;
 	}
 
-	if (BOND_MODE_IS_LB(mode) && bond->params.arp_interval) {
+	if ((BOND_MODE_IS_LB(mode) || bond_is_l2da(bond)) &&
+	    bond->params.arp_interval) {
 		pr_err("%s: %s mode is incompatible with arp monitoring.\n",
 		       bond->dev->name, bond_mode_tbl[mode].modename);
 		return -EINVAL;
 	}
 
+	if (bond->params.mode == mode)
+		;
+	else if (mode == BOND_MODE_L2DA) {
+		int ret = bond_l2da_initialize(bond);
+		if (ret) {
+			pr_err("%s: %s mode cannot be initialized.\n",
+			       bond->dev->name,
+			       bond_mode_tbl[mode].modename);
+			return ret;
+		}
+	} else if (bond_is_l2da(bond))
+		bond_l2da_deinitialize(bond);
+
+
 	/* don't cache arp_validate between modes */
 	bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
 	bond->params.mode = mode;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 0ec2a7e..d4727d2 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -525,8 +525,9 @@ static ssize_t bonding_store_arp_interval(struct device *d,
 	}
 	if (bond->params.mode == BOND_MODE_ALB ||
 	    bond->params.mode == BOND_MODE_TLB ||
-	    bond->params.mode == BOND_MODE_8023AD) {
-		pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad. Only MII monitoring is supported on %s.\n",
+	    bond->params.mode == BOND_MODE_8023AD ||
+	    bond->params.mode == BOND_MODE_L2DA) {
+		pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad/L2DA. Only MII monitoring is supported on %s.\n",
 			bond->dev->name, bond->dev->name);
 		ret = -EINVAL;
 		goto out;
@@ -1679,6 +1680,222 @@ static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
 		   bonding_show_packets_per_slave,
 		   bonding_store_packets_per_slave);
 
+static ssize_t bonding_show_l2da_default_slave(struct device *d,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ + 1];
+	int ret;
+
+	read_lock(&bond->lock);
+	ret = bond_l2da_get_default_slave_name(bond, ifname, sizeof(ifname));
+	read_unlock(&bond->lock);
+
+	ifname[IFNAMSIZ] = 0;
+
+	return sprintf(buf, "%s\n", ifname);
+}
+
+static ssize_t bonding_store_l2da_default_slave(struct device *d,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	int ret = -EINVAL;
+	struct slave *slave;
+	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ];
+	struct list_head *iter;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	if (sscanf(buf, "%15s", ifname) != 1) {/* IFNAMSIZ */
+		pr_info("%s: no L2DA slave name specified\n",
+			netdev_name(bond->dev));
+		goto rtnl_out;
+	}
+
+	block_netpoll_tx();
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond)) {
+		pr_info("%s: Unable to set default slave ; %s is in mode %d\n",
+			netdev_name(bond->dev), netdev_name(bond->dev),
+			bond->params.mode);
+		goto out;
+	}
+
+	bond_for_each_slave(bond, slave, iter) {
+		if (strncmp(netdev_name(slave->dev), ifname, IFNAMSIZ) == 0) {
+			if (slave->link == BOND_LINK_UP && IS_UP(slave->dev)) {
+				bond_l2da_set_default_slave(bond, slave);
+				ret = count;
+			} else
+				pr_warn("%s: cannot set %s as default slave\n",
+					netdev_name(bond->dev),
+					netdev_name(slave->dev));
+
+			break;
+		}
+	}
+
+	if (ret < 0)
+		pr_warn("%s: Unable to to set %.*s as default slave.\n",
+			netdev_name(bond->dev), (int)strlen(buf) - 1, buf);
+
+out:
+	read_unlock(&bond->lock);
+	unblock_netpoll_tx();
+rtnl_out:
+	rtnl_unlock();
+
+	return ret;
+}
+
+static DEVICE_ATTR(l2da_default_slave, S_IRUGO | S_IWUSR,
+		   bonding_show_l2da_default_slave,
+		   bonding_store_l2da_default_slave);
+
+struct bonding_show_l2da_table_clb_ctx {
+	char *buf;
+	int   res;
+};
+
+static int bonding_show_l2da_table_clb(const unsigned char *da,
+		struct slave *slave, void *_ctx)
+{
+	struct bonding_show_l2da_table_clb_ctx *ctx = _ctx;
+	if (ctx->res > (PAGE_SIZE - 18 - IFNAMSIZ)) {
+		/* not enough space for another da@interface_name pair */
+		if ((PAGE_SIZE - ctx->res) > 9)
+			ctx->res = PAGE_SIZE - 9;
+		ctx->res += sprintf(ctx->buf + ctx->res, "++more++");
+		return 1;
+	}
+	ctx->res += sprintf(ctx->buf + ctx->res, "%pM@%s\n",
+		       da, netdev_name(slave->dev));
+	return 0;
+}
+
+static ssize_t bonding_show_l2da_table(struct device *d,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct bonding *bond = to_bond(d);
+	struct bonding_show_l2da_table_clb_ctx ctx = {
+		.buf = buf,
+	};
+
+	read_lock(&bond->lock);
+	bond_l2da_call_foreach(bond, bonding_show_l2da_table_clb, &ctx);
+	read_unlock(&bond->lock);
+	if (ctx.res)
+		buf[ctx.res-1] = '\n'; /* eat the leftover space */
+
+	return ctx.res;
+}
+
+static ssize_t bonding_store_l2da_table(struct device *d,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct slave *slave;
+	struct bonding *bond = to_bond(d);
+	int ret = -EINVAL;
+	char *delim;
+	unsigned char da[ETH_ALEN];
+	struct net_device *sdev = NULL;
+	struct list_head *iter;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	/* Check command syntax and extract parameters */
+	if (buf[0] == '+') {
+		unsigned char ifname[IFNAMSIZ] = {0, };
+		/* delim will point to slave interface name if successful */
+		delim = strchr(buf, '@');
+		if (!delim) {
+			pr_err("%s: Invalid L2DA command string: %s\n",
+			    netdev_name(bond->dev), buf);
+			goto out;
+		}
+		/* Terminate string that points to device name and bump it
+		 * up one, so we can read the destination address there.
+		 */
+		*delim = '\0';
+		if (sscanf(delim + 1, "%15s", ifname) != 1) { /* IFNAMSIZ */
+			pr_err("%s: no L2DA slave name specified\n",
+			    netdev_name(bond->dev));
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* Check valid ifname */
+		if (!dev_valid_name(ifname)) {
+			pr_err("%s: Invalid L2DA slave name: '%s'\n",
+			    netdev_name(bond->dev), ifname);
+			ret = -EINVAL;
+			goto out;
+
+		}
+
+		/* Get the pointer to that interface if it exists */
+		sdev = __dev_get_by_name(dev_net(bond->dev), ifname);
+		if (!sdev) {
+			pr_err("%s: No L2DA slave found: %s\n",
+			    netdev_name(bond->dev), ifname);
+			ret = -EINVAL;
+			goto out;
+		}
+	} else if (buf[0] != '-') {
+		pr_err("%s: Invalid L2DA command string: %s\n",
+		    netdev_name(bond->dev), buf);
+		goto out;
+	}
+
+	if (!mac_pton(buf + 1, da)) {
+		pr_err("%s: Invalid L2DA MAC address string: %s\n",
+		    netdev_name(bond->dev), buf + 1);
+		goto out;
+	}
+
+	if (!is_valid_ether_addr(da)) {
+		pr_err("%s: Invalid L2DA MAC address: %pM\n",
+		    netdev_name(bond->dev), da);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	block_netpoll_tx();
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		pr_info("%s: Unable to set default slave ; %s is in mode %d\n",
+			netdev_name(bond->dev), netdev_name(bond->dev),
+			bond->params.mode);
+	else if (buf[0] == '-')
+		ret = bond_l2da_del_da(bond, da);
+	else {
+		bond_for_each_slave(bond, slave, iter) {
+			if (sdev == slave->dev) {
+				ret = bond_l2da_set_da_slave(bond, da, slave);
+				break;
+			}
+		}
+	}
+
+	read_unlock(&bond->lock);
+	unblock_netpoll_tx();
+out:
+	rtnl_unlock();
+	return ret ? ret : count;
+}
+
+static DEVICE_ATTR(l2da_table, S_IRUGO | S_IWUSR,
+		   bonding_show_l2da_table, bonding_store_l2da_table);
+
 static struct attribute *per_bond_attrs[] = {
 	&dev_attr_slaves.attr,
 	&dev_attr_mode.attr,
@@ -1711,6 +1928,8 @@ static struct attribute *per_bond_attrs[] = {
 	&dev_attr_min_links.attr,
 	&dev_attr_lp_interval.attr,
 	&dev_attr_packets_per_slave.attr,
+	&dev_attr_l2da_default_slave.attr,
+	&dev_attr_l2da_table.attr,
 	NULL,
 };
 
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 2069584..6c8c851 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -273,6 +273,11 @@ static inline bool bond_is_lb(const struct bonding *bond)
 	return BOND_MODE_IS_LB(bond->params.mode);
 }
 
+static inline bool bond_is_l2da(const struct bonding *bond)
+{
+	return bond->params.mode == BOND_MODE_L2DA;
+}
+
 static inline void bond_set_active_slave(struct slave *slave)
 {
 	slave->backup = 0;
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index 9635a62..b70ff3e 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -70,6 +70,7 @@
 #define BOND_MODE_8023AD        4
 #define BOND_MODE_TLB           5
 #define BOND_MODE_ALB		6 /* TLB + RLB (receive load balancing) */
+#define BOND_MODE_L2DA          7
 
 /* each slave's link has 4 states */
 #define BOND_LINK_UP    0           /* link is up and running */
-- 
1.8.3.1

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

* [PATCH 3/4] bonding: L2DA command IOCTL
  2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 1/4] bonding: L2DA mode added Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 2/4] bonding: L2DA mode intergated Anton Nayshtut
@ 2013-11-21 14:55 ` Anton Nayshtut
  2013-11-21 14:55 ` [PATCH 4/4] bonding: L2DA query IOCTL Anton Nayshtut
  2013-11-21 15:09 ` [PATCH 0/4] bonding: L2DA mode Andy Gospodarek
  4 siblings, 0 replies; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum, Boris Lapshin

L2DA command IOCTL introduction. The SIOCBONDL2DACMD is used to control L2DA
bonding logic: change default slave, add and delete map entries.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c | 52 +++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_bonding.h | 16 +++++++++++++
 include/uapi/linux/sockios.h    |  1 +
 net/core/dev_ioctl.c            |  2 ++
 net/socket.c                    |  2 ++
 5 files changed, 73 insertions(+)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 33733be..ddaee7b 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1886,6 +1886,55 @@ static int  bond_release_and_destroy(struct net_device *bond_dev,
 	return ret;
 }
 
+static int bond_ioctl_l2da_cmd(struct net_device *bond_dev,
+			       void __user *usr_data)
+{
+	struct bonding *bond = netdev_priv(bond_dev);
+	struct l2da_cmd *u_l2dacmd = (struct l2da_cmd __user *)usr_data;
+	struct l2da_cmd  k_l2dacmd;
+	struct net_device *slave_dev;
+	struct slave *slave;
+	int res = -EFAULT;
+
+	if (copy_from_user(&k_l2dacmd, u_l2dacmd, sizeof(struct l2da_cmd)))
+		return res;
+
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		goto out;
+
+	switch (k_l2dacmd.cmd) {
+	case BOND_L2DA_CMD_SET_DEFAULT:
+		slave_dev = dev_get_by_name(dev_net(bond_dev),
+					    k_l2dacmd.slave_name);
+		if (slave_dev) {
+			slave = bond_get_slave_by_dev(bond, slave_dev);
+			bond_l2da_set_default_slave(bond, slave);
+			dev_put(slave_dev);
+			res = 0;
+		}
+		break;
+	case BOND_L2DA_CMD_MAP_ADD:
+		slave_dev = dev_get_by_name(dev_net(bond_dev),
+					    k_l2dacmd.slave_name);
+		if (slave_dev) {
+			slave = bond_get_slave_by_dev(bond, slave_dev);
+			res = bond_l2da_set_da_slave(bond, k_l2dacmd.da, slave);
+			dev_put(slave_dev);
+		}
+		break;
+	case BOND_L2DA_CMD_MAP_DEL:
+		res = bond_l2da_del_da(bond, k_l2dacmd.da);
+		break;
+	default:
+		break;
+	}
+out:
+	read_unlock(&bond->lock);
+	return res;
+}
+
 static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
@@ -3262,6 +3311,9 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 		return -EPERM;
 
+	if (cmd == SIOCBONDL2DACMD)
+		return bond_ioctl_l2da_cmd(bond_dev, ifr->ifr_data);
+
 	slave_dev = dev_get_by_name(net, ifr->ifr_slave);
 
 	pr_debug("slave_dev=%p:\n", slave_dev);
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index b70ff3e..a4d3baf 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -95,6 +95,11 @@
 #define BOND_XMIT_POLICY_ENCAP23	3 /* encapsulated layer 2+3 */
 #define BOND_XMIT_POLICY_ENCAP34	4 /* encapsulated layer 3+4 */
 
+/* l2da commands */
+#define BOND_L2DA_CMD_SET_DEFAULT    0 /* set default slave */
+#define BOND_L2DA_CMD_MAP_ADD        1 /* add DA:iface mapping */
+#define BOND_L2DA_CMD_MAP_DEL        2 /* add DA mapping */
+
 typedef struct ifbond {
 	__s32 bond_mode;
 	__s32 num_slaves;
@@ -117,6 +122,17 @@ struct ad_info {
 	__u8 partner_system[ETH_ALEN];
 };
 
+struct l2da_cmd {
+	__u16 cmd; /* one of the BOND_L2DA_CMD_... commands */
+	__u8 da[ETH_ALEN]; /* Used as IN parameter in:
+			    *   BOND_L2DA_CMD_MAP_ADD
+			    *   BOND_L2DA_CMD_MAP_DEL*/
+	char slave_name[IFNAMSIZ]; /* Used as IN parameter in:
+				    *   BOND_L2DA_CMD_SET_DEFAULT
+				    *   BOND_L2DA_CMD_MAP_ADD
+				    * */
+};
+
 #endif /* _LINUX_IF_BONDING_H */
 
 /*
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index 7997a50..2af055d 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -117,6 +117,7 @@
 #define SIOCBONDSLAVEINFOQUERY 0x8993   /* rtn info about slave state   */
 #define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
 #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
+#define SIOCBONDL2DACMD        0x8996   /* l2da related command */
 			
 /* bridge calls */
 #define SIOCBRADDBR     0x89a0		/* create new bridge device     */
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 5b7d0e1..3a4716a 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -321,6 +321,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 		    cmd == SIOCBONDSLAVEINFOQUERY ||
 		    cmd == SIOCBONDINFOQUERY ||
 		    cmd == SIOCBONDCHANGEACTIVE ||
+		    cmd == SIOCBONDL2DACMD ||
 		    cmd == SIOCGMIIPHY ||
 		    cmd == SIOCGMIIREG ||
 		    cmd == SIOCSMIIREG ||
@@ -518,6 +519,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 	case SIOCBONDRELEASE:
 	case SIOCBONDSETHWADDR:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 	case SIOCBRADDIF:
 	case SIOCBRDELIF:
 	case SIOCSHWTSTAMP:
diff --git a/net/socket.c b/net/socket.c
index c226ace..2ed653b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2975,6 +2975,7 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
 	case SIOCBONDRELEASE:
 	case SIOCBONDSETHWADDR:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 		if (copy_from_user(&kifr, ifr32, sizeof(struct compat_ifreq)))
 			return -EFAULT;
 
@@ -3262,6 +3263,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 		return bond_ioctl(net, cmd, argp);
 	case SIOCADDRT:
 	case SIOCDELRT:
-- 
1.8.3.1

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

* [PATCH 4/4] bonding: L2DA query IOCTL
  2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
                   ` (2 preceding siblings ...)
  2013-11-21 14:55 ` [PATCH 3/4] bonding: L2DA command IOCTL Anton Nayshtut
@ 2013-11-21 14:55 ` Anton Nayshtut
  2013-11-21 15:09 ` [PATCH 0/4] bonding: L2DA mode Andy Gospodarek
  4 siblings, 0 replies; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum, Boris Lapshin

L2DA query IOCTL introduction. The SIOCBONDL2DAQUERY is used to get L2DA related
information: default slave and map entries.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c | 69 +++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_bonding.h | 15 +++++++++
 include/uapi/linux/sockios.h    |  3 +-
 net/core/dev_ioctl.c            |  2 ++
 net/socket.c                    |  2 ++
 5 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index ddaee7b..ecfe349 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1935,6 +1935,59 @@ out:
 	return res;
 }
 
+struct bond_ioctl_l2da_query_clb_ctx {
+	int res;
+	__s32 i;
+	struct l2da_query *l2da_query;
+};
+
+static int bond_ioctl_l2da_query_clb(const unsigned char *da,
+				     struct slave *slave, void *_ctx)
+{
+	struct bond_ioctl_l2da_query_clb_ctx *ctx = _ctx;
+	if (ctx->i == ctx->l2da_query->entry_id) {
+		strcpy(ctx->l2da_query->slave_name, slave->dev->name);
+		memcpy(ctx->l2da_query->da, da, ETH_ALEN);
+		ctx->res = 0;
+		return 1;
+	}
+	++ctx->i;
+	return 0;
+}
+
+static int bond_ioctl_l2da_query(struct net_device *bond_dev,
+				 struct l2da_query *l2da_query)
+{
+	int res = -EINVAL;
+	struct bonding *bond = netdev_priv(bond_dev);
+	struct bond_ioctl_l2da_query_clb_ctx ctx;
+
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		goto out;
+
+	switch (l2da_query->query) {
+	case BOND_L2DA_QUERY_DEFAULT:
+		res = bond_l2da_get_default_slave_name(bond,
+					l2da_query->slave_name,
+					sizeof(l2da_query->slave_name));
+		break;
+	case BOND_L2DA_QUERY_MAP:
+		ctx.res        = res;
+		ctx.i          = 0;
+		ctx.l2da_query = l2da_query;
+		bond_l2da_call_foreach(bond, bond_ioctl_l2da_query_clb, &ctx);
+		res = ctx.res;
+		break;
+	default:
+		break;
+	}
+out:
+	read_unlock(&bond->lock);
+	return res;
+}
+
 static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
@@ -3239,6 +3292,8 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 	struct ifbond __user *u_binfo = NULL;
 	struct ifslave k_sinfo;
 	struct ifslave __user *u_sinfo = NULL;
+	struct l2da_query k_l2da_query;
+	struct l2da_query __user *u_l2da_query = NULL;
 	struct mii_ioctl_data *mii = NULL;
 	struct net *net;
 	int res = 0;
@@ -3301,6 +3356,20 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 			return -EFAULT;
 
 		return res;
+	case SIOCBONDL2DAQUERY:
+		u_l2da_query = (struct l2da_query __user *)ifr->ifr_data;
+
+		if (copy_from_user(&k_l2da_query, u_l2da_query,
+				   sizeof(k_l2da_query)))
+			return -EFAULT;
+
+		res = bond_ioctl_l2da_query(bond_dev, &k_l2da_query);
+		if (res == 0 &&
+		    copy_to_user(u_l2da_query, &k_l2da_query,
+				 sizeof(k_l2da_query)))
+			return -EFAULT;
+
+		return res;
 	default:
 		/* Go on */
 		break;
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index a4d3baf..fdfe491 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -100,6 +100,10 @@
 #define BOND_L2DA_CMD_MAP_ADD        1 /* add DA:iface mapping */
 #define BOND_L2DA_CMD_MAP_DEL        2 /* add DA mapping */
 
+/* l2da queries */
+#define BOND_L2DA_QUERY_DEFAULT      0 /* get default slave */
+#define BOND_L2DA_QUERY_MAP          1 /* get map entry */
+
 typedef struct ifbond {
 	__s32 bond_mode;
 	__s32 num_slaves;
@@ -133,6 +137,17 @@ struct l2da_cmd {
 				    * */
 };
 
+struct l2da_query {
+	__u16 query; /* one of the BOND_L2DA_QUERY_... queries */
+	__s32 entry_id;  /* Used as IN parameter in:
+			  *   BOND_L2DA_QUERY_MAP */
+	__u8 da[ETH_ALEN]; /* Used as OUT parameter in:
+			    *   BOND_L2DA_QUERY_MAP */
+	char slave_name[IFNAMSIZ]; /* Used as OUT parameter in:
+				    *   BOND_L2DA_QUERY_DEFAULT
+				    *   BOND_L2DA_QUERY_MAP */
+};
+
 #endif /* _LINUX_IF_BONDING_H */
 
 /*
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index 2af055d..86b1d0a 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -118,7 +118,8 @@
 #define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
 #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
 #define SIOCBONDL2DACMD        0x8996   /* l2da related command */
-			
+#define SIOCBONDL2DAQUERY      0x8997   /* l2da related query */
+
 /* bridge calls */
 #define SIOCBRADDBR     0x89a0		/* create new bridge device     */
 #define SIOCBRDELBR     0x89a1		/* remove bridge device         */
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 3a4716a..f189744 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -322,6 +322,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 		    cmd == SIOCBONDINFOQUERY ||
 		    cmd == SIOCBONDCHANGEACTIVE ||
 		    cmd == SIOCBONDL2DACMD ||
+		    cmd == SIOCBONDL2DAQUERY ||
 		    cmd == SIOCGMIIPHY ||
 		    cmd == SIOCGMIIREG ||
 		    cmd == SIOCSMIIREG ||
@@ -528,6 +529,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 		/* fall through */
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
+	case SIOCBONDL2DAQUERY:
 		dev_load(net, ifr.ifr_name);
 		rtnl_lock();
 		ret = dev_ifsioc(net, &ifr, cmd);
diff --git a/net/socket.c b/net/socket.c
index 2ed653b..e4524f5 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2988,6 +2988,7 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
 		return err;
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
+	case SIOCBONDL2DAQUERY:
 		uifr = compat_alloc_user_space(sizeof(*uifr));
 		if (copy_in_user(&uifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
 			return -EFAULT;
@@ -3264,6 +3265,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCBONDINFOQUERY:
 	case SIOCBONDCHANGEACTIVE:
 	case SIOCBONDL2DACMD:
+	case SIOCBONDL2DAQUERY:
 		return bond_ioctl(net, cmd, argp);
 	case SIOCADDRT:
 	case SIOCDELRT:
-- 
1.8.3.1

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

* Re: [PATCH 0/4] bonding: L2DA mode
  2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
                   ` (3 preceding siblings ...)
  2013-11-21 14:55 ` [PATCH 4/4] bonding: L2DA query IOCTL Anton Nayshtut
@ 2013-11-21 15:09 ` Andy Gospodarek
  4 siblings, 0 replies; 9+ messages in thread
From: Andy Gospodarek @ 2013-11-21 15:09 UTC (permalink / raw)
  To: Anton Nayshtut, Jay Vosburgh, Veaceslav Falico, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev

On 11/21/2013 09:55 AM, Anton Nayshtut wrote:
> L2 Destination Address based (L2DA) mode allows bonding to send packets using
> different slaves according to packets L2 Destination Address.
>
> In L2DA mode, the bonding maintains a default slave and DA/slave map.
>
> Upon a packet transmission, the bonding examines DA of the packet and tries to
> find a corresponding slave within the map. If found, the slave is used for the
> packet transmission. Otherwise, the default slave is used. If the default slave
> is unable to transmit at this moment, the bonding tries to fall back to an
> arbitrary slave that can transmit.
>
> Both the default slave and the map can be controlled via sysfs or by ioctls.
>
> Anton Nayshtut (4):
>    bonding: L2DA mode added
>    bonding: L2DA mode intergated
>    bonding: L2DA command IOCTL
>    bonding: L2DA query IOCTL
>
>   drivers/net/bonding/Makefile       |   2 +-
>   drivers/net/bonding/bond_l2da.c    | 425 +++++++++++++++++++++++++++++++++++++
>   drivers/net/bonding/bond_l2da.h    |  56 +++++
>   drivers/net/bonding/bond_main.c    | 172 ++++++++++++++-
>   drivers/net/bonding/bond_options.c |  17 +-
>   drivers/net/bonding/bond_sysfs.c   | 223 ++++++++++++++++++-
>   drivers/net/bonding/bonding.h      |   7 +
>   include/uapi/linux/if_bonding.h    |  32 +++
>   include/uapi/linux/sockios.h       |   4 +-
>   net/core/dev_ioctl.c               |   4 +
>   net/socket.c                       |   4 +
>   11 files changed, 937 insertions(+), 9 deletions(-)
>   create mode 100644 drivers/net/bonding/bond_l2da.c
>   create mode 100644 drivers/net/bonding/bond_l2da.h
>

I have not done a full review of this, but I notice right away that 
there are no patches to Documentation/networking/bonding.txt.  You can 
wait until there are more comments before submitting a v2 of this 
series, but please make sure it includes a documentation update when 
submitted.

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

* Re: [PATCH 1/4] bonding: L2DA mode added
  2013-11-21 14:55 ` [PATCH 1/4] bonding: L2DA mode added Anton Nayshtut
@ 2013-11-21 15:32   ` Eric Dumazet
  2013-11-21 15:37     ` Nikolay Aleksandrov
  0 siblings, 1 reply; 9+ messages in thread
From: Eric Dumazet @ 2013-11-21 15:32 UTC (permalink / raw)
  To: Anton Nayshtut
  Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet, linux-kernel, netdev,
	Erez Kirshenbaum, Boris Lapshin

On Thu, 2013-11-21 at 16:55 +0200, Anton Nayshtut wrote:
> This patches introduces L2DA bonding module with all the data structures and
> interfaces. It's not integrated yet.

I veto any kind of new bonding mode not using RCU.

Adding another rwlock in 2013 is simply not an option.

Thanks

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

* Re: [PATCH 1/4] bonding: L2DA mode added
  2013-11-21 15:32   ` Eric Dumazet
@ 2013-11-21 15:37     ` Nikolay Aleksandrov
  2013-11-25  9:10       ` Anton Nayshtut
  0 siblings, 1 reply; 9+ messages in thread
From: Nikolay Aleksandrov @ 2013-11-21 15:37 UTC (permalink / raw)
  To: Eric Dumazet, Anton Nayshtut
  Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet, linux-kernel, netdev,
	Erez Kirshenbaum, Boris Lapshin

On 11/21/2013 04:32 PM, Eric Dumazet wrote:
> On Thu, 2013-11-21 at 16:55 +0200, Anton Nayshtut wrote:
>> This patches introduces L2DA bonding module with all the data structures and
>> interfaces. It's not integrated yet.
> 
> I veto any kind of new bonding mode not using RCU.
> 
> Adding another rwlock in 2013 is simply not an option.
> 
> Thanks
> 
> 
+1
Also,
I haven't read the patch-set closely but based on the description in the first
patch I think you can already get this (or very similar) functionality by using
the slave override option in the bonding. You can match the destination L2
address and override the slave based on that.

Nik

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

* Re: [PATCH 1/4] bonding: L2DA mode added
  2013-11-21 15:37     ` Nikolay Aleksandrov
@ 2013-11-25  9:10       ` Anton Nayshtut
  0 siblings, 0 replies; 9+ messages in thread
From: Anton Nayshtut @ 2013-11-25  9:10 UTC (permalink / raw)
  To: Nikolay Aleksandrov, Eric Dumazet
  Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet, linux-kernel, netdev,
	Erez Kirshenbaum, Boris Lapshin

Eric, Nik, Andy,

Thanks for your comments.

I'll investigate the override option approach first, and then will
re-implement the patch using RCU with corresponding
Documentation/networking/bonding.txt changes, if still needed.

P.S. Sorry for delay with my reply. It was caused by personal reasons.

Best Regards,
Anton

On 21/11/2013 17:37, Nikolay Aleksandrov wrote:
> On 11/21/2013 04:32 PM, Eric Dumazet wrote:
>> On Thu, 2013-11-21 at 16:55 +0200, Anton Nayshtut wrote:
>>> This patches introduces L2DA bonding module with all the data structures and
>>> interfaces. It's not integrated yet.
>>
>> I veto any kind of new bonding mode not using RCU.
>>
>> Adding another rwlock in 2013 is simply not an option.
>>
>> Thanks
>>
>>
> +1
> Also,
> I haven't read the patch-set closely but based on the description in the first
> patch I think you can already get this (or very similar) functionality by using
> the slave override option in the bonding. You can match the destination L2
> address and override the slave based on that.
> 
> Nik
> 

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

end of thread, other threads:[~2013-11-25  9:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-21 14:55 [PATCH 0/4] bonding: L2DA mode Anton Nayshtut
2013-11-21 14:55 ` [PATCH 1/4] bonding: L2DA mode added Anton Nayshtut
2013-11-21 15:32   ` Eric Dumazet
2013-11-21 15:37     ` Nikolay Aleksandrov
2013-11-25  9:10       ` Anton Nayshtut
2013-11-21 14:55 ` [PATCH 2/4] bonding: L2DA mode intergated Anton Nayshtut
2013-11-21 14:55 ` [PATCH 3/4] bonding: L2DA command IOCTL Anton Nayshtut
2013-11-21 14:55 ` [PATCH 4/4] bonding: L2DA query IOCTL Anton Nayshtut
2013-11-21 15:09 ` [PATCH 0/4] bonding: L2DA mode Andy Gospodarek

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).