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 */
