Parse switchdev flow object into device specific commands and program
the hardware to classify and mark/drop the flow accordingly.

A new Kconfig is introduced: MLX5_EN_SWITCHDEV. This config enables to
compile the driver when switchdev is not compiled.

Signed-off-by: Amir Vadai <a...@vadai.me>
---
 drivers/net/ethernet/mellanox/mlx5/core/Kconfig    |   7 +
 drivers/net/ethernet/mellanox/mlx5/core/Makefile   |   3 +
 drivers/net/ethernet/mellanox/mlx5/core/en.h       |  10 +
 drivers/net/ethernet/mellanox/mlx5/core/en_fs.c    |  10 +-
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c  |   2 +
 drivers/net/ethernet/mellanox/mlx5/core/en_rx.c    |   2 +
 .../net/ethernet/mellanox/mlx5/core/en_switchdev.c | 475 +++++++++++++++++++++
 .../net/ethernet/mellanox/mlx5/core/en_switchdev.h |  60 +++
 8 files changed, 568 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.c
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.h

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig 
b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index c503ea0..61a9eed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -19,3 +19,10 @@ config MLX5_CORE_EN
          Ethernet support in Mellanox Technologies ConnectX-4 NIC.
          Ethernet and Infiniband support in ConnectX-4 are currently mutually
          exclusive.
+
+config MLX5_EN_SWITCHDEV
+       bool "MLX5 EN switchdev support"
+       depends on MLX5_CORE_EN && NET_SWITCHDEV
+       default y
+       ---help---
+         Switchdev support in Mellanox Technologies ConnectX-4 NIC.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile 
b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 01c0256..b80143e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -3,6 +3,9 @@ obj-$(CONFIG_MLX5_CORE)         += mlx5_core.o
 mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
                health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o   \
                mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o
+
+mlx5_core-$(CONFIG_MLX5_EN_SWITCHDEV) += en_switchdev.o
+
 mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \
                en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \
                en_txrx.o en_clock.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h 
b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 9ea49a8..e61a67c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -39,6 +39,8 @@
 #include <linux/mlx5/qp.h>
 #include <linux/mlx5/cq.h>
 #include <linux/mlx5/vport.h>
+#include <linux/rhashtable.h>
+#include <net/switchdev.h>
 #include "wq.h"
 #include "transobj.h"
 #include "mlx5_core.h"
@@ -497,8 +499,16 @@ struct mlx5e_flow_table {
        struct mlx5_flow_group          **g;
 };
 
+struct mlx5e_offloads_flow_table {
+       struct mlx5_flow_table          *t;
+
+       struct rhashtable_params        ht_params;
+       struct rhashtable               ht;
+};
+
 struct mlx5e_flow_tables {
        struct mlx5_flow_namespace      *ns;
+       struct mlx5e_offloads_flow_table      offloads;
        struct mlx5e_flow_table         vlan;
        struct mlx5e_flow_table         main;
 };
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c 
b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
index 80d81ab..0fbe45c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
@@ -36,6 +36,7 @@
 #include <linux/tcp.h>
 #include <linux/mlx5/fs.h>
 #include "en.h"
+#include "en_switchdev.h"
 
 #define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v)
 
@@ -1202,12 +1203,18 @@ int mlx5e_create_flow_tables(struct mlx5e_priv *priv)
        if (err)
                goto err_destroy_vlan_flow_table;
 
-       err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+       err = mlx5e_create_offloads_flow_table(priv);
        if (err)
                goto err_destroy_main_flow_table;
 
+       err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+       if (err)
+               goto err_destroy_offloads_flow_table;
+
        return 0;
 
+err_destroy_offloads_flow_table:
+       mlx5e_destroy_offloads_flow_table(priv);
 err_destroy_main_flow_table:
        mlx5e_destroy_main_flow_table(priv);
 err_destroy_vlan_flow_table:
@@ -1219,6 +1226,7 @@ err_destroy_vlan_flow_table:
 void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv)
 {
        mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+       mlx5e_destroy_offloads_flow_table(priv);
        mlx5e_destroy_main_flow_table(priv);
        mlx5e_destroy_vlan_flow_table(priv);
 }
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c 
b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 5c74a73..4bc9243 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -32,6 +32,7 @@
 
 #include <linux/mlx5/fs.h>
 #include "en.h"
+#include "en_switchdev.h"
 #include "eswitch.h"
 
 struct mlx5e_rq_param {
@@ -2178,6 +2179,7 @@ static void mlx5e_build_netdev(struct net_device *netdev)
 
        netdev->priv_flags       |= IFF_UNICAST_FLT;
 
+       mlx5e_switchdev_init(netdev);
        mlx5e_set_netdev_dev_addr(netdev);
 }
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c 
b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index dd959d9..678d4e0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -223,6 +223,8 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 
*cqe,
        if (cqe_has_vlan(cqe))
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                       be16_to_cpu(cqe->vlan_info));
+
+       skb->mark = be32_to_cpu(cqe->sop_drop_qpn) & 0x00ffffff;
 }
 
 int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.c 
b/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.c
new file mode 100644
index 0000000..b88ead4
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <net/switchdev.h>
+#include <linux/mlx5/fs.h>
+#include <linux/mlx5/device.h>
+#include <linux/rhashtable.h>
+#include "en.h"
+#include "en_switchdev.h"
+#include "eswitch.h"
+
+struct mlx5e_switchdev_flow {
+       struct rhash_head       node;
+       unsigned long           cookie;
+       void                    *rule;
+};
+
+static int prep_flow_attr(struct switchdev_obj_port_flow *f)
+{
+       struct switchdev_obj_port_flow_act *act = f->actions;
+
+       if (~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+             BIT(FLOW_DISSECTOR_KEY_BASIC) |
+             BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_VLANID) |
+             BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_PORTS) |
+             BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS)) & f->dissector->used_keys) {
+               pr_warn("Unsupported key used: 0x%x\n",
+                       f->dissector->used_keys);
+               return -ENOTSUPP;
+       }
+
+       if (~(BIT(SWITCHDEV_OBJ_PORT_FLOW_ACT_DROP) |
+             BIT(SWITCHDEV_OBJ_PORT_FLOW_ACT_MARK)) & act->actions) {
+               pr_warn("Unsupported action used: 0x%x\n", act->actions);
+               return -ENOTSUPP;
+       }
+
+       if (BIT(SWITCHDEV_OBJ_PORT_FLOW_ACT_MARK) & act->actions &&
+           (act->mark & ~0xffff)) {
+               pr_warn("Bad flow mark - only 16 bit is supported: 0x%x\n",
+                       act->mark);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int parse_flow_attr(u32 *match_c, u32 *match_v,
+                          u32 *action, u32 *flow_tag,
+                          struct switchdev_obj_port_flow *f)
+{
+       void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
+                                            outer_headers);
+       void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v,
+                                            outer_headers);
+       struct switchdev_obj_port_flow_act *act = f->actions;
+       u16 addr_type = 0;
+       u8 ip_proto = 0;
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_dissector_key_control *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->key);
+               addr_type = key->addr_type;
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_dissector_key_basic *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->key);
+               struct flow_dissector_key_basic *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->mask);
+               ip_proto = key->ip_proto;
+
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ethertype,
+                        ntohs(mask->n_proto));
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ethertype,
+                        ntohs(key->n_proto));
+
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol,
+                        mask->ip_proto);
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol,
+                        key->ip_proto);
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_dissector_key_eth_addrs *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                                 f->key);
+               struct flow_dissector_key_eth_addrs *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                                 f->mask);
+
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+                                            outer_headers_c, dmac_47_16),
+                               mask->dst);
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+                                            outer_headers_v, dmac_47_16),
+                               key->dst);
+
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+                                            outer_headers_c, smac_47_16),
+                               mask->src);
+               ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+                                            outer_headers_v, smac_47_16),
+                               key->src);
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLANID)) {
+               struct flow_dissector_key_tags *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_VLANID,
+                                                 f->key);
+               struct flow_dissector_key_tags *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_VLANID,
+                                                 f->mask);
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, vlan_tag, 1);
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, vlan_tag, 1);
+
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, first_vid,
+                        ntohs(mask->vlan_id));
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, first_vid,
+                        ntohs(key->vlan_id));
+
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, first_cfi,
+                        ntohs(mask->flow_label));
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, first_cfi,
+                        ntohs(key->flow_label));
+
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, first_prio,
+                        ntohs(mask->flow_label) >> 1);
+               MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, first_prio,
+                        ntohs(key->flow_label) >> 1);
+       }
+
+       if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+               struct flow_dissector_key_ipv4_addrs *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+                                                 f->key);
+               struct flow_dissector_key_ipv4_addrs *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+                                                 f->mask);
+
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+                                   src_ipv4_src_ipv6.ipv4_layout.ipv4),
+                      &mask->src, sizeof(mask->src));
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+                                   src_ipv4_src_ipv6.ipv4_layout.ipv4),
+                      &key->src, sizeof(key->src));
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+                                   dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+                      &mask->dst, sizeof(mask->dst));
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+                                   dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+                      &key->dst, sizeof(key->dst));
+       }
+
+       if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+               struct flow_dissector_key_ipv6_addrs *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+                                                 f->key);
+               struct flow_dissector_key_ipv6_addrs *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+                                                 f->mask);
+
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+                                   src_ipv4_src_ipv6.ipv6_layout.ipv6),
+                      &mask->src, sizeof(mask->src));
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+                                   src_ipv4_src_ipv6.ipv6_layout.ipv6),
+                      &key->src, sizeof(key->src));
+
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+                                   dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
+                      &mask->dst, sizeof(mask->dst));
+               memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+                                   dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
+                      &key->dst, sizeof(key->dst));
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_dissector_key_ports *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_PORTS,
+                                                 f->key);
+               struct flow_dissector_key_ports *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_PORTS,
+                                                 f->mask);
+               switch (ip_proto) {
+               case IPPROTO_TCP:
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+                                tcp_sport, ntohs(mask->src));
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+                                tcp_sport, ntohs(key->src));
+
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+                                tcp_dport, ntohs(mask->dst));
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+                                tcp_dport, ntohs(key->dst));
+                       break;
+
+               case IPPROTO_UDP:
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+                                udp_sport, ntohs(mask->src));
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+                                udp_sport, ntohs(key->src));
+
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+                                udp_dport, ntohs(mask->dst));
+                       MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+                                udp_dport, ntohs(key->dst));
+                       break;
+               default:
+                       pr_err("Only UDP and TCP transport are supported\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* Actions: */
+       if (BIT(SWITCHDEV_OBJ_PORT_FLOW_ACT_MARK) & act->actions) {
+               *flow_tag = act->mark;
+               *action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       }
+
+       if (BIT(SWITCHDEV_OBJ_PORT_FLOW_ACT_DROP) & act->actions)
+               *action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+
+       return 0;
+}
+
+#define MLX5E_TC_FLOW_TABLE_NUM_ENTRIES 10
+#define MLX5E_TC_FLOW_TABLE_NUM_GROUPS 10
+int mlx5e_create_offloads_flow_table(struct mlx5e_priv *priv)
+{
+       struct mlx5_flow_namespace *ns;
+
+       ns = mlx5_get_flow_namespace(priv->mdev,
+                                    MLX5_FLOW_NAMESPACE_OFFLOADS);
+       if (!ns)
+               return -EINVAL;
+
+       priv->fts.offloads.t = mlx5_create_auto_grouped_flow_table(ns, 0,
+                                           MLX5E_TC_FLOW_TABLE_NUM_ENTRIES,
+                                           MLX5E_TC_FLOW_TABLE_NUM_GROUPS);
+       if (IS_ERR(priv->fts.offloads.t))
+               return PTR_ERR(priv->fts.offloads.t);
+
+       return 0;
+}
+
+void mlx5e_destroy_offloads_flow_table(struct mlx5e_priv *priv)
+{
+       mlx5_destroy_flow_table(priv->fts.offloads.t);
+       priv->fts.offloads.t = NULL;
+}
+
+static u8 generate_match_criteria_enable(u32 *match_c)
+{
+       u8 match_criteria_enable = 0;
+       void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
+                                             outer_headers);
+       void *inner_headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
+                                             inner_headers);
+       void *misc_c = MLX5_ADDR_OF(fte_match_param, match_c,
+                                    misc_parameters);
+       size_t header_size = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4);
+       size_t misc_size = MLX5_ST_SZ_BYTES(fte_match_set_misc);
+
+       if (memchr_inv(outer_headers_c, 0, header_size))
+               match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+       if (memchr_inv(misc_c, 0, misc_size))
+               match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS;
+       if (memchr_inv(inner_headers_c, 0, header_size))
+               match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
+
+       return match_criteria_enable;
+}
+
+static int mlx5e_offloads_flow_add(struct net_device *netdev,
+                                  struct switchdev_obj_port_flow *f)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_offloads_flow_table *offloads = &priv->fts.offloads;
+       struct mlx5_flow_table *ft = offloads->t;
+       u8 match_criteria_enable;
+       u32 *match_c;
+       u32 *match_v;
+       int err = 0;
+       u32 flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
+       u32 action = 0;
+       struct mlx5e_switchdev_flow *flow;
+
+       match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+       match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+       if (!match_c || !match_v) {
+               err = -ENOMEM;
+               goto free;
+       }
+
+       flow = kzalloc(sizeof(*flow), GFP_KERNEL);
+       if (!flow) {
+               err = -ENOMEM;
+               goto free;
+       }
+       flow->cookie = f->cookie;
+
+       err = parse_flow_attr(match_c, match_v, &action, &flow_tag, f);
+       if (err < 0)
+               goto free;
+
+       /* Outer header support only */
+       match_criteria_enable = generate_match_criteria_enable(match_c);
+
+       flow->rule = mlx5_add_flow_rule(ft, match_criteria_enable,
+                                       match_c, match_v,
+                                       action, flow_tag, NULL);
+       if (IS_ERR(flow->rule)) {
+               kfree(flow);
+               err = PTR_ERR(flow->rule);
+               goto free;
+       }
+
+       err = rhashtable_insert_fast(&offloads->ht, &flow->node,
+                                    offloads->ht_params);
+       if (err) {
+               mlx5_del_flow_rule(flow->rule);
+               kfree(flow);
+       }
+
+free:
+       kfree(match_c);
+       kfree(match_v);
+       return err;
+}
+
+static int mlx5e_offloads_flow_del(struct net_device *netdev,
+                                  struct switchdev_obj_port_flow *f)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_switchdev_flow *flow;
+       struct mlx5e_offloads_flow_table *offloads = &priv->fts.offloads;
+
+       flow = rhashtable_lookup_fast(&offloads->ht, &f->cookie,
+                                     offloads->ht_params);
+       if (!flow) {
+               pr_err("Can't find requested flow");
+               return -EINVAL;
+       }
+
+       mlx5_del_flow_rule(flow->rule);
+
+       rhashtable_remove_fast(&offloads->ht, &flow->node, offloads->ht_params);
+       kfree(flow);
+
+       return 0;
+}
+
+static int mlx5e_port_obj_add(struct net_device *dev,
+                             const struct switchdev_obj *obj,
+                             struct switchdev_trans *trans)
+{
+       int err = 0;
+
+       if (trans->ph_prepare) {
+               switch (obj->id) {
+               case SWITCHDEV_OBJ_ID_PORT_FLOW:
+                       err = prep_flow_attr(SWITCHDEV_OBJ_PORT_FLOW(obj));
+                       break;
+               default:
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+
+               return err;
+       }
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_FLOW:
+               err = mlx5e_offloads_flow_add(dev,
+                                             SWITCHDEV_OBJ_PORT_FLOW(obj));
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int mlx5e_port_obj_del(struct net_device *dev,
+                             const struct switchdev_obj *obj)
+{
+       int err = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_FLOW:
+               err = mlx5e_offloads_flow_del(dev,
+                                             SWITCHDEV_OBJ_PORT_FLOW(obj));
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+const struct switchdev_ops mlx5e_switchdev_ops = {
+       .switchdev_port_obj_add = mlx5e_port_obj_add,
+       .switchdev_port_obj_del = mlx5e_port_obj_del,
+};
+
+static const struct rhashtable_params mlx5e_switchdev_flow_ht_params = {
+       .head_offset = offsetof(struct mlx5e_switchdev_flow, node),
+       .key_offset = offsetof(struct mlx5e_switchdev_flow, cookie),
+       .key_len = sizeof(unsigned long),
+       .hashfn = jhash,
+       .automatic_shrinking = true,
+};
+
+void mlx5e_switchdev_init(struct net_device *netdev)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_offloads_flow_table *offloads = &priv->fts.offloads;
+
+       netdev->switchdev_ops = &mlx5e_switchdev_ops;
+
+       offloads->ht_params = mlx5e_switchdev_flow_ht_params;
+       rhashtable_init(&offloads->ht, &offloads->ht_params);
+}
+
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.h 
b/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.h
new file mode 100644
index 0000000..8f4e3a3
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_switchdev.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __MLX5_EN_SWITCHDEV__H__
+#define __MLX5_EN_SWITCHDEV__H__
+
+#ifdef CONFIG_MLX5_EN_SWITCHDEV
+
+extern const struct switchdev_ops mlx5e_switchdev_ops;
+
+void mlx5e_destroy_offloads_flow_table(struct mlx5e_priv *priv);
+int mlx5e_create_offloads_flow_table(struct mlx5e_priv *priv);
+void mlx5e_switchdev_init(struct net_device *dev);
+
+#else
+static inline void mlx5e_destroy_offloads_flow_table(struct mlx5e_priv *priv)
+{
+}
+
+static inline int mlx5e_create_offloads_flow_table(struct mlx5e_priv *priv)
+{
+       return 0;
+}
+
+static inline void mlx5e_switchdev_init(struct net_device *dev)
+{
+}
+#endif
+
+#endif /* __MLX5_EN_SWITCHDEV__H__ */
+
-- 
2.7.0

Reply via email to