lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1520256514-27885-3-git-send-email-john.hurley@netronome.com>
Date:   Mon,  5 Mar 2018 13:28:30 +0000
From:   John Hurley <john.hurley@...ronome.com>
To:     netdev@...r.kernel.org
Cc:     jiri@...lanox.com, ogerlitz@...lanox.com,
        jakub.kicinski@...ronome.com, simon.horman@...ronome.com,
        John Hurley <john.hurley@...ronome.com>
Subject: [RFC net-next 2/6] driver: net: bonding: allow registration of tc offload callbacks in bond

Allow drivers to register netdev callbacks for tc offload in linux bonds.
If a netdev has registered and is a slave of a given bond, then any tc
rules offloaded to the bond will be relayed to it if both the bond and the
slave permit hw offload.

Because the bond itself is not offloaded, just the rules, we don't care
about whether the bond ports are on the same device or whether some of
slaves are representor ports and some are not.

Signed-off-by: John Hurley <john.hurley@...ronome.com>
---
 drivers/net/bonding/bond_main.c | 195 +++++++++++++++++++++++++++++++++++++++-
 include/net/bonding.h           |   7 ++
 2 files changed, 201 insertions(+), 1 deletion(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e6415f6..d9e41cf 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -335,9 +335,201 @@ static inline unsigned int bond_get_offload_cnt(struct bonding *bond)
 	return bond->tc_block->offloadcnt;
 }
 
+struct tcf_bond_cb {
+	struct list_head list;
+	tc_setup_cb_t *cb;
+	void *cb_priv;
+};
+
+struct tcf_bond_off {
+	struct rhash_head ht_node;
+	const struct net_device *netdev;
+	unsigned int refcnt;
+	struct list_head cb_list;
+};
+
+static const struct rhashtable_params tcf_bond_ht_params = {
+	.key_offset = offsetof(struct tcf_bond_off, netdev),
+	.head_offset = offsetof(struct tcf_bond_off, ht_node),
+	.key_len = sizeof(const struct net_device *),
+};
+
+static struct tcf_bond_off *tcf_bond_off_lookup(const struct net_device *dev)
+{
+	struct bond_net *bn = net_generic(dev_net(dev), bond_net_id);
+
+	return rhashtable_lookup_fast(&bn->bond_offload_ht, &dev,
+				      tcf_bond_ht_params);
+}
+
+static struct tcf_bond_cb *tcf_bond_off_cb_lookup(struct tcf_bond_off *off,
+						  tc_setup_cb_t *cb,
+						  void *cb_priv)
+{
+	struct tcf_bond_cb *bond_cb;
+
+	list_for_each_entry(bond_cb, &off->cb_list, list)
+		if (bond_cb->cb == cb && bond_cb->cb_priv == cb_priv)
+			return bond_cb;
+	return NULL;
+}
+
+static struct tcf_bond_off *tcf_bond_off_get(const struct net_device *dev,
+					     tc_setup_cb_t *cb,
+					     void *cb_priv)
+{
+	struct tcf_bond_off *bond_off;
+	struct bond_net *bn;
+
+	bond_off = tcf_bond_off_lookup(dev);
+	if (bond_off)
+		goto inc_ref;
+
+	bond_off = kzalloc(sizeof(*bond_off), GFP_KERNEL);
+	if (!bond_off)
+		return NULL;
+	INIT_LIST_HEAD(&bond_off->cb_list);
+	bond_off->netdev = dev;
+	bn = net_generic(dev_net(dev), bond_net_id);
+	rhashtable_insert_fast(&bn->bond_offload_ht, &bond_off->ht_node,
+			       tcf_bond_ht_params);
+
+inc_ref:
+	bond_off->refcnt++;
+	return bond_off;
+}
+
+static void tcf_bond_off_put(struct tcf_bond_off *bond_off)
+{
+	struct bond_net *bn;
+
+	if (--bond_off->refcnt)
+		return;
+	bn = net_generic(dev_net(bond_off->netdev), bond_net_id);
+	rhashtable_remove_fast(&bn->bond_offload_ht, &bond_off->ht_node,
+			       tcf_bond_ht_params);
+	kfree(bond_off);
+}
+
+static int tcf_bond_off_cb_add(struct tcf_bond_off *bond_off,
+			       tc_setup_cb_t *cb, void *cb_priv)
+{
+	struct tcf_bond_cb *bond_cb;
+
+	bond_cb = tcf_bond_off_cb_lookup(bond_off, cb, cb_priv);
+	if (WARN_ON(bond_cb))
+		return -EEXIST;
+	bond_cb = kzalloc(sizeof(*bond_cb), GFP_KERNEL);
+	if (!bond_cb)
+		return -ENOMEM;
+	bond_cb->cb = cb;
+	bond_cb->cb_priv = cb_priv;
+	list_add(&bond_cb->list, &bond_off->cb_list);
+	return 0;
+}
+
+static void tcf_bond_off_cb_del(struct tcf_bond_off *bond_off,
+				tc_setup_cb_t *cb, void *cb_priv)
+{
+	struct tcf_bond_cb *bond_cb;
+
+	bond_cb = tcf_bond_off_cb_lookup(bond_off, cb, cb_priv);
+	if (WARN_ON(!bond_cb))
+		return;
+	list_del(&bond_cb->list);
+	kfree(bond_cb);
+}
+
+static int __tc_setup_cb_bond_register(const struct net_device *dev,
+				       tc_setup_cb_t *cb, void *cb_priv)
+{
+	struct tcf_bond_off *bond_off = tcf_bond_off_get(dev, cb, cb_priv);
+	int err;
+
+	if (!bond_off)
+		return -ENOMEM;
+	err = tcf_bond_off_cb_add(bond_off, cb, cb_priv);
+	if (err)
+		goto err_cb_add;
+	return 0;
+
+err_cb_add:
+	tcf_bond_off_put(bond_off);
+	return err;
+}
+
+int tc_setup_cb_bond_register(const struct net_device *dev, tc_setup_cb_t *cb,
+			      void *cb_priv)
+{
+	int err;
+
+	rtnl_lock();
+	err = __tc_setup_cb_bond_register(dev, cb, cb_priv);
+	rtnl_unlock();
+	return err;
+}
+EXPORT_SYMBOL_GPL(tc_setup_cb_bond_register);
+
+static void __tc_setup_cb_bond_unregister(const struct net_device *dev,
+					  tc_setup_cb_t *cb, void *cb_priv)
+{
+	struct tcf_bond_off *bond_off = tcf_bond_off_lookup(dev);
+
+	if (WARN_ON(!bond_off))
+		return;
+	tcf_bond_off_cb_del(bond_off, cb, cb_priv);
+	tcf_bond_off_put(bond_off);
+}
+
+void tc_setup_cb_bond_unregister(const struct net_device *dev,
+				 tc_setup_cb_t *cb, void *cb_priv)
+{
+	rtnl_lock();
+	__tc_setup_cb_bond_unregister(dev, cb, cb_priv);
+	rtnl_unlock();
+}
+EXPORT_SYMBOL_GPL(tc_setup_cb_bond_unregister);
+
 static int bond_tc_relay_cb(enum tc_setup_type type, void *type_data,
 			    void *cb_priv)
 {
+	struct net_device *bond_dev = cb_priv;
+	struct tcf_bond_off *bond_off;
+	struct tcf_bond_cb *bond_cb;
+	struct list_head *iter;
+	struct bonding *bond;
+	struct slave *slave;
+	int err;
+
+	bond = netdev_priv(bond_dev);
+
+	if (!tc_can_offload(bond_dev))
+		return -EOPNOTSUPP;
+
+	bond_for_each_slave(bond, slave, iter) {
+		if (!tc_can_offload(slave->dev))
+			continue;
+
+		bond_off = tcf_bond_off_lookup(slave->dev);
+		if (!bond_off)
+			continue;
+
+		list_for_each_entry(bond_cb, &bond_off->cb_list, list) {
+			err = bond_cb->cb(type, type_data, bond_cb->cb_priv);
+			/* Possible here that some of the relayed callbacks are
+			 * accepted before the error meaning a rule add may be
+			 * offloaded to some ports and not others.
+			 *
+			 * If skip_sw is set then the classifier will generate
+			 * a destroy message undoing the adds. If not set then
+			 * some of the relays exist in hw and some software
+			 * only.
+			 */
+			if (err)
+				return err;
+		}
+	}
+
 	return 0;
 }
 
@@ -4829,7 +5021,7 @@ static int __net_init bond_net_init(struct net *net)
 	bond_create_proc_dir(bn);
 	bond_create_sysfs(bn);
 
-	return 0;
+	return rhashtable_init(&bn->bond_offload_ht, &tcf_bond_ht_params);
 }
 
 static void __net_exit bond_net_exit(struct net *net)
@@ -4848,6 +5040,7 @@ static void __net_exit bond_net_exit(struct net *net)
 	rtnl_unlock();
 
 	bond_destroy_proc_dir(bn);
+	rhashtable_destroy(&bn->bond_offload_ht);
 }
 
 static struct pernet_operations bond_net_ops = {
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 424b9ea..056f5fc 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -30,6 +30,7 @@
 #include <net/bond_alb.h>
 #include <net/bond_options.h>
 #include <net/pkt_cls.h>
+#include <net/act_api.h>
 
 #define BOND_MAX_ARP_TARGETS	16
 
@@ -584,6 +585,7 @@ struct bond_net {
 	struct proc_dir_entry	*proc_dir;
 #endif
 	struct class_attribute	class_attr_bonding_masters;
+	struct rhashtable	bond_offload_ht;
 };
 
 int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
@@ -620,6 +622,11 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
 void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay);
 void bond_work_init_all(struct bonding *bond);
 
+int tc_setup_cb_bond_register(const struct net_device *dev, tc_setup_cb_t *cb,
+			      void *cb_priv);
+void tc_setup_cb_bond_unregister(const struct net_device *dev,
+				 tc_setup_cb_t *cb, void *cb_priv);
+
 #ifdef CONFIG_PROC_FS
 void bond_create_proc_entry(struct bonding *bond);
 void bond_remove_proc_entry(struct bonding *bond);
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ