Benefit from the possibility to work with flows in switch devices and
use the swdev api to offload flow datapath.

Signed-off-by: Jiri Pirko <j...@resnulli.us>
---
 include/linux/sw_flow.h      |  16 +++-
 net/openvswitch/Makefile     |   3 +-
 net/openvswitch/datapath.c   |  16 +++-
 net/openvswitch/hw_offload.c | 170 +++++++++++++++++++++++++++++++++++++++++++
 net/openvswitch/hw_offload.h |  31 ++++++++
 5 files changed, 231 insertions(+), 5 deletions(-)
 create mode 100644 net/openvswitch/hw_offload.c
 create mode 100644 net/openvswitch/hw_offload.h

diff --git a/include/linux/sw_flow.h b/include/linux/sw_flow.h
index 3bbd5aa..12a24f9 100644
--- a/include/linux/sw_flow.h
+++ b/include/linux/sw_flow.h
@@ -102,7 +102,21 @@ struct sw_flow {
        struct sw_flow_mask *mask;
 };
 
+enum sw_flow_action_type {
+       SW_FLOW_ACTION_TYPE_OUTPUT,
+       SW_FLOW_ACTION_TYPE_VLAN_PUSH,
+       SW_FLOW_ACTION_TYPE_VLAN_POP,
+};
+
 struct sw_flow_action {
-}
+       enum sw_flow_action_type type;
+       union {
+               struct net_device *output_dev;
+               struct {
+                       __be16 vlan_proto;
+                       u16 vlan_tci;
+               } vlan;
+       };
+};
 
 #endif /* _LINUX_SW_FLOW_H_ */
diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile
index 3591cb5..5152437 100644
--- a/net/openvswitch/Makefile
+++ b/net/openvswitch/Makefile
@@ -13,7 +13,8 @@ openvswitch-y := \
        flow_table.o \
        vport.o \
        vport-internal_dev.o \
-       vport-netdev.o
+       vport-netdev.o \
+       hw_offload.o
 
 ifneq ($(CONFIG_OPENVSWITCH_VXLAN),)
 openvswitch-y += vport-vxlan.o
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 10ffb0a..f1e0792 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -59,6 +59,7 @@
 #include "flow_netlink.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
+#include "hw_offload.h"
 
 int ovs_net_id __read_mostly;
 
@@ -840,13 +841,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_hw_flow_insert(dp, flow, flow->sf_acts);
+               if (error)
+                       goto err_flow_tbl_remove;
 
                reply = ovs_flow_cmd_build_info(flow, dp, info, 
OVS_FLOW_CMD_NEW);
        } else {
@@ -868,6 +871,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                if (!ovs_flow_cmp_unmasked_key(flow, &match))
                        goto err_unlock_ovs;
 
+               error = ovs_hw_flow_actions_update(dp, flow, acts);
+               if (error)
+                       goto err_unlock_ovs;
+
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
@@ -888,6 +895,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:
@@ -985,6 +994,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
                goto unlock;
        }
 
+       ovs_hw_flow_remove(dp, flow);
        ovs_flow_tbl_remove(&dp->table, flow);
 
        err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
diff --git a/net/openvswitch/hw_offload.c b/net/openvswitch/hw_offload.c
new file mode 100644
index 0000000..c4c64d0
--- /dev/null
+++ b/net/openvswitch/hw_offload.c
@@ -0,0 +1,170 @@
+/*
+ * 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/sw_flow.h>
+#include <linux/switchdev.h>
+
+#include "datapath.h"
+#include "vport-netdev.h"
+
+static struct net_device *__dp_master(struct datapath *dp)
+{
+       struct vport *local;
+
+       local = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);
+       BUG_ON(!local);
+       return local->ops->get_netdev(local);
+}
+
+static int sw_flow_action_create(struct datapath *dp,
+                                struct sw_flow_action **p_action,
+                                size_t *p_action_count,
+                                struct sw_flow_actions *acts)
+{
+       const struct nlattr *attr = acts->actions;
+       int len = acts->actions_len;
+       const struct nlattr *a;
+       int rem;
+       struct sw_flow_action *action, *cur;
+       size_t action_count = 0;
+       int err;
+
+       for (a = attr, rem = len; rem > 0; a = nla_next(a, &rem))
+               action_count++;
+
+       action = kzalloc(sizeof(*action) * action_count, GFP_KERNEL);
+       if (!action)
+               return -ENOMEM;
+
+       cur = action;
+       for (a = attr, rem = len; rem > 0; a = nla_next(a, &rem)) {
+               switch (nla_type(a)) {
+               case OVS_ACTION_ATTR_OUTPUT:
+                       {
+                               struct vport *vport;
+
+                               vport = ovs_vport_ovsl_rcu(dp, nla_get_u32(a));
+                               if (vport->ops->type != OVS_VPORT_TYPE_NETDEV) {
+                                       err = -EOPNOTSUPP;
+                                       goto errout;
+                               }
+                               cur->type = SW_FLOW_ACTION_TYPE_OUTPUT;
+                               cur->output_dev = vport->ops->get_netdev(vport);
+                       }
+                       break;
+
+               case OVS_ACTION_ATTR_PUSH_VLAN:
+                       {
+                               const struct ovs_action_push_vlan *vlan;
+
+                               vlan = nla_data(a);
+                               cur->type = SW_FLOW_ACTION_TYPE_VLAN_PUSH;
+                               cur->vlan.vlan_proto = vlan->vlan_tpid;
+                               cur->vlan.vlan_tci = vlan->vlan_tci;
+                       }
+                       break;
+
+               case OVS_ACTION_ATTR_POP_VLAN:
+                       cur->type = SW_FLOW_ACTION_TYPE_VLAN_POP;
+                       break;
+
+               default:
+                       err = -EOPNOTSUPP;
+                       goto errout;
+               }
+               action++;
+       }
+       *p_action = action;
+       *p_action_count = action_count;
+       return 0;
+
+errout:
+       kfree(action);
+       return err;
+}
+
+int ovs_hw_flow_insert(struct datapath *dp, struct ovs_flow *flow,
+                      struct sw_flow_actions *acts)
+{
+       struct list_head *iter;
+       struct net_device *dev;
+       struct sw_flow_action *action;
+       size_t action_count;
+       int err;
+
+       err = sw_flow_action_create(dp, &action, &action_count, acts);
+       if (err)
+               return err;
+
+       rcu_read_lock();
+       netdev_for_each_all_lower_dev_rcu(__dp_master(dp), dev, iter) {
+               err = swdev_flow_insert(dev, &flow->flow);
+               if (err && err != -EOPNOTSUPP)
+                       break;
+               err = swdev_flow_action_set(dev, &flow->flow,
+                                           action, action_count);
+               if (err && err != -EOPNOTSUPP)
+                       break;
+       }
+       rcu_read_unlock();
+
+       kfree(action);
+
+       return err;
+}
+
+void ovs_hw_flow_remove(struct datapath *dp, struct ovs_flow *flow)
+{
+       struct list_head *iter;
+       struct net_device *dev;
+
+       rcu_read_lock();
+       netdev_for_each_all_lower_dev_rcu(__dp_master(dp), dev, iter)
+               swdev_flow_remove(dev, &flow->flow);
+       rcu_read_unlock();
+}
+
+int ovs_hw_flow_actions_update(struct datapath *dp, struct ovs_flow *flow,
+                              struct sw_flow_actions *acts)
+{
+       struct list_head *iter;
+       struct net_device *dev;
+       struct sw_flow_action *action;
+       size_t action_count;
+       int err;
+
+       err = sw_flow_action_create(dp, &action, &action_count, acts);
+       if (err)
+               return err;
+
+       rcu_read_lock();
+       netdev_for_each_all_lower_dev_rcu(__dp_master(dp), dev, iter) {
+               err = swdev_flow_action_set(dev, &flow->flow,
+                                           action, action_count);
+               if (err && err != -EOPNOTSUPP)
+                       break;
+       }
+       rcu_read_unlock();
+
+       kfree(action);
+
+       return err;
+}
diff --git a/net/openvswitch/hw_offload.h b/net/openvswitch/hw_offload.h
new file mode 100644
index 0000000..7ecb60f
--- /dev/null
+++ b/net/openvswitch/hw_offload.h
@@ -0,0 +1,31 @@
+/*
+ * 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 HW_OFFLOAD_H
+#define HW_OFFLOAD_H 1
+
+#include "datapath.h"
+#include "flow.h"
+
+int ovs_hw_flow_insert(struct datapath *dp, struct ovs_flow *flow,
+                      struct sw_flow_actions *acts);
+void ovs_hw_flow_remove(struct datapath *dp, struct ovs_flow *flow);
+int ovs_hw_flow_actions_update(struct datapath *dp, struct ovs_flow *flow,
+                              struct sw_flow_actions *acts);
+
+#endif
-- 
1.9.0

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to