[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1395851472-10524-6-git-send-email-jiri@resnulli.us>
Date: Wed, 26 Mar 2014 17:31:11 +0100
From: Jiri Pirko <jiri@...nulli.us>
To: netdev@...r.kernel.org
Cc: davem@...emloft.net, nhorman@...driver.com, andy@...yhouse.net,
tgraf@...g.ch, dborkman@...hat.com, ogerlitz@...lanox.com,
jesse@...ira.com, pshelar@...ira.com, azhou@...ira.com,
ben@...adent.org.uk, stephen@...workplumber.org,
jeffrey.t.kirsher@...el.com, vyasevic@...hat.com,
xiyou.wangcong@...il.com, john.r.fastabend@...el.com,
edumazet@...gle.com, jhs@...atatu.com, sfeldma@...ulusnetworks.com,
f.fainelli@...il.com, roopa@...ulusnetworks.com,
linville@...driver.com, dev@...nvswitch.org
Subject: [patch net-next RFC v2 5/6] openvswitch: Introduce support for switchdev based datapath
Signed-off-by: Jiri Pirko <jiri@...nulli.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 <jiri@...nulli.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 <jiri@...nulli.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 <jiri@...nulli.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 <jiri@...nulli.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
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists