Signed-off-by: Jiri Pirko <j...@resnulli.us> --- include/uapi/linux/openvswitch.h | 4 + net/openvswitch/Makefile | 4 + net/openvswitch/datapath.c | 45 ++++++- net/openvswitch/datapath.h | 8 ++ net/openvswitch/dp_notify.c | 3 +- net/openvswitch/vport-internal_switchdev.c | 179 +++++++++++++++++++++++++ net/openvswitch/vport-internal_switchdev.h | 28 ++++ net/openvswitch/vport-netdev.c | 4 +- net/openvswitch/vport-switchportdev.c | 205 +++++++++++++++++++++++++++++ net/openvswitch/vport-switchportdev.h | 24 ++++ net/openvswitch/vport.c | 4 + net/openvswitch/vport.h | 2 + 12 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 net/openvswitch/vport-internal_switchdev.c create mode 100644 net/openvswitch/vport-internal_switchdev.h create mode 100644 net/openvswitch/vport-switchportdev.c create mode 100644 net/openvswitch/vport-switchportdev.h
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 970553c..8df1a49 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -189,6 +189,10 @@ enum ovs_vport_type { OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */ OVS_VPORT_TYPE_GRE, /* GRE tunnel. */ OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */ + OVS_VPORT_TYPE_INTERNAL_SWITCHDEV, /* network device which represents + a hardware switch */ + OVS_VPORT_TYPE_SWITCHPORTDEV, /* network device which represents + a port of a hardware switch */ __OVS_VPORT_TYPE_MAX }; diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile index 3591cb5..6e9fb2a 100644 --- a/net/openvswitch/Makefile +++ b/net/openvswitch/Makefile @@ -22,3 +22,7 @@ endif ifneq ($(CONFIG_OPENVSWITCH_GRE),) openvswitch-y += vport-gre.o endif + +ifneq ($(CONFIG_NET_SWITCHDEV),) +openvswitch-y += vport-internal_switchdev.o vport-switchportdev.o +endif diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f229ab6..6056325 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -58,7 +58,9 @@ #include "flow_table.h" #include "flow_netlink.h" #include "vport-internal_dev.h" +#include "vport-internal_switchdev.h" #include "vport-netdev.h" +#include "vport-switchportdev.h" int ovs_net_id __read_mostly; @@ -124,6 +126,9 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) dev = dev_get_by_index_rcu(net, dp_ifindex); if (dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); + + if (!vport) + vport = ovs_internal_swdev_get_vport(dev); if (vport) dp = vport->dp; } @@ -768,6 +773,19 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct ovs_flow *flow, return skb; } +static int ovs_dp_flow_insert(struct datapath *dp, struct sw_flow *flow) +{ + if (dp->ops && dp->ops->flow_insert) + return dp->ops->flow_insert(dp, flow); + return 0; +} + +static void ovs_dp_flow_remove(struct datapath *dp, struct sw_flow *flow) +{ + if (dp->ops && dp->ops->flow_remove) + dp->ops->flow_remove(dp, flow); +} + static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; @@ -836,13 +854,15 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) flow->flow.key = masked_key; flow->flow.unmasked_key = key; rcu_assign_pointer(flow->sf_acts, acts); + acts = NULL; /* Put flow in bucket. */ error = ovs_flow_tbl_insert(&dp->table, flow, &mask); - if (error) { - acts = NULL; + if (error) goto err_flow_free; - } + error = ovs_dp_flow_insert(dp, &flow->flow); + if (error) + goto err_flow_tbl_remove; reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); } else { @@ -884,6 +904,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) 0, PTR_ERR(reply)); return 0; +err_flow_tbl_remove: + ovs_flow_tbl_remove(&dp->table, flow); err_flow_free: ovs_flow_free(flow, false); err_unlock_ovs: @@ -981,6 +1003,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) goto unlock; } + ovs_dp_flow_remove(dp, &flow->flow); ovs_flow_tbl_remove(&dp->table, flow); err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, @@ -1234,7 +1257,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) /* Set up our datapath device. */ parms.name = nla_data(a[OVS_DP_ATTR_NAME]); - parms.type = OVS_VPORT_TYPE_INTERNAL; + if (ovs_is_suitable_for_internal_swdev(sock_net(skb->sk), parms.name)) + parms.type = OVS_VPORT_TYPE_INTERNAL_SWITCHDEV; + else + parms.type = OVS_VPORT_TYPE_INTERNAL; parms.options = NULL; parms.dp = dp; parms.port_no = OVSP_LOCAL; @@ -1572,6 +1598,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct vport *vport; struct datapath *dp; + struct vport *local_vport; u32 port_no; int err; @@ -1611,6 +1638,16 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) parms.name = nla_data(a[OVS_VPORT_ATTR_NAME]); parms.type = nla_get_u32(a[OVS_VPORT_ATTR_TYPE]); + + if (parms.type == OVS_VPORT_TYPE_NETDEV && + ovs_is_suitable_for_switchportdev(sock_net(skb->sk), parms.name)) + parms.type = OVS_VPORT_TYPE_SWITCHPORTDEV; + + local_vport = ovs_vport_ovsl(dp, OVSP_LOCAL); + if (local_vport->ops->type == OVS_VPORT_TYPE_INTERNAL_SWITCHDEV && + parms.type != OVS_VPORT_TYPE_SWITCHPORTDEV) + return -EOPNOTSUPP; + parms.options = a[OVS_VPORT_ATTR_OPTIONS]; parms.dp = dp; parms.port_no = port_no; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 5388cac..584999b 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -58,6 +58,8 @@ struct dp_stats_percpu { struct u64_stats_sync syncp; }; +struct dp_ops; + /** * struct datapath - datapath for flow-based packet switching * @rcu: RCU callback head for deferred destruction. @@ -90,6 +92,12 @@ struct datapath { #endif u32 user_features; + const struct dp_ops *ops; +}; + +struct dp_ops { + int (*flow_insert)(struct datapath *dp, struct sw_flow *flow); + void (*flow_remove)(struct datapath *dp, struct sw_flow *flow); }; /** diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c index 2c631fe..7f9b6ae 100644 --- a/net/openvswitch/dp_notify.c +++ b/net/openvswitch/dp_notify.c @@ -22,6 +22,7 @@ #include "datapath.h" #include "vport-internal_dev.h" +#include "vport-internal_switchdev.h" #include "vport-netdev.h" static void dp_detach_port_notify(struct vport *vport) @@ -79,7 +80,7 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vport *vport = NULL; - if (!ovs_is_internal_dev(dev)) + if (!ovs_is_internal_dev(dev) && !ovs_is_internal_swdev(dev)) vport = ovs_netdev_get_vport(dev); if (!vport) diff --git a/net/openvswitch/vport-internal_switchdev.c b/net/openvswitch/vport-internal_switchdev.c new file mode 100644 index 0000000..e11547f --- /dev/null +++ b/net/openvswitch/vport-internal_switchdev.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <linux/kernel.h> +#include <linux/rcupdate.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/switchdev.h> + +#include <net/net_namespace.h> +#include <net/netns/generic.h> + +#include "datapath.h" +#include "vport-netdev.h" +#include "vport-internal_switchdev.h" + +static int internal_swdev_flow_insert(struct datapath *dp, struct sw_flow *flow) +{ + struct vport *vport; + struct netdev_vport *netdev_vport; + + vport = ovs_vport_ovsl(dp, OVSP_LOCAL); + netdev_vport = netdev_vport_priv(vport); + return swdev_flow_insert(netdev_vport->dev, flow); +} + +static void internal_swdev_flow_remove(struct datapath *dp, struct sw_flow *flow) +{ + struct vport *vport; + struct netdev_vport *netdev_vport; + + vport = ovs_vport_ovsl(dp, OVSP_LOCAL); + netdev_vport = netdev_vport_priv(vport); + swdev_flow_remove(netdev_vport->dev, flow); +} + +static const struct dp_ops internal_swdev_dp_ops = { + .flow_insert = internal_swdev_flow_insert, + .flow_remove = internal_swdev_flow_remove, +}; + +static struct vport *internal_swdev_create(const struct vport_parms *parms) +{ + struct vport *vport; + struct netdev_vport *netdev_vport; + struct net_device *dev; + int err; + + vport = ovs_vport_alloc(sizeof(struct netdev_vport), + &ovs_internal_swdev_vport_ops, parms); + if (IS_ERR(vport)) + return vport; + + netdev_vport = netdev_vport_priv(vport); + + rtnl_lock(); + dev = __dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name); + if (!dev) { + err = -ENODEV; + goto error_free_vport; + } + if (!swdev_dev_check(dev)) { + err = -EINVAL; + goto error_free_vport; + } + netdev_vport->dev = dev; + vport->dp->ops = &internal_swdev_dp_ops; + dev_hold(dev); + rtnl_unlock(); + + return vport; + +error_free_vport: + rtnl_unlock(); + ovs_vport_free(vport); + return ERR_PTR(err); +} + +static void internal_swdev_free_rcu(struct rcu_head *rcu) +{ + struct netdev_vport *netdev_vport; + + netdev_vport = container_of(rcu, struct netdev_vport, rcu); + ovs_vport_free(vport_from_priv(netdev_vport)); +} + +static void internal_swdev_destroy(struct vport *vport) +{ + struct netdev_vport *netdev_vport = netdev_vport_priv(vport); + + dev_put(netdev_vport->dev); + call_rcu(&netdev_vport->rcu, internal_swdev_free_rcu); +} + +static int internal_swdev_send(struct vport *vport, struct sk_buff *skb) +{ + int len; + + len = skb->len; + consume_skb(skb); + return len; +} + +const struct vport_ops ovs_internal_swdev_vport_ops = { + .type = OVS_VPORT_TYPE_INTERNAL_SWITCHDEV, + .create = internal_swdev_create, + .destroy = internal_swdev_destroy, + .get_name = ovs_netdev_get_name, + .send = internal_swdev_send, +}; + +bool ovs_is_suitable_for_internal_swdev(struct net *net, const char *name) +{ + struct net_device *dev; + bool ret; + + rcu_read_lock(); + dev = dev_get_by_name_rcu(net, name); + ret = dev ? swdev_dev_check(dev) : false; + rcu_read_unlock(); + return ret; +} + +struct vport *ovs_internal_swdev_get_vport_by_dp(const struct datapath *dp) +{ + struct vport *vport; + + vport = ovs_vport_rcu(dp, OVSP_LOCAL); + if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL_SWITCHDEV) + return NULL; + return vport; +} + +struct vport *ovs_internal_swdev_get_vport(const struct net_device *dev) +{ + struct ovs_net *ovs_net = net_generic(dev_net(dev), ovs_net_id); + struct datapath *dp; + struct vport *vport = NULL; + + if (!swdev_dev_check(dev)) + return NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) { + vport = ovs_internal_swdev_get_vport_by_dp(dp); + if (vport && netdev_vport_priv(vport)->dev == dev) + break; + vport = NULL; + } + rcu_read_unlock(); + return vport; +} + +bool ovs_is_internal_swdev(const struct net_device *dev) +{ + return ovs_internal_swdev_get_vport(dev) ? true : false; +} + +struct net_device *ovs_internal_swdev_get_netdev(struct vport *vport) +{ + struct netdev_vport *netdev_vport = netdev_vport_priv(vport); + + return netdev_vport->dev; +} diff --git a/net/openvswitch/vport-internal_switchdev.h b/net/openvswitch/vport-internal_switchdev.h new file mode 100644 index 0000000..7f320ff --- /dev/null +++ b/net/openvswitch/vport-internal_switchdev.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef VPORT_INTERNAL_SWITCHDEV_H +#define VPORT_INTERNAL_SWITCHDEV_H 1 + +bool ovs_is_suitable_for_internal_swdev(struct net *net, const char *name); +struct vport *ovs_internal_swdev_get_vport_by_dp(const struct datapath *dp); +struct vport *ovs_internal_swdev_get_vport(const struct net_device *dev); +bool ovs_is_internal_swdev(const struct net_device *dev); +struct net_device *ovs_internal_swdev_get_netdev(struct vport *vport); + +#endif /* vport-internal_switchdev.h */ diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index d21f77d..3121b59 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -31,6 +31,7 @@ #include "datapath.h" #include "vport-internal_dev.h" +#include "vport-internal_switchdev.h" #include "vport-netdev.h" /* Must be called with rcu_read_lock. */ @@ -107,7 +108,8 @@ static struct vport *netdev_create(const struct vport_parms *parms) if (netdev_vport->dev->flags & IFF_LOOPBACK || netdev_vport->dev->type != ARPHRD_ETHER || - ovs_is_internal_dev(netdev_vport->dev)) { + ovs_is_internal_dev(netdev_vport->dev) || + ovs_is_internal_swdev(netdev_vport->dev)) { err = -EINVAL; goto error_put; } diff --git a/net/openvswitch/vport-switchportdev.c b/net/openvswitch/vport-switchportdev.c new file mode 100644 index 0000000..8657065 --- /dev/null +++ b/net/openvswitch/vport-switchportdev.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <linux/kernel.h> +#include <linux/rcupdate.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/if_vlan.h> +#include <linux/switchdev.h> + +#include <net/net_namespace.h> + +#include "datapath.h" +#include "vport-internal_switchdev.h" + +struct swportdev_vport { + struct rcu_head rcu; + struct net_device *dev; + struct packet_type pt; +}; + +static inline struct swportdev_vport *swportdev_vport_priv(const struct vport *vport) +{ + return vport_priv(vport); +} + +static int vport_swportdev_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) +{ + struct vport *vport = pt->priv; + struct dp_upcall_info upcall; + struct sw_flow_key key; + int err; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + + if (!skb->missed_flow || skb->pkt_type == PACKET_OUTGOING) + goto drop; + + /* Extract flow from 'skb' into 'key'. */ + err = ovs_flow_extract(skb, vport->port_no, &key); + if (unlikely(err)) + goto drop; + + upcall.cmd = OVS_PACKET_CMD_MISS; + upcall.key = &key; + upcall.userdata = NULL; + upcall.portid = vport->upcall_portid; + ovs_dp_upcall(vport->dp, skb, &upcall); + consume_skb(skb); + return NET_RX_SUCCESS; + +drop: + consume_skb(skb); + return NET_RX_DROP; +} + +static struct vport *vport_swportdev_create(const struct vport_parms *parms) +{ + struct vport *vport; + struct vport *swdev_vport; + struct swportdev_vport *swportdev_vport; + struct net_device *dev; + int err; + + swdev_vport = ovs_internal_swdev_get_vport_by_dp(parms->dp); + if (!swdev_vport) + return ERR_PTR(-EOPNOTSUPP); + + vport = ovs_vport_alloc(sizeof(struct swportdev_vport), + &ovs_swportdev_vport_ops, parms); + if (IS_ERR(vport)) + return vport; + + swportdev_vport = swportdev_vport_priv(vport); + + rtnl_lock(); + dev = __dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name); + if (!dev) { + err = -ENODEV; + goto error_free_vport; + } + if (!swportdev_dev_check(dev)) { + err = -EINVAL; + goto error_free_vport; + } + if (netdev_master_upper_dev_get(dev) != + ovs_internal_swdev_get_netdev(swdev_vport)) { + err = -EINVAL; + goto error_free_vport; + } + + dev_hold(dev); + rtnl_unlock(); + swportdev_vport->dev = dev; + swportdev_vport->pt.type = cpu_to_be16(ETH_P_ALL), + swportdev_vport->pt.dev = dev; + swportdev_vport->pt.func = vport_swportdev_rcv; + swportdev_vport->pt.priv = vport; + dev_add_pack(&swportdev_vport->pt); + + return vport; + +error_free_vport: + rtnl_unlock(); + ovs_vport_free(vport); + return ERR_PTR(err); +} + +static void vport_swportdev_free_rcu(struct rcu_head *rcu) +{ + struct swportdev_vport *swportdev_vport; + + swportdev_vport = container_of(rcu, struct swportdev_vport, rcu); + ovs_vport_free(vport_from_priv(swportdev_vport)); +} + +static void vport_swportdev_destroy(struct vport *vport) +{ + struct swportdev_vport *swportdev_vport = swportdev_vport_priv(vport); + + __dev_remove_pack(&swportdev_vport->pt); + dev_put(swportdev_vport->dev); + call_rcu(&swportdev_vport->rcu, vport_swportdev_free_rcu); +} + +static unsigned int packet_length(const struct sk_buff *skb) +{ + unsigned int length = skb->len - ETH_HLEN; + + if (skb->protocol == htons(ETH_P_8021Q)) + length -= VLAN_HLEN; + + return length; +} + +static int vport_swportdev_send(struct vport *vport, struct sk_buff *skb) +{ + struct swportdev_vport *swportdev_vport = swportdev_vport_priv(vport); + struct net_device *dev = swportdev_vport->dev; + int mtu = dev->mtu; + int len; + + if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { + net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", + dev->name, packet_length(skb), mtu); + goto drop; + } + + skb->dev = dev; + len = skb->len; + dev_queue_xmit(skb); + + return len; + +drop: + kfree_skb(skb); + return 0; +} + +static const char *vport_swportdev_get_name(const struct vport *vport) +{ + struct swportdev_vport *swportdev_vport = swportdev_vport_priv(vport); + + return swportdev_vport->dev->name; +} + +const struct vport_ops ovs_swportdev_vport_ops = { + .type = OVS_VPORT_TYPE_SWITCHPORTDEV, + .create = vport_swportdev_create, + .destroy = vport_swportdev_destroy, + .get_name = vport_swportdev_get_name, + .send = vport_swportdev_send, +}; + +bool ovs_is_suitable_for_switchportdev(struct net *net, const char *name) +{ + struct net_device *dev; + bool ret; + + rcu_read_lock(); + dev = dev_get_by_name_rcu(net, name); + ret = dev ? swportdev_dev_check(dev) : false; + rcu_read_unlock(); + return ret; +} + diff --git a/net/openvswitch/vport-switchportdev.h b/net/openvswitch/vport-switchportdev.h new file mode 100644 index 0000000..b578794 --- /dev/null +++ b/net/openvswitch/vport-switchportdev.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef VPORT_SWITCHPORTDEV_H +#define VPORT_SWITCHPORTDEV_H 1 + +bool ovs_is_suitable_for_switchportdev(struct net *net, const char *name); + +#endif /* vport-switchportdev.h */ diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 81b083c..39aa836 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -48,6 +48,10 @@ static const struct vport_ops *vport_ops_list[] = { #ifdef CONFIG_OPENVSWITCH_VXLAN &ovs_vxlan_vport_ops, #endif +#if IS_ENABLED(CONFIG_NET_SWITCHDEV) + &ovs_internal_swdev_vport_ops, + &ovs_swportdev_vport_ops, +#endif }; /* Protected by RCU read lock for reading, ovs_mutex for writing. */ diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 0979304..100277f 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -199,6 +199,8 @@ extern const struct vport_ops ovs_netdev_vport_ops; extern const struct vport_ops ovs_internal_vport_ops; extern const struct vport_ops ovs_gre_vport_ops; extern const struct vport_ops ovs_vxlan_vport_ops; +extern const struct vport_ops ovs_internal_swdev_vport_ops; +extern const struct vport_ops ovs_swportdev_vport_ops; static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, const void *start, unsigned int len) -- 1.8.5.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev