net/mlx5e: Add TC tunnel release action for SRIOV offloads
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_tc.c
index ce8c54d18906bedc57f0bdd8353e1f2156d47ce7..89466539a00c60e2223cc926a98611812f9b9c52 100644 (file)
 #include <net/switchdev.h>
 #include <net/tc_act/tc_mirred.h>
 #include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_tunnel_key.h>
 #include "en.h"
 #include "en_tc.h"
 #include "eswitch.h"
+#include "vxlan.h"
 
 struct mlx5e_tc_flow {
        struct rhash_head       node;
        u64                     cookie;
-       struct mlx5_flow_rule   *rule;
+       struct mlx5_flow_handle *rule;
        struct mlx5_esw_flow_attr *attr;
 };
 
 #define MLX5E_TC_TABLE_NUM_ENTRIES 1024
 #define MLX5E_TC_TABLE_NUM_GROUPS 4
 
-static struct mlx5_flow_rule *mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
-                                                   struct mlx5_flow_spec *spec,
-                                                   u32 action, u32 flow_tag)
+static struct mlx5_flow_handle *
+mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
+                     struct mlx5_flow_spec *spec,
+                     u32 action, u32 flow_tag)
 {
        struct mlx5_core_dev *dev = priv->mdev;
        struct mlx5_flow_destination dest = { 0 };
+       struct mlx5_flow_act flow_act = {
+               .action = action,
+               .flow_tag = flow_tag,
+               .encap_id = 0,
+       };
        struct mlx5_fc *counter = NULL;
-       struct mlx5_flow_rule *rule;
+       struct mlx5_flow_handle *rule;
        bool table_created = false;
 
        if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
@@ -82,7 +90,7 @@ static struct mlx5_flow_rule *mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
                                                            MLX5E_TC_PRIO,
                                                            MLX5E_TC_TABLE_NUM_ENTRIES,
                                                            MLX5E_TC_TABLE_NUM_GROUPS,
-                                                           0);
+                                                           0, 0);
                if (IS_ERR(priv->fs.tc.t)) {
                        netdev_err(priv->netdev,
                                   "Failed to create tc offload table\n");
@@ -94,9 +102,7 @@ static struct mlx5_flow_rule *mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
        }
 
        spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
-       rule = mlx5_add_flow_rule(priv->fs.tc.t, spec,
-                                 action, flow_tag,
-                                 &dest);
+       rule = mlx5_add_flow_rules(priv->fs.tc.t, spec, &flow_act, &dest, 1);
 
        if (IS_ERR(rule))
                goto err_add_rule;
@@ -114,9 +120,10 @@ err_create_ft:
        return rule;
 }
 
-static struct mlx5_flow_rule *mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
-                                                   struct mlx5_flow_spec *spec,
-                                                   struct mlx5_esw_flow_attr *attr)
+static struct mlx5_flow_handle *
+mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
+                     struct mlx5_flow_spec *spec,
+                     struct mlx5_esw_flow_attr *attr)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        int err;
@@ -129,7 +136,7 @@ static struct mlx5_flow_rule *mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
 }
 
 static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
-                             struct mlx5_flow_rule *rule,
+                             struct mlx5_flow_handle *rule,
                              struct mlx5_esw_flow_attr *attr)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
@@ -140,7 +147,7 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
        if (esw && esw->mode == SRIOV_OFFLOADS)
                mlx5_eswitch_del_vlan_action(esw, attr);
 
-       mlx5_del_flow_rule(rule);
+       mlx5_del_flow_rules(rule);
 
        mlx5_fc_destroy(priv->mdev, counter);
 
@@ -150,6 +157,121 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
        }
 }
 
+static void parse_vxlan_attr(struct mlx5_flow_spec *spec,
+                            struct tc_cls_flower_offload *f)
+{
+       void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+                                      outer_headers);
+       void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                                      outer_headers);
+       void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+                                   misc_parameters);
+       void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                                   misc_parameters);
+
+       MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_dissector_key_keyid *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                                 f->key);
+               struct flow_dissector_key_keyid *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                                 f->mask);
+               MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
+                        be32_to_cpu(mask->keyid));
+               MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
+                        be32_to_cpu(key->keyid));
+       }
+}
+
+static int parse_tunnel_attr(struct mlx5e_priv *priv,
+                            struct mlx5_flow_spec *spec,
+                            struct tc_cls_flower_offload *f)
+{
+       void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+                                      outer_headers);
+       void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                                      outer_headers);
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
+               struct flow_dissector_key_ports *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_PORTS,
+                                                 f->key);
+               struct flow_dissector_key_ports *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_PORTS,
+                                                 f->mask);
+
+               /* Full udp dst port must be given */
+               if (memchr_inv(&mask->dst, 0xff, sizeof(mask->dst)))
+                       return -EOPNOTSUPP;
+
+               /* udp src port isn't supported */
+               if (memchr_inv(&mask->src, 0, sizeof(mask->src)))
+                       return -EOPNOTSUPP;
+
+               if (mlx5e_vxlan_lookup_port(priv, be16_to_cpu(key->dst)) &&
+                   MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
+                       parse_vxlan_attr(spec, f);
+               else
+                       return -EOPNOTSUPP;
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+                        udp_dport, ntohs(mask->dst));
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+                        udp_dport, ntohs(key->dst));
+
+       } else { /* udp dst port must be given */
+                       return -EOPNOTSUPP;
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
+               struct flow_dissector_key_ipv4_addrs *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
+                                                 f->key);
+               struct flow_dissector_key_ipv4_addrs *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
+                                                 f->mask);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+                        src_ipv4_src_ipv6.ipv4_layout.ipv4,
+                        ntohl(mask->src));
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+                        src_ipv4_src_ipv6.ipv4_layout.ipv4,
+                        ntohl(key->src));
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+                        dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
+                        ntohl(mask->dst));
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+                        dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
+                        ntohl(key->dst));
+       }
+
+       MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IP);
+
+       /* Enforce DMAC when offloading incoming tunneled flows.
+        * Flow counters require a match on the DMAC.
+        */
+       MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_47_16);
+       MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_15_0);
+       ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+                                    dmac_47_16), priv->netdev->dev_addr);
+
+       /* let software handle IP fragments */
+       MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 0);
+
+       return 0;
+}
+
 static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec,
                            struct tc_cls_flower_offload *f)
 {
@@ -167,12 +289,44 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec
              BIT(FLOW_DISSECTOR_KEY_VLAN) |
              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
-             BIT(FLOW_DISSECTOR_KEY_PORTS))) {
+             BIT(FLOW_DISSECTOR_KEY_PORTS) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
                netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
                            f->dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
+       if ((dissector_uses_key(f->dissector,
+                               FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) ||
+            dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID) ||
+            dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) &&
+           dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
+               struct flow_dissector_key_control *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_CONTROL,
+                                                 f->key);
+               switch (key->addr_type) {
+               case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
+                       if (parse_tunnel_attr(priv, spec, f))
+                               return -EOPNOTSUPP;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+
+               /* In decap flow, header pointers should point to the inner
+                * headers, outer header were already set by parse_tunnel_attr
+                */
+               headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+                                        inner_headers);
+               headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+                                        inner_headers);
+       }
+
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
                struct flow_dissector_key_control *key =
                        skb_flow_dissector_target(f->dissector,
@@ -404,7 +558,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        continue;
                }
 
-               if (is_tcf_mirred_redirect(a)) {
+               if (is_tcf_mirred_egress_redirect(a)) {
                        int ifindex = tcf_mirred_ifindex(a);
                        struct net_device *out_dev;
                        struct mlx5e_priv *out_priv;
@@ -417,7 +571,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                return -EINVAL;
                        }
 
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+                                       MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        out_priv = netdev_priv(out_dev);
                        attr->out_rep = out_priv->ppriv;
                        continue;
@@ -436,6 +591,11 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        continue;
                }
 
+               if (is_tcf_tunnel_release(a)) {
+                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
+                       continue;
+               }
+
                return -EINVAL;
        }
        return 0;
@@ -450,7 +610,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
        u32 flow_tag, action;
        struct mlx5e_tc_flow *flow;
        struct mlx5_flow_spec *spec;
-       struct mlx5_flow_rule *old = NULL;
+       struct mlx5_flow_handle *old = NULL;
        struct mlx5_esw_flow_attr *old_attr = NULL;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
 
@@ -511,7 +671,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
        goto out;
 
 err_del_rule:
-       mlx5_del_flow_rule(flow->rule);
+       mlx5_del_flow_rules(flow->rule);
 
 err_free:
        if (!old)