[ovs-dev] [PATCH v2] datapath: Turn vports with dependencies into separate modules

Thomas Graf tgraf at noironetworks.com
Tue Mar 31 20:30:21 UTC 2015


Upstream commit:
    The internal and netdev vport remain part of openvswitch.ko. Encap
    vports including vxlan, gre, and geneve can be built as separate
    modules and are loaded on demand. Modules can be unloaded after use.
    Datapath ports keep a reference to the vport module during their
    lifetime.

    Allows to remove the error prone maintenance of the global list
    vport_ops_list.

    Signed-off-by: Thomas Graf <tgraf at suug.ch>
    Signed-off-by: David S. Miller <davem at davemloft.net>

Also folds in the follow-up commits 9ba559d9ca3 to turned the non-GPL
symbol exports to GPL exports, and fa2d8ff4e35 which fixes a module
reference release bug.

Exports various backwards compat functions linked into the main
openvswitch module as GPL symbols to ensure vport modules can use them.

Some fiddling with the Makefile was needed to work around the fact
that Makefile variables can't contain '-' characters needed to define
'vport-xxx' module sources. Also, Kbuild complains heavily if a
$(module)-y = $(module).o is defined which is actually backed with a .c
file of the same name. Therefore, a new $(build_multi_modules) variable
is defined which lists all module which consist of more than one source
file.

Upstream: 62b9c8d0372 ("ovs: Turn vports with dependencies into separate modules")
Upstream: 9ba559d9ca3 ("openvswitch: Export symbols as GPL symbols.")
Upstream: fa2d8ff4e35 ("openvswitch: Return vport module ref before destruction")
Signed-off-by: Thomas Graf <tgraf at noironetworks.com>
---
v2:
 - Addressed Pravin's comments:
   - Exported symbol lockdep_ovsl_is_held
   - Fixed module reference bug (upstream: fa2d8ff4e35)

 datapath/Modules.mk                     |  20 +++++--
 datapath/datapath.c                     |  17 +++++-
 datapath/linux/Kbuild.in                |   4 +-
 datapath/linux/compat/flow_dissector.c  |   1 +
 datapath/linux/compat/geneve.c          |   3 +
 datapath/linux/compat/gre.c             |   4 ++
 datapath/linux/compat/ip_tunnels_core.c |   4 ++
 datapath/linux/compat/udp_tunnel.c      |   6 ++
 datapath/linux/compat/vxlan.c           |   3 +
 datapath/vport-geneve.c                 |  22 ++++++-
 datapath/vport-gre.c                    |  39 ++++++++++++-
 datapath/vport-internal_dev.c           |  17 +++++-
 datapath/vport-lisp.c                   |  22 ++++++-
 datapath/vport-netdev.c                 |  13 ++++-
 datapath/vport-netdev.h                 |   3 +
 datapath/vport-vxlan.c                  |  23 +++++++-
 datapath/vport.c                        | 100 ++++++++++++++++++++++----------
 datapath/vport.h                        |  16 ++---
 18 files changed, 259 insertions(+), 58 deletions(-)

diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index 33f9dd9..e9f4079 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -2,7 +2,14 @@
 #
 # Some modules should be built but not distributed, e.g. third-party
 # hwtable modules.
-both_modules = openvswitch
+build_multi_modules = \
+	openvswitch
+both_modules = \
+	$(build_multi_modules) \
+	vport_geneve \
+	vport_gre \
+	vport_lisp \
+	vport_vxlan
 build_modules = $(both_modules)	# Modules to build
 dist_modules = $(both_modules)	# Modules to distribute
 
@@ -14,12 +21,13 @@ openvswitch_sources = \
 	flow_netlink.c \
 	flow_table.c \
 	vport.c \
-	vport-geneve.c \
-	vport-gre.c \
 	vport-internal_dev.c \
-	vport-lisp.c \
-	vport-netdev.c \
-	vport-vxlan.c
+	vport-netdev.c
+
+vport_geneve_sources = vport-geneve.c
+vport_vxlan_sources = vport-vxlan.c
+vport_gre_sources = vport-gre.c
+vport_lisp_sources = vport-lisp.c
 
 openvswitch_headers = \
 	compat.h \
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 7f431ed..c0af9ad 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -61,6 +61,7 @@
 #include "vport-netdev.h"
 
 int ovs_net_id __read_mostly;
+EXPORT_SYMBOL_GPL(ovs_net_id);
 
 static struct genl_family dp_packet_genl_family;
 static struct genl_family dp_flow_genl_family;
@@ -134,6 +135,7 @@ int lockdep_ovsl_is_held(void)
 	else
 		return 1;
 }
+EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held);
 #endif
 
 static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
@@ -1900,6 +1902,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 		return -ENOMEM;
 
 	ovs_lock();
+restart:
 	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
 	err = -ENODEV;
 	if (!dp)
@@ -1931,8 +1934,11 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
 	vport = new_vport(&parms);
 	err = PTR_ERR(vport);
-	if (IS_ERR(vport))
+	if (IS_ERR(vport)) {
+		if (err == -EAGAIN)
+			goto restart;
 		goto exit_unlock_free;
+	}
 
 	err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
 				      info->snd_seq, 0, OVS_VPORT_CMD_NEW);
@@ -2291,12 +2297,18 @@ static int __init dp_init(void)
 	if (err)
 		goto error_netns_exit;
 
+	err = ovs_netdev_init();
+	if (err)
+		goto error_unreg_notifier;
+
 	err = dp_register_genl();
 	if (err < 0)
-		goto error_unreg_notifier;
+		goto error_unreg_netdev;
 
 	return 0;
 
+error_unreg_netdev:
+	ovs_netdev_exit();
 error_unreg_notifier:
 	unregister_netdevice_notifier(&ovs_dp_device_notifier);
 error_netns_exit:
@@ -2316,6 +2328,7 @@ error:
 static void dp_cleanup(void)
 {
 	dp_unregister_genl(ARRAY_SIZE(dp_genl_families));
+	ovs_netdev_exit();
 	unregister_netdevice_notifier(&ovs_dp_device_notifier);
 	unregister_pernet_device(&ovs_net_ops);
 	rcu_barrier();
diff --git a/datapath/linux/Kbuild.in b/datapath/linux/Kbuild.in
index cb98c11..9e3259f 100644
--- a/datapath/linux/Kbuild.in
+++ b/datapath/linux/Kbuild.in
@@ -18,10 +18,10 @@ ccflags-y += -include $(builddir)/kcompat.h
 # right place, even though it's conceptually incorrect.
 NOSTDINC_FLAGS += -I$(top_srcdir)/include -I$(srcdir)/compat -I$(srcdir)/compat/include
 
-obj-m := $(patsubst %,%.o,$(build_modules))
+obj-m := $(subst _,-,$(patsubst %,%.o,$(build_modules)))
 
 define module_template
 $(1)-y = $$(notdir $$(patsubst %.c,%.o,$($(1)_sources)))
 endef
 
-$(foreach module,$(build_modules),$(eval $(call module_template,$(module))))
+$(foreach module,$(build_multi_modules),$(eval $(call module_template,$(module))))
diff --git a/datapath/linux/compat/flow_dissector.c b/datapath/linux/compat/flow_dissector.c
index ab60daa..a68f84f 100644
--- a/datapath/linux/compat/flow_dissector.c
+++ b/datapath/linux/compat/flow_dissector.c
@@ -231,4 +231,5 @@ u32 __skb_get_hash(struct sk_buff *skb)
 #endif
 	return hash;
 }
+EXPORT_SYMBOL_GPL(__skb_get_hash);
 #endif
diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c
index 35c01bb..5fc6c67 100644
--- a/datapath/linux/compat/geneve.c
+++ b/datapath/linux/compat/geneve.c
@@ -118,6 +118,7 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
 				   tos, ttl, df, src_port, dst_port, xnet,
 				   !csum);
 }
+EXPORT_SYMBOL_GPL(geneve_xmit_skb);
 
 /* Callback from net/ipv4/udp.c to receive packets */
 static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
@@ -226,6 +227,7 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port,
 {
 	return geneve_socket_create(net, port, rcv, data, ipv6);
 }
+EXPORT_SYMBOL_GPL(geneve_sock_add);
 
 static void rcu_free_gs(struct rcu_head *rcu)
 {
@@ -239,3 +241,4 @@ void geneve_sock_release(struct geneve_sock *gs)
 	udp_tunnel_sock_release(gs->sock);
 	call_rcu(&gs->rcu, rcu_free_gs);
 }
+EXPORT_SYMBOL_GPL(geneve_sock_release);
diff --git a/datapath/linux/compat/gre.c b/datapath/linux/compat/gre.c
index 22bbf2f..06956f0 100644
--- a/datapath/linux/compat/gre.c
+++ b/datapath/linux/compat/gre.c
@@ -250,6 +250,7 @@ int gre_cisco_register(struct gre_cisco_protocol *newp)
 	return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
 		0 : -EBUSY;
 }
+EXPORT_SYMBOL_GPL(gre_cisco_register);
 
 int gre_cisco_unregister(struct gre_cisco_protocol *proto)
 {
@@ -265,6 +266,7 @@ int gre_cisco_unregister(struct gre_cisco_protocol *proto)
 	ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(gre_cisco_unregister);
 
 #endif /* !HAVE_GRE_CISCO_REGISTER */
 
@@ -297,6 +299,7 @@ struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
 
 	return ovs_iptunnel_handle_offloads(skb, gre_csum, type, fix_segment);
 }
+EXPORT_SYMBOL_GPL(gre_handle_offloads);
 
 static bool is_gre_gso(struct sk_buff *skb)
 {
@@ -334,6 +337,7 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 
 	ovs_skb_set_inner_protocol(skb, tpi->proto);
 }
+EXPORT_SYMBOL_GPL(gre_build_header);
 
 #endif /* CONFIG_NET_IPGRE_DEMUX */
 
diff --git a/datapath/linux/compat/ip_tunnels_core.c b/datapath/linux/compat/ip_tunnels_core.c
index 5e38603..f2c4ffd 100644
--- a/datapath/linux/compat/ip_tunnels_core.c
+++ b/datapath/linux/compat/ip_tunnels_core.c
@@ -80,6 +80,7 @@ int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
 		pkt_len = 0;
 	return pkt_len;
 }
+EXPORT_SYMBOL_GPL(rpl_iptunnel_xmit);
 
 struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb,
 					     bool csum_help, int gso_type_mask,
@@ -132,6 +133,7 @@ error:
 	kfree_skb(skb);
 	return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(ovs_iptunnel_handle_offloads);
 
 int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
 {
@@ -166,6 +168,7 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
 	skb->pkt_type = PACKET_HOST;
 	return 0;
 }
+EXPORT_SYMBOL_GPL(iptunnel_pull_header);
 
 #endif
 
@@ -176,3 +179,4 @@ bool skb_is_encapsulated(struct sk_buff *skb)
 	 */
 	return ovs_skb_get_inner_protocol(skb) || skb_encapsulation(skb);
 }
+EXPORT_SYMBOL_GPL(skb_is_encapsulated);
diff --git a/datapath/linux/compat/udp_tunnel.c b/datapath/linux/compat/udp_tunnel.c
index a1c4951..f640113 100644
--- a/datapath/linux/compat/udp_tunnel.c
+++ b/datapath/linux/compat/udp_tunnel.c
@@ -95,6 +95,7 @@ error:
 	*sockp = NULL;
 	return err;
 }
+EXPORT_SYMBOL_GPL(udp_sock_create);
 
 void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 			   struct udp_tunnel_sock_cfg *cfg)
@@ -114,6 +115,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 
 	udp_tunnel_encap_enable(sock);
 }
+EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
 
 void ovs_udp_gso(struct sk_buff *skb)
 {
@@ -123,6 +125,7 @@ void ovs_udp_gso(struct sk_buff *skb)
 	uh = udp_hdr(skb);
 	uh->len = htons(skb->len - udp_offset);
 }
+EXPORT_SYMBOL_GPL(ovs_udp_gso);
 
 void ovs_udp_csum_gso(struct sk_buff *skb)
 {
@@ -137,6 +140,7 @@ void ovs_udp_csum_gso(struct sk_buff *skb)
 	udp_set_csum(true, skb, iph->saddr, iph->daddr,
 		     skb->len - udp_offset);
 }
+EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
 
 int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
 			__be32 src, __be32 dst, __u8 tos, __u8 ttl,
@@ -158,6 +162,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
 	return iptunnel_xmit(skb->sk, rt, skb, src, dst, IPPROTO_UDP,
 			     tos, ttl, df, xnet);
 }
+EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
 
 void udp_tunnel_sock_release(struct socket *sock)
 {
@@ -165,5 +170,6 @@ void udp_tunnel_sock_release(struct socket *sock)
 	kernel_sock_shutdown(sock, SHUT_RDWR);
 	sk_release_kernel(sock->sk);
 }
+EXPORT_SYMBOL_GPL(udp_tunnel_sock_release);
 
 #endif /* Linux version < 3.20 */
diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
index 960ddba..8af3433 100644
--- a/datapath/linux/compat/vxlan.c
+++ b/datapath/linux/compat/vxlan.c
@@ -225,6 +225,7 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
 				   ttl, df, src_port, dst_port, xnet,
 				   !udp_sum);
 }
+EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
 
 static void rcu_free_vs(struct rcu_head *rcu)
 {
@@ -313,6 +314,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
 {
 	return vxlan_socket_create(net, port, rcv, data, flags);
 }
+EXPORT_SYMBOL_GPL(vxlan_sock_add);
 
 void vxlan_sock_release(struct vxlan_sock *vs)
 {
@@ -320,5 +322,6 @@ void vxlan_sock_release(struct vxlan_sock *vs)
 
 	queue_work(system_wq, &vs->del_work);
 }
+EXPORT_SYMBOL_GPL(vxlan_sock_release);
 
 #endif /* !USE_UPSTREAM_VXLAN */
diff --git a/datapath/vport-geneve.c b/datapath/vport-geneve.c
index 624a6a0..2d7a6b3 100644
--- a/datapath/vport-geneve.c
+++ b/datapath/vport-geneve.c
@@ -27,6 +27,8 @@
 #include "datapath.h"
 #include "vport.h"
 
+static struct vport_ops ovs_geneve_vport_ops;
+
 /**
  * struct geneve_port - Keeps track of open UDP ports
  * @gs: The socket created for this port number.
@@ -248,7 +250,7 @@ static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
 					  IPPROTO_UDP, skb->mark, sport, dport);
 }
 
-const struct vport_ops ovs_geneve_vport_ops = {
+static struct vport_ops ovs_geneve_vport_ops = {
 	.type			= OVS_VPORT_TYPE_GENEVE,
 	.create			= geneve_tnl_create,
 	.destroy		= geneve_tnl_destroy,
@@ -256,4 +258,22 @@ const struct vport_ops ovs_geneve_vport_ops = {
 	.get_options		= geneve_get_options,
 	.send			= geneve_tnl_send,
 	.get_egress_tun_info	= geneve_get_egress_tun_info,
+	.owner			= THIS_MODULE,
 };
+
+static int __init ovs_geneve_tnl_init(void)
+{
+	return ovs_vport_ops_register(&ovs_geneve_vport_ops);
+}
+
+static void __exit ovs_geneve_tnl_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_geneve_vport_ops);
+}
+
+module_init(ovs_geneve_tnl_init);
+module_exit(ovs_geneve_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: Geneve swiching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-5");
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index c0ed009..7bbcf57 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -31,6 +31,7 @@
 #include <linux/jhash.h>
 #include <linux/list.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/workqueue.h>
 #include <linux/rculist.h>
 #include <net/net_namespace.h>
@@ -47,6 +48,9 @@
 #include "datapath.h"
 #include "vport.h"
 
+static struct vport_ops ovs_gre_vport_ops;
+static struct vport_ops ovs_gre64_vport_ops;
+
 /* Returns the least-significant 32 bits of a __be64. */
 static __be32 be64_get_low32(__be64 x)
 {
@@ -308,13 +312,14 @@ static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
 					  IPPROTO_GRE, skb->mark, 0, 0);
 }
 
-const struct vport_ops ovs_gre_vport_ops = {
+static struct vport_ops ovs_gre_vport_ops = {
 	.type			= OVS_VPORT_TYPE_GRE,
 	.create			= gre_create,
 	.destroy		= gre_tnl_destroy,
 	.get_name		= gre_get_name,
 	.send			= gre_send,
 	.get_egress_tun_info	= gre_get_egress_tun_info,
+	.owner			= THIS_MODULE,
 };
 
 /* GRE64 vport. */
@@ -387,12 +392,42 @@ static int gre64_send(struct vport *vport, struct sk_buff *skb)
 	return __send(vport, skb, hlen, seq, (TUNNEL_KEY|TUNNEL_SEQ));
 }
 
-const struct vport_ops ovs_gre64_vport_ops = {
+static struct vport_ops ovs_gre64_vport_ops = {
 	.type			= OVS_VPORT_TYPE_GRE64,
 	.create			= gre64_create,
 	.destroy		= gre64_tnl_destroy,
 	.get_name		= gre_get_name,
 	.send			= gre64_send,
 	.get_egress_tun_info	= gre_get_egress_tun_info,
+	.owner			= THIS_MODULE,
 };
+
+static int __init ovs_gre_tnl_init(void)
+{
+	int err;
+
+	err = ovs_vport_ops_register(&ovs_gre_vport_ops);
+	if (err < 0)
+		return err;
+
+	err = ovs_vport_ops_register(&ovs_gre64_vport_ops);
+	if (err < 0)
+		ovs_vport_ops_unregister(&ovs_gre_vport_ops);
+
+	return err;
+}
+
+static void __exit ovs_gre_tnl_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_gre64_vport_ops);
+	ovs_vport_ops_unregister(&ovs_gre_vport_ops);
+}
+
+module_init(ovs_gre_tnl_init);
+module_exit(ovs_gre_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: GRE switching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-3");
+MODULE_ALIAS("vport-type-104");
 #endif
diff --git a/datapath/vport-internal_dev.c b/datapath/vport-internal_dev.c
index 040b371..f38f9be 100644
--- a/datapath/vport-internal_dev.c
+++ b/datapath/vport-internal_dev.c
@@ -38,6 +38,8 @@ struct internal_dev {
 	struct vport *vport;
 };
 
+static struct vport_ops ovs_internal_vport_ops;
+
 static struct internal_dev *internal_dev_priv(struct net_device *netdev)
 {
 	return netdev_priv(netdev);
@@ -285,7 +287,7 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
 	return len;
 }
 
-const struct vport_ops ovs_internal_vport_ops = {
+static struct vport_ops ovs_internal_vport_ops = {
 	.type		= OVS_VPORT_TYPE_INTERNAL,
 	.create		= internal_dev_create,
 	.destroy	= internal_dev_destroy,
@@ -308,10 +310,21 @@ struct vport *ovs_internal_dev_get_vport(struct net_device *netdev)
 
 int ovs_internal_dev_rtnl_link_register(void)
 {
-	return rtnl_link_register(&internal_dev_link_ops);
+	int err;
+
+	err = rtnl_link_register(&internal_dev_link_ops);
+	if (err < 0)
+		return err;
+
+	err = ovs_vport_ops_register(&ovs_internal_vport_ops);
+	if (err < 0)
+		rtnl_link_unregister(&internal_dev_link_ops);
+
+	return err;
 }
 
 void ovs_internal_dev_rtnl_link_unregister(void)
 {
+	ovs_vport_ops_unregister(&ovs_internal_vport_ops);
 	rtnl_link_unregister(&internal_dev_link_ops);
 }
diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c
index 293002f..0024eb4 100644
--- a/datapath/vport-lisp.c
+++ b/datapath/vport-lisp.c
@@ -24,6 +24,7 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/net.h>
+#include <linux/module.h>
 #include <linux/rculist.h>
 #include <linux/udp.h>
 
@@ -110,6 +111,7 @@ struct lisp_port {
 };
 
 static LIST_HEAD(lisp_ports);
+static struct vport_ops ovs_lisp_vport_ops;
 
 static inline struct lisp_port *lisp_vport(const struct vport *vport)
 {
@@ -493,7 +495,7 @@ static int lisp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
 					  lisp_port->dst_port);
 }
 
-const struct vport_ops ovs_lisp_vport_ops = {
+static struct vport_ops ovs_lisp_vport_ops = {
 	.type			= OVS_VPORT_TYPE_LISP,
 	.create			= lisp_tnl_create,
 	.destroy		= lisp_tnl_destroy,
@@ -501,4 +503,22 @@ const struct vport_ops ovs_lisp_vport_ops = {
 	.get_options		= lisp_get_options,
 	.send			= lisp_send,
 	.get_egress_tun_info	= lisp_get_egress_tun_info,
+	.owner			= THIS_MODULE,
 };
+
+static int __init ovs_lisp_tnl_init(void)
+{
+	return ovs_vport_ops_register(&ovs_lisp_vport_ops);
+}
+
+static void __exit ovs_lisp_tnl_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_lisp_vport_ops);
+}
+
+module_init(ovs_lisp_tnl_init);
+module_exit(ovs_lisp_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: LISP switching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-105");
diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c
index 9c0908a..05ad0b4 100644
--- a/datapath/vport-netdev.c
+++ b/datapath/vport-netdev.c
@@ -35,6 +35,7 @@
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
+static struct vport_ops ovs_netdev_vport_ops;
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
@@ -274,7 +275,7 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev)
 #endif
 }
 
-const struct vport_ops ovs_netdev_vport_ops = {
+static struct vport_ops ovs_netdev_vport_ops = {
 	.type		= OVS_VPORT_TYPE_NETDEV,
 	.create		= netdev_create,
 	.destroy	= netdev_destroy,
@@ -282,6 +283,16 @@ const struct vport_ops ovs_netdev_vport_ops = {
 	.send		= netdev_send,
 };
 
+int __init ovs_netdev_init(void)
+{
+	return ovs_vport_ops_register(&ovs_netdev_vport_ops);
+}
+
+void ovs_netdev_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_netdev_vport_ops);
+}
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && \
     !defined HAVE_RHEL_OVS_HOOK
 /*
diff --git a/datapath/vport-netdev.h b/datapath/vport-netdev.h
index 8df01c1..6f7038e 100644
--- a/datapath/vport-netdev.h
+++ b/datapath/vport-netdev.h
@@ -41,4 +41,7 @@ netdev_vport_priv(const struct vport *vport)
 const char *ovs_netdev_get_name(const struct vport *);
 void ovs_netdev_detach_dev(struct vport *);
 
+int __init ovs_netdev_init(void);
+void ovs_netdev_exit(void);
+
 #endif /* vport_netdev.h */
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index c25cc58..eff7ca2 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -26,6 +26,7 @@
 #include <linux/net.h>
 #include <linux/rculist.h>
 #include <linux/udp.h>
+#include <linux/module.h>
 
 #include <net/icmp.h>
 #include <net/ip.h>
@@ -54,6 +55,8 @@ struct vxlan_port {
 	u32 exts; /* VXLAN_F_* in <net/vxlan.h> */
 };
 
+static struct vport_ops ovs_vxlan_vport_ops;
+
 static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
 {
 	return vport_priv(vport);
@@ -293,7 +296,7 @@ static const char *vxlan_get_name(const struct vport *vport)
 	return vxlan_port->name;
 }
 
-const struct vport_ops ovs_vxlan_vport_ops = {
+static struct vport_ops ovs_vxlan_vport_ops = {
 	.type			= OVS_VPORT_TYPE_VXLAN,
 	.create			= vxlan_tnl_create,
 	.destroy		= vxlan_tnl_destroy,
@@ -301,4 +304,22 @@ const struct vport_ops ovs_vxlan_vport_ops = {
 	.get_options		= vxlan_get_options,
 	.send			= vxlan_tnl_send,
 	.get_egress_tun_info	= vxlan_get_egress_tun_info,
+	.owner			= THIS_MODULE,
 };
+
+static int __init ovs_vxlan_tnl_init(void)
+{
+	return ovs_vport_ops_register(&ovs_vxlan_vport_ops);
+}
+
+static void __exit ovs_vxlan_tnl_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_vxlan_vport_ops);
+}
+
+module_init(ovs_vxlan_tnl_init);
+module_exit(ovs_vxlan_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: VXLAN switching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-4");
diff --git a/datapath/vport.c b/datapath/vport.c
index 5a7067b..4486d06 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -23,6 +23,7 @@
 #include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
 #include <linux/rcupdate.h>
@@ -39,20 +40,7 @@
 static void ovs_vport_record_error(struct vport *,
 				   enum vport_err_type err_type);
 
-/* List of statically compiled vport implementations.  Don't forget to also
- * add yours to the list at the bottom of vport.h.
- */
-static const struct vport_ops *vport_ops_list[] = {
-	&ovs_netdev_vport_ops,
-	&ovs_internal_vport_ops,
-	&ovs_geneve_vport_ops,
-#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
-	&ovs_gre_vport_ops,
-	&ovs_gre64_vport_ops,
-#endif
-	&ovs_vxlan_vport_ops,
-	&ovs_lisp_vport_ops,
-};
+static LIST_HEAD(vport_ops_list);
 
 /* Protected by RCU read lock for reading, ovs_mutex for writing. */
 static struct hlist_head *dev_table;
@@ -89,6 +77,32 @@ static struct hlist_head *hash_bucket(const struct net *net, const char *name)
 	return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
 }
 
+int ovs_vport_ops_register(struct vport_ops *ops)
+{
+	int err = -EEXIST;
+	struct vport_ops *o;
+
+	ovs_lock();
+	list_for_each_entry(o, &vport_ops_list, list)
+	if (ops->type == o->type)
+		goto errout;
+
+	list_add_tail(&ops->list, &vport_ops_list);
+	err = 0;
+errout:
+	ovs_unlock();
+	return err;
+}
+EXPORT_SYMBOL_GPL(ovs_vport_ops_register);
+
+void ovs_vport_ops_unregister(struct vport_ops *ops)
+{
+	ovs_lock();
+	list_del(&ops->list);
+	ovs_unlock();
+}
+EXPORT_SYMBOL_GPL(ovs_vport_ops_unregister);
+
 /**
  *	ovs_vport_locate - find a port that has already been created
  *
@@ -154,6 +168,18 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 
 	return vport;
 }
+EXPORT_SYMBOL_GPL(ovs_vport_alloc);
+
+static struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms)
+{
+	struct vport_ops *ops;
+
+	list_for_each_entry(ops, &vport_ops_list, list)
+		if (ops->type == parms->type)
+			return ops;
+
+	return NULL;
+}
 
 /**
  *	ovs_vport_free - uninitialize and free vport
@@ -171,6 +197,7 @@ void ovs_vport_free(struct vport *vport)
 	free_percpu(vport->percpu_stats);
 	kfree(vport);
 }
+EXPORT_SYMBOL_GPL(ovs_vport_free);
 
 /**
  *	ovs_vport_add - add vport device (for kernel callers)
@@ -182,31 +209,40 @@ void ovs_vport_free(struct vport *vport)
  */
 struct vport *ovs_vport_add(const struct vport_parms *parms)
 {
+	struct vport_ops *ops;
 	struct vport *vport;
-	int err = 0;
-	int i;
 
-	for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) {
-		if (vport_ops_list[i]->type == parms->type) {
-			struct hlist_head *bucket;
+	ops = ovs_vport_lookup(parms);
+	if (ops) {
+		struct hlist_head *bucket;
 
-			vport = vport_ops_list[i]->create(parms);
-			if (IS_ERR(vport)) {
-				err = PTR_ERR(vport);
-				goto out;
-			}
+		if (!try_module_get(ops->owner))
+			return ERR_PTR(-EAFNOSUPPORT);
 
-			bucket = hash_bucket(ovs_dp_get_net(vport->dp),
-					     vport->ops->get_name(vport));
-			hlist_add_head_rcu(&vport->hash_node, bucket);
+		vport = ops->create(parms);
+		if (IS_ERR(vport)) {
+			module_put(ops->owner);
 			return vport;
 		}
+
+		bucket = hash_bucket(ovs_dp_get_net(vport->dp),
+				     vport->ops->get_name(vport));
+		hlist_add_head_rcu(&vport->hash_node, bucket);
+		return vport;
 	}
 
-	err = -EAFNOSUPPORT;
+	/* Unlock to attempt module load and return -EAGAIN if load
+	 * was successful as we need to restart the port addition
+	 * workflow.
+	 */
+	ovs_unlock();
+	request_module("vport-type-%d", parms->type);
+	ovs_lock();
 
-out:
-	return ERR_PTR(err);
+	if (!ovs_vport_lookup(parms))
+		return ERR_PTR(-EAFNOSUPPORT);
+	else
+		return ERR_PTR(-EAGAIN);
 }
 
 /**
@@ -238,6 +274,7 @@ void ovs_vport_del(struct vport *vport)
 	ASSERT_OVSL();
 
 	hlist_del_rcu(&vport->hash_node);
+	module_put(vport->ops->owner);
 	vport->ops->destroy(vport);
 }
 
@@ -467,6 +504,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
 
 	ovs_dp_process_packet(skb, &key);
 }
+EXPORT_SYMBOL_GPL(ovs_vport_receive);
 
 /**
  *	ovs_vport_send - send a packet on a device
@@ -544,6 +582,7 @@ void ovs_vport_deferred_free(struct vport *vport)
 
 	call_rcu(&vport->rcu, free_vport_rcu);
 }
+EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
 
 int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
 			       struct net *net,
@@ -592,6 +631,7 @@ int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
 
 int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
 				  struct ovs_tunnel_info *info)
diff --git a/datapath/vport.h b/datapath/vport.h
index e256fc0..c289d60 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -175,6 +175,9 @@ struct vport_ops {
 	int (*send)(struct vport *, struct sk_buff *);
 	int (*get_egress_tun_info)(struct vport *, struct sk_buff *,
 				   struct ovs_tunnel_info *);
+
+	struct module *owner;
+	struct list_head list;
 };
 
 enum vport_err_type {
@@ -223,16 +226,6 @@ static inline struct vport *vport_from_priv(void *priv)
 void ovs_vport_receive(struct vport *, struct sk_buff *,
 		       const struct ovs_tunnel_info *);
 
-/* List of statically compiled vport implementations.  Don't forget to also
- * add yours to the list at the top of vport.c.
- */
-extern const struct vport_ops ovs_netdev_vport_ops;
-extern const struct vport_ops ovs_internal_vport_ops;
-extern const struct vport_ops ovs_geneve_vport_ops;
-extern const struct vport_ops ovs_gre_vport_ops;
-extern const struct vport_ops ovs_gre64_vport_ops;
-extern const struct vport_ops ovs_vxlan_vport_ops;
-extern const struct vport_ops ovs_lisp_vport_ops;
 
 static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
 				      const void *start, unsigned int len)
@@ -240,4 +233,7 @@ static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
 	if (skb->ip_summed == CHECKSUM_COMPLETE)
 		skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
 }
+
+int ovs_vport_ops_register(struct vport_ops *ops);
+void ovs_vport_ops_unregister(struct vport_ops *ops);
 #endif /* vport.h */
-- 
1.9.3




More information about the dev mailing list