netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Konstantin Khlebnikov <khlebnikov-XoJtRXgx1JseBXzfvpsJ4g@public.gmane.org>
To: dev-yBygre7rU0TnMu66kgdUjQ@public.gmane.org,
	Pravin Shelar <pshelar-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	"David S. Miller" <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
Subject: [PATCH RFC] openvswitch: add support for netpoll
Date: Thu, 23 Apr 2015 18:01:27 +0300	[thread overview]
Message-ID: <20150423150127.12697.83460.stgit@buzz> (raw)

This patch simply forwards unicast netpoll packets via one of physical
interface in datapath depending on source mac address from the skb.

It seems possible to use common net flow classification for netpoll but
there is no way to guarantee presence of right flow in kernel cache.

Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
---
 net/openvswitch/vport-internal_dev.c |   74 ++++++++++++++++++++++++++++++++++
 net/openvswitch/vport-netdev.c       |   63 ++++++++++++++++++++++++++++-
 net/openvswitch/vport-netdev.h       |   15 +++++++
 3 files changed, 148 insertions(+), 4 deletions(-)

diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 6a55f7105505..d1eb09ac09e8 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -23,6 +23,7 @@
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/skbuff.h>
+#include <linux/netpoll.h>
 
 #include <net/dst.h>
 #include <net/xfrm.h>
@@ -66,11 +67,77 @@ static struct rtnl_link_stats64 *internal_dev_get_stats(struct net_device *netde
 	return stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+static void internal_dev_poll_controller(struct net_device *dev)
+{
+}
+
+static struct netdev_vport *get_local_netdev_vport(struct net_device *dev)
+{
+	struct datapath *dp = internal_dev_priv(dev)->vport->dp;
+
+	return netdev_vport_priv(ovs_lookup_vport(dp, OVSP_LOCAL));
+}
+
+static int internal_dev_netpoll_setup(struct net_device *internal_dev,
+				      struct netpoll_info *info)
+{
+	struct netdev_vport *local = get_local_netdev_vport(internal_dev);
+	struct netdev_vport *lower;
+	struct list_head *iter;
+	int ret = -EOPNOTSUPP;
+
+	ASSERT_RTNL();
+	/* succeed if at least one lower device can handle netpoll */
+	netdev_for_each_lower_private(local->dev, lower, iter)
+		if (!ovs_netdev_netpoll_enable(lower))
+			ret = 0;
+	/* enable netpoll on local device as mark for new devices */
+	if (!ret && local->dev != internal_dev)
+		ovs_netdev_netpoll_enable(local);
+	return ret;
+}
+
+static void internal_dev_netpoll_cleanup(struct net_device *internal_dev)
+{
+	struct netdev_vport *local = get_local_netdev_vport(internal_dev);
+	struct netdev_vport *lower;
+	struct list_head *iter;
+
+	/* FIXME needs reference counting for more than one netpoll in dp */
+
+	ASSERT_RTNL();
+	netdev_for_each_lower_private(local->dev, lower, iter)
+		ovs_netdev_netpoll_disable(lower);
+	ovs_netdev_netpoll_disable(local);
+}
+
+static void internal_dev_netpoll_xmit(struct sk_buff *skb,
+				      struct net_device *internal_dev)
+{
+	struct netdev_vport *local = get_local_netdev_vport(internal_dev);
+	struct netdev_vport *lower;
+	struct list_head *iter;
+
+	netdev_for_each_lower_private_rcu(local->dev, lower, iter)
+		if (!ovs_netdev_netpoll_send(lower, skb))
+			return; /* Unicast only, first gets all. */
+	dev_kfree_skb_irq(skb);
+}
+
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 /* Called with rcu_read_lock_bh. */
 static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
 	rcu_read_lock();
-	ovs_vport_receive(internal_dev_priv(netdev)->vport, skb, NULL);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	if (unlikely(netpoll_tx_running(netdev)))
+		internal_dev_netpoll_xmit(skb, netdev);
+	else
+#endif
+		ovs_vport_receive(internal_dev_priv(netdev)->vport, skb, NULL);
 	rcu_read_unlock();
 	return 0;
 }
@@ -122,6 +189,11 @@ static const struct net_device_ops internal_dev_netdev_ops = {
 	.ndo_set_mac_address = eth_mac_addr,
 	.ndo_change_mtu = internal_dev_change_mtu,
 	.ndo_get_stats64 = internal_dev_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = internal_dev_poll_controller,
+	.ndo_netpoll_setup = internal_dev_netpoll_setup,
+	.ndo_netpoll_cleanup = internal_dev_netpoll_cleanup,
+#endif
 };
 
 static struct rtnl_link_ops internal_dev_link_ops __read_mostly = {
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 4776282c6417..324fb078d32a 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -26,6 +26,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
 #include <linux/openvswitch.h>
+#include <linux/netpoll.h>
 
 #include <net/llc.h>
 
@@ -86,10 +87,59 @@ static struct net_device *get_dpdev(const struct datapath *dp)
 	return netdev_vport_priv(local)->dev;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+int ovs_netdev_netpoll_enable(struct netdev_vport *netdev_vport)
+{
+	struct netpoll *np;
+	int err;
+
+	if (netdev_vport->np)
+		return 0;
+
+	np = kzalloc(sizeof(*np), GFP_KERNEL);
+	if (!np)
+		return -ENOMEM;
+
+	err = __netpoll_setup(np, netdev_vport->dev);
+	if (err)
+		kfree(np);
+	else
+		netdev_vport->np = np;
+	return err;
+}
+
+void ovs_netdev_netpoll_disable(struct netdev_vport *netdev_vport)
+{
+	struct netpoll *np = netdev_vport->np;
+
+	if (np) {
+		netdev_vport->np = NULL;
+		__netpoll_free_async(np);
+	}
+}
+
+int ovs_netdev_netpoll_send(struct netdev_vport *netdev_vport,
+			    struct sk_buff *skb)
+{
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+	if (!netdev_vport->np)
+		return -EOPNOTSUPP;
+
+	if (!ether_addr_equal(eth->h_source, netdev_vport->dev->dev_addr))
+		return -EXDEV;  /* Not ours */
+
+	skb->dev = netdev_vport->dev;
+	netpoll_send_skb_on_dev(netdev_vport->np, skb, netdev_vport->dev);
+	return 0;
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 static struct vport *netdev_create(const struct vport_parms *parms)
 {
 	struct vport *vport;
 	struct netdev_vport *netdev_vport;
+	struct net_device *dpdev;
 	int err;
 
 	vport = ovs_vport_alloc(sizeof(struct netdev_vport),
@@ -115,11 +165,16 @@ static struct vport *netdev_create(const struct vport_parms *parms)
 	}
 
 	rtnl_lock();
-	err = netdev_master_upper_dev_link(netdev_vport->dev,
-					   get_dpdev(vport->dp));
+	call_netdevice_notifiers(NETDEV_JOIN, netdev_vport->dev);
+	dpdev = get_dpdev(vport->dp);
+	err = netdev_master_upper_dev_link_private(netdev_vport->dev,
+						   dpdev, netdev_vport);
 	if (err)
 		goto error_unlock;
 
+	if (dpdev->npinfo)
+		ovs_netdev_netpoll_enable(netdev_vport);
+
 	err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
 					 vport);
 	if (err)
@@ -132,7 +187,8 @@ static struct vport *netdev_create(const struct vport_parms *parms)
 	return vport;
 
 error_master_upper_dev_unlink:
-	netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
+	ovs_netdev_netpoll_disable(netdev_vport);
+	netdev_upper_dev_unlink(netdev_vport->dev, dpdev);
 error_unlock:
 	rtnl_unlock();
 error_put:
@@ -159,6 +215,7 @@ void ovs_netdev_detach_dev(struct vport *vport)
 	ASSERT_RTNL();
 	netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
 	netdev_rx_handler_unregister(netdev_vport->dev);
+	ovs_netdev_netpoll_disable(netdev_vport);
 	netdev_upper_dev_unlink(netdev_vport->dev,
 				netdev_master_upper_dev_get(netdev_vport->dev));
 	dev_set_promiscuity(netdev_vport->dev, -1);
diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h
index 6f7038e79c52..a9085522d73c 100644
--- a/net/openvswitch/vport-netdev.h
+++ b/net/openvswitch/vport-netdev.h
@@ -30,6 +30,9 @@ struct netdev_vport {
 	struct rcu_head rcu;
 
 	struct net_device *dev;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	struct netpoll *np;
+#endif
 };
 
 static inline struct netdev_vport *
@@ -44,4 +47,16 @@ void ovs_netdev_detach_dev(struct vport *);
 int __init ovs_netdev_init(void);
 void ovs_netdev_exit(void);
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+int ovs_netdev_netpoll_enable(struct netdev_vport *);
+void ovs_netdev_netpoll_disable(struct netdev_vport *);
+int ovs_netdev_netpoll_send(struct netdev_vport *, struct sk_buff *);
+#else
+static inline int ovs_netdev_netpoll_enable(struct netdev_vport *nv)
+{
+	return -EOPNOTSUPP;
+}
+static inline void ovs_netdev_netpoll_disable(struct netdev_vport *nv) { }
+#endif
+
 #endif /* vport_netdev.h */

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

                 reply	other threads:[~2015-04-23 15:01 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150423150127.12697.83460.stgit@buzz \
    --to=khlebnikov-xojtrxgx1jsebxzfvpsj4g@public.gmane.org \
    --cc=davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org \
    --cc=dev-yBygre7rU0TnMu66kgdUjQ@public.gmane.org \
    --cc=netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=pshelar-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).