From patchwork Thu Apr 23 15:01:27 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konstantin Khlebnikov X-Patchwork-Id: 463987 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id C90AE140133 for ; Fri, 24 Apr 2015 01:01:38 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="verification failed; unprotected key" header.d=yandex-team.ru header.i=@yandex-team.ru header.b=V8TTHjoS; dkim-adsp=unknown (unprotected policy); dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965394AbbDWPBe (ORCPT ); Thu, 23 Apr 2015 11:01:34 -0400 Received: from forward-corp1m.cmail.yandex.net ([5.255.216.100]:34749 "EHLO forward-corp1m.cmail.yandex.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756917AbbDWPBd (ORCPT ); Thu, 23 Apr 2015 11:01:33 -0400 Received: from smtpcorp1m.mail.yandex.net (smtpcorp1m.mail.yandex.net [77.88.61.150]) by forward-corp1m.cmail.yandex.net (Yandex) with ESMTP id D37C6609AE; Thu, 23 Apr 2015 18:01:28 +0300 (MSK) Received: from smtpcorp1m.mail.yandex.net (localhost [127.0.0.1]) by smtpcorp1m.mail.yandex.net (Yandex) with ESMTP id A409E2CA0504; Thu, 23 Apr 2015 18:01:28 +0300 (MSK) Received: from unknown (unknown [2a02:6b8:0:408:9045:86e4:64db:55fa]) by smtpcorp1m.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id BhCjNQu4FV-1Si8YofB; Thu, 23 Apr 2015 18:01:28 +0300 (using TLSv1.2 with cipher AES128-SHA256 (128/128 bits)) (Client certificate not present) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1429801288; bh=vv6TsBFb9Dh66IpVymXjdeN5c7xd3A/hEya/9BlmffE=; h=Subject:From:To:Cc:Date:Message-ID:User-Agent:MIME-Version: Content-Type:Content-Transfer-Encoding; b=V8TTHjoSgj3RQ/xFjradxAZ94yMI8LTIaw98PdG3ZEdroRiwdmwwaYWh11CdGcV0V uqTP6FnpOoWw76Rjgtc65BYoKoTEe+Q9Hz3fJPw51i8bCAF+wrKE6B/bDNW54i0FKc eiHxV+ewOf6jzVPfPHeKwqmyHAGn8E0zySx9n550= Authentication-Results: smtpcorp1m.mail.yandex.net; dkim=pass header.i=@yandex-team.ru Subject: [PATCH RFC] openvswitch: add support for netpoll From: Konstantin Khlebnikov To: dev@openvswitch.org, Pravin Shelar Cc: netdev@vger.kernel.org, "David S. Miller" Date: Thu, 23 Apr 2015 18:01:27 +0300 Message-ID: <20150423150127.12697.83460.stgit@buzz> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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(-) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 #include #include +#include #include #include @@ -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 #include #include +#include #include @@ -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 */