Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ovs: Turn vports with dependencies into separate modules

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@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Thomas Graf and committed by
David S. Miller
62b9c8d0 cf7b2003

+199 -68
+9 -9
net/openvswitch/Kconfig
··· 29 29 If unsure, say N. 30 30 31 31 config OPENVSWITCH_GRE 32 - bool "Open vSwitch GRE tunneling support" 32 + tristate "Open vSwitch GRE tunneling support" 33 33 depends on INET 34 34 depends on OPENVSWITCH 35 - depends on NET_IPGRE_DEMUX && !(OPENVSWITCH=y && NET_IPGRE_DEMUX=m) 36 - default y 35 + depends on NET_IPGRE_DEMUX 36 + default OPENVSWITCH 37 37 ---help--- 38 38 If you say Y here, then the Open vSwitch will be able create GRE 39 39 vport. ··· 43 43 If unsure, say Y. 44 44 45 45 config OPENVSWITCH_VXLAN 46 - bool "Open vSwitch VXLAN tunneling support" 46 + tristate "Open vSwitch VXLAN tunneling support" 47 47 depends on INET 48 48 depends on OPENVSWITCH 49 - depends on VXLAN && !(OPENVSWITCH=y && VXLAN=m) 50 - default y 49 + depends on VXLAN 50 + default OPENVSWITCH 51 51 ---help--- 52 52 If you say Y here, then the Open vSwitch will be able create vxlan vport. 53 53 ··· 56 56 If unsure, say Y. 57 57 58 58 config OPENVSWITCH_GENEVE 59 - bool "Open vSwitch Geneve tunneling support" 59 + tristate "Open vSwitch Geneve tunneling support" 60 60 depends on INET 61 61 depends on OPENVSWITCH 62 - depends on GENEVE && !(OPENVSWITCH=y && GENEVE=m) 63 - default y 62 + depends on GENEVE 63 + default OPENVSWITCH 64 64 ---help--- 65 65 If you say Y here, then the Open vSwitch will be able create geneve vport. 66 66
+3 -11
net/openvswitch/Makefile
··· 15 15 vport-internal_dev.o \ 16 16 vport-netdev.o 17 17 18 - ifneq ($(CONFIG_OPENVSWITCH_GENEVE),) 19 - openvswitch-y += vport-geneve.o 20 - endif 21 - 22 - ifneq ($(CONFIG_OPENVSWITCH_VXLAN),) 23 - openvswitch-y += vport-vxlan.o 24 - endif 25 - 26 - ifneq ($(CONFIG_OPENVSWITCH_GRE),) 27 - openvswitch-y += vport-gre.o 28 - endif 18 + obj-$(CONFIG_OPENVSWITCH_GENEVE)+= vport-geneve.o 19 + obj-$(CONFIG_OPENVSWITCH_VXLAN) += vport-vxlan.o 20 + obj-$(CONFIG_OPENVSWITCH_GRE) += vport-gre.o
+14 -2
net/openvswitch/datapath.c
··· 59 59 #include "vport-netdev.h" 60 60 61 61 int ovs_net_id __read_mostly; 62 + EXPORT_SYMBOL(ovs_net_id); 62 63 63 64 static struct genl_family dp_packet_genl_family; 64 65 static struct genl_family dp_flow_genl_family; ··· 1765 1764 return -ENOMEM; 1766 1765 1767 1766 ovs_lock(); 1767 + restart: 1768 1768 dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 1769 1769 err = -ENODEV; 1770 1770 if (!dp) ··· 1797 1795 1798 1796 vport = new_vport(&parms); 1799 1797 err = PTR_ERR(vport); 1800 - if (IS_ERR(vport)) 1798 + if (IS_ERR(vport)) { 1799 + if (err == -EAGAIN) 1800 + goto restart; 1801 1801 goto exit_unlock_free; 1802 + } 1802 1803 1803 1804 err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, 1804 1805 info->snd_seq, 0, OVS_VPORT_CMD_NEW); ··· 2117 2112 if (err) 2118 2113 goto error_netns_exit; 2119 2114 2115 + err = ovs_netdev_init(); 2116 + if (err) 2117 + goto error_unreg_notifier; 2118 + 2120 2119 err = dp_register_genl(); 2121 2120 if (err < 0) 2122 - goto error_unreg_notifier; 2121 + goto error_unreg_netdev; 2123 2122 2124 2123 return 0; 2125 2124 2125 + error_unreg_netdev: 2126 + ovs_netdev_exit(); 2126 2127 error_unreg_notifier: 2127 2128 unregister_netdevice_notifier(&ovs_dp_device_notifier); 2128 2129 error_netns_exit: ··· 2148 2137 static void dp_cleanup(void) 2149 2138 { 2150 2139 dp_unregister_genl(ARRAY_SIZE(dp_genl_families)); 2140 + ovs_netdev_exit(); 2151 2141 unregister_netdevice_notifier(&ovs_dp_device_notifier); 2152 2142 unregister_pernet_device(&ovs_net_ops); 2153 2143 rcu_barrier();
+22 -1
net/openvswitch/vport-geneve.c
··· 17 17 #include <linux/rculist.h> 18 18 #include <linux/udp.h> 19 19 #include <linux/if_vlan.h> 20 + #include <linux/module.h> 20 21 21 22 #include <net/geneve.h> 22 23 #include <net/icmp.h> ··· 28 27 29 28 #include "datapath.h" 30 29 #include "vport.h" 30 + 31 + static struct vport_ops ovs_geneve_vport_ops; 31 32 32 33 /** 33 34 * struct geneve_port - Keeps track of open UDP ports ··· 228 225 return geneve_port->name; 229 226 } 230 227 231 - const struct vport_ops ovs_geneve_vport_ops = { 228 + static struct vport_ops ovs_geneve_vport_ops = { 232 229 .type = OVS_VPORT_TYPE_GENEVE, 233 230 .create = geneve_tnl_create, 234 231 .destroy = geneve_tnl_destroy, 235 232 .get_name = geneve_get_name, 236 233 .get_options = geneve_get_options, 237 234 .send = geneve_tnl_send, 235 + .owner = THIS_MODULE, 238 236 }; 237 + 238 + static int __init ovs_geneve_tnl_init(void) 239 + { 240 + return ovs_vport_ops_register(&ovs_geneve_vport_ops); 241 + } 242 + 243 + static void __exit ovs_geneve_tnl_exit(void) 244 + { 245 + ovs_vport_ops_unregister(&ovs_geneve_vport_ops); 246 + } 247 + 248 + module_init(ovs_geneve_tnl_init); 249 + module_exit(ovs_geneve_tnl_exit); 250 + 251 + MODULE_DESCRIPTION("OVS: Geneve swiching port"); 252 + MODULE_LICENSE("GPL"); 253 + MODULE_ALIAS("vport-type-5");
+22 -1
net/openvswitch/vport-gre.c
··· 29 29 #include <linux/jhash.h> 30 30 #include <linux/list.h> 31 31 #include <linux/kernel.h> 32 + #include <linux/module.h> 32 33 #include <linux/workqueue.h> 33 34 #include <linux/rculist.h> 34 35 #include <net/route.h> ··· 45 44 46 45 #include "datapath.h" 47 46 #include "vport.h" 47 + 48 + static struct vport_ops ovs_gre_vport_ops; 48 49 49 50 /* Returns the least-significant 32 bits of a __be64. */ 50 51 static __be32 be64_get_low32(__be64 x) ··· 284 281 gre_exit(); 285 282 } 286 283 287 - const struct vport_ops ovs_gre_vport_ops = { 284 + static struct vport_ops ovs_gre_vport_ops = { 288 285 .type = OVS_VPORT_TYPE_GRE, 289 286 .create = gre_create, 290 287 .destroy = gre_tnl_destroy, 291 288 .get_name = gre_get_name, 292 289 .send = gre_tnl_send, 290 + .owner = THIS_MODULE, 293 291 }; 292 + 293 + static int __init ovs_gre_tnl_init(void) 294 + { 295 + return ovs_vport_ops_register(&ovs_gre_vport_ops); 296 + } 297 + 298 + static void __exit ovs_gre_tnl_exit(void) 299 + { 300 + ovs_vport_ops_unregister(&ovs_gre_vport_ops); 301 + } 302 + 303 + module_init(ovs_gre_tnl_init); 304 + module_exit(ovs_gre_tnl_exit); 305 + 306 + MODULE_DESCRIPTION("OVS: GRE switching port"); 307 + MODULE_LICENSE("GPL"); 308 + MODULE_ALIAS("vport-type-3");
+15 -2
net/openvswitch/vport-internal_dev.c
··· 36 36 struct vport *vport; 37 37 }; 38 38 39 + static struct vport_ops ovs_internal_vport_ops; 40 + 39 41 static struct internal_dev *internal_dev_priv(struct net_device *netdev) 40 42 { 41 43 return netdev_priv(netdev); ··· 240 238 return len; 241 239 } 242 240 243 - const struct vport_ops ovs_internal_vport_ops = { 241 + static struct vport_ops ovs_internal_vport_ops = { 244 242 .type = OVS_VPORT_TYPE_INTERNAL, 245 243 .create = internal_dev_create, 246 244 .destroy = internal_dev_destroy, ··· 263 261 264 262 int ovs_internal_dev_rtnl_link_register(void) 265 263 { 266 - return rtnl_link_register(&internal_dev_link_ops); 264 + int err; 265 + 266 + err = rtnl_link_register(&internal_dev_link_ops); 267 + if (err < 0) 268 + return err; 269 + 270 + err = ovs_vport_ops_register(&ovs_internal_vport_ops); 271 + if (err < 0) 272 + rtnl_link_unregister(&internal_dev_link_ops); 273 + 274 + return err; 267 275 } 268 276 269 277 void ovs_internal_dev_rtnl_link_unregister(void) 270 278 { 279 + ovs_vport_ops_unregister(&ovs_internal_vport_ops); 271 280 rtnl_link_unregister(&internal_dev_link_ops); 272 281 }
+13 -1
net/openvswitch/vport-netdev.c
··· 33 33 #include "vport-internal_dev.h" 34 34 #include "vport-netdev.h" 35 35 36 + static struct vport_ops ovs_netdev_vport_ops; 37 + 36 38 /* Must be called with rcu_read_lock. */ 37 39 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) 38 40 { ··· 226 224 return NULL; 227 225 } 228 226 229 - const struct vport_ops ovs_netdev_vport_ops = { 227 + static struct vport_ops ovs_netdev_vport_ops = { 230 228 .type = OVS_VPORT_TYPE_NETDEV, 231 229 .create = netdev_create, 232 230 .destroy = netdev_destroy, 233 231 .get_name = ovs_netdev_get_name, 234 232 .send = netdev_send, 235 233 }; 234 + 235 + int __init ovs_netdev_init(void) 236 + { 237 + return ovs_vport_ops_register(&ovs_netdev_vport_ops); 238 + } 239 + 240 + void ovs_netdev_exit(void) 241 + { 242 + ovs_vport_ops_unregister(&ovs_netdev_vport_ops); 243 + }
+3
net/openvswitch/vport-netdev.h
··· 41 41 const char *ovs_netdev_get_name(const struct vport *); 42 42 void ovs_netdev_detach_dev(struct vport *); 43 43 44 + int __init ovs_netdev_init(void); 45 + void ovs_netdev_exit(void); 46 + 44 47 #endif /* vport_netdev.h */
+22 -1
net/openvswitch/vport-vxlan.c
··· 24 24 #include <linux/net.h> 25 25 #include <linux/rculist.h> 26 26 #include <linux/udp.h> 27 + #include <linux/module.h> 27 28 28 29 #include <net/icmp.h> 29 30 #include <net/ip.h> ··· 50 49 struct vxlan_sock *vs; 51 50 char name[IFNAMSIZ]; 52 51 }; 52 + 53 + static struct vport_ops ovs_vxlan_vport_ops; 53 54 54 55 static inline struct vxlan_port *vxlan_vport(const struct vport *vport) 55 56 { ··· 195 192 return vxlan_port->name; 196 193 } 197 194 198 - const struct vport_ops ovs_vxlan_vport_ops = { 195 + static struct vport_ops ovs_vxlan_vport_ops = { 199 196 .type = OVS_VPORT_TYPE_VXLAN, 200 197 .create = vxlan_tnl_create, 201 198 .destroy = vxlan_tnl_destroy, 202 199 .get_name = vxlan_get_name, 203 200 .get_options = vxlan_get_options, 204 201 .send = vxlan_tnl_send, 202 + .owner = THIS_MODULE, 205 203 }; 204 + 205 + static int __init ovs_vxlan_tnl_init(void) 206 + { 207 + return ovs_vport_ops_register(&ovs_vxlan_vport_ops); 208 + } 209 + 210 + static void __exit ovs_vxlan_tnl_exit(void) 211 + { 212 + ovs_vport_ops_unregister(&ovs_vxlan_vport_ops); 213 + } 214 + 215 + module_init(ovs_vxlan_tnl_init); 216 + module_exit(ovs_vxlan_tnl_exit); 217 + 218 + MODULE_DESCRIPTION("OVS: VXLAN switching port"); 219 + MODULE_LICENSE("GPL"); 220 + MODULE_ALIAS("vport-type-4");
+70 -32
net/openvswitch/vport.c
··· 28 28 #include <linux/rtnetlink.h> 29 29 #include <linux/compat.h> 30 30 #include <net/net_namespace.h> 31 + #include <linux/module.h> 31 32 32 33 #include "datapath.h" 33 34 #include "vport.h" ··· 37 36 static void ovs_vport_record_error(struct vport *, 38 37 enum vport_err_type err_type); 39 38 40 - /* List of statically compiled vport implementations. Don't forget to also 41 - * add yours to the list at the bottom of vport.h. */ 42 - static const struct vport_ops *vport_ops_list[] = { 43 - &ovs_netdev_vport_ops, 44 - &ovs_internal_vport_ops, 45 - 46 - #ifdef CONFIG_OPENVSWITCH_GRE 47 - &ovs_gre_vport_ops, 48 - #endif 49 - #ifdef CONFIG_OPENVSWITCH_VXLAN 50 - &ovs_vxlan_vport_ops, 51 - #endif 52 - #ifdef CONFIG_OPENVSWITCH_GENEVE 53 - &ovs_geneve_vport_ops, 54 - #endif 55 - }; 39 + static LIST_HEAD(vport_ops_list); 56 40 57 41 /* Protected by RCU read lock for reading, ovs_mutex for writing. */ 58 42 static struct hlist_head *dev_table; ··· 73 87 unsigned int hash = jhash(name, strlen(name), (unsigned long) net); 74 88 return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)]; 75 89 } 90 + 91 + int ovs_vport_ops_register(struct vport_ops *ops) 92 + { 93 + int err = -EEXIST; 94 + struct vport_ops *o; 95 + 96 + ovs_lock(); 97 + list_for_each_entry(o, &vport_ops_list, list) 98 + if (ops->type == o->type) 99 + goto errout; 100 + 101 + list_add_tail(&ops->list, &vport_ops_list); 102 + err = 0; 103 + errout: 104 + ovs_unlock(); 105 + return err; 106 + } 107 + EXPORT_SYMBOL(ovs_vport_ops_register); 108 + 109 + void ovs_vport_ops_unregister(struct vport_ops *ops) 110 + { 111 + ovs_lock(); 112 + list_del(&ops->list); 113 + ovs_unlock(); 114 + } 115 + EXPORT_SYMBOL(ovs_vport_ops_unregister); 76 116 77 117 /** 78 118 * ovs_vport_locate - find a port that has already been created ··· 165 153 166 154 return vport; 167 155 } 156 + EXPORT_SYMBOL(ovs_vport_alloc); 168 157 169 158 /** 170 159 * ovs_vport_free - uninitialize and free vport ··· 186 173 free_percpu(vport->percpu_stats); 187 174 kfree(vport); 188 175 } 176 + EXPORT_SYMBOL(ovs_vport_free); 177 + 178 + static struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms) 179 + { 180 + struct vport_ops *ops; 181 + 182 + list_for_each_entry(ops, &vport_ops_list, list) 183 + if (ops->type == parms->type) 184 + return ops; 185 + 186 + return NULL; 187 + } 189 188 190 189 /** 191 190 * ovs_vport_add - add vport device (for kernel callers) ··· 209 184 */ 210 185 struct vport *ovs_vport_add(const struct vport_parms *parms) 211 186 { 187 + struct vport_ops *ops; 212 188 struct vport *vport; 213 - int err = 0; 214 - int i; 215 189 216 - for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) { 217 - if (vport_ops_list[i]->type == parms->type) { 218 - struct hlist_head *bucket; 190 + ops = ovs_vport_lookup(parms); 191 + if (ops) { 192 + struct hlist_head *bucket; 219 193 220 - vport = vport_ops_list[i]->create(parms); 221 - if (IS_ERR(vport)) { 222 - err = PTR_ERR(vport); 223 - goto out; 224 - } 194 + if (!try_module_get(ops->owner)) 195 + return ERR_PTR(-EAFNOSUPPORT); 225 196 226 - bucket = hash_bucket(ovs_dp_get_net(vport->dp), 227 - vport->ops->get_name(vport)); 228 - hlist_add_head_rcu(&vport->hash_node, bucket); 197 + vport = ops->create(parms); 198 + if (IS_ERR(vport)) { 199 + module_put(ops->owner); 229 200 return vport; 230 201 } 202 + 203 + bucket = hash_bucket(ovs_dp_get_net(vport->dp), 204 + vport->ops->get_name(vport)); 205 + hlist_add_head_rcu(&vport->hash_node, bucket); 206 + return vport; 231 207 } 232 208 233 - err = -EAFNOSUPPORT; 209 + /* Unlock to attempt module load and return -EAGAIN if load 210 + * was successful as we need to restart the port addition 211 + * workflow. 212 + */ 213 + ovs_unlock(); 214 + request_module("vport-type-%d", parms->type); 215 + ovs_lock(); 234 216 235 - out: 236 - return ERR_PTR(err); 217 + if (!ovs_vport_lookup(parms)) 218 + return ERR_PTR(-EAFNOSUPPORT); 219 + else 220 + return ERR_PTR(-EAGAIN); 237 221 } 238 222 239 223 /** ··· 276 242 hlist_del_rcu(&vport->hash_node); 277 243 278 244 vport->ops->destroy(vport); 245 + 246 + module_put(vport->ops->owner); 279 247 } 280 248 281 249 /** ··· 493 457 } 494 458 ovs_dp_process_packet(skb, &key); 495 459 } 460 + EXPORT_SYMBOL(ovs_vport_receive); 496 461 497 462 /** 498 463 * ovs_vport_send - send a packet on a device ··· 572 535 573 536 call_rcu(&vport->rcu, free_vport_rcu); 574 537 } 538 + EXPORT_SYMBOL(ovs_vport_deferred_free);
+6 -8
net/openvswitch/vport.h
··· 161 161 const char *(*get_name)(const struct vport *); 162 162 163 163 int (*send)(struct vport *, struct sk_buff *); 164 + 165 + struct module *owner; 166 + struct list_head list; 164 167 }; 165 168 166 169 enum vport_err_type { ··· 212 209 void ovs_vport_receive(struct vport *, struct sk_buff *, 213 210 struct ovs_tunnel_info *); 214 211 215 - /* List of statically compiled vport implementations. Don't forget to also 216 - * add yours to the list at the top of vport.c. */ 217 - extern const struct vport_ops ovs_netdev_vport_ops; 218 - extern const struct vport_ops ovs_internal_vport_ops; 219 - extern const struct vport_ops ovs_gre_vport_ops; 220 - extern const struct vport_ops ovs_vxlan_vport_ops; 221 - extern const struct vport_ops ovs_geneve_vport_ops; 222 - 223 212 static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, 224 213 const void *start, unsigned int len) 225 214 { 226 215 if (skb->ip_summed == CHECKSUM_COMPLETE) 227 216 skb->csum = csum_add(skb->csum, csum_partial(start, len, 0)); 228 217 } 218 + 219 + int ovs_vport_ops_register(struct vport_ops *ops); 220 + void ovs_vport_ops_unregister(struct vport_ops *ops); 229 221 230 222 #endif /* vport.h */