net/mlx5e: Use short attribute form when adding/deleting offloaded TC flows
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_tc.c
index 11c27e4fadf6e09400a432c5498749df73265477..a9feddc31667ca5f6821dc615cc8519a199b923b 100644 (file)
@@ -43,6 +43,7 @@
 #include <net/tc_act/tc_vlan.h>
 #include <net/tc_act/tc_tunnel_key.h>
 #include <net/tc_act/tc_pedit.h>
+#include <net/tc_act/tc_csum.h>
 #include <net/vxlan.h>
 #include <net/arp.h>
 #include "en.h"
@@ -176,6 +177,7 @@ err_create_mod_hdr_id:
 static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
                                  struct mlx5e_tc_flow *flow)
 {
+       struct mlx5_nic_flow_attr *attr = flow->nic_attr;
        struct mlx5_fc *counter = NULL;
 
        counter = mlx5_flow_rule_counter(flow->rule);
@@ -187,9 +189,9 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
                priv->fs.tc.t = NULL;
        }
 
-       if (flow->nic_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5_modify_header_dealloc(priv->mdev,
-                                          flow->nic_attr->mod_hdr_id);
+                                          attr->mod_hdr_id);
 }
 
 static void mlx5e_detach_encap(struct mlx5e_priv *priv,
@@ -230,7 +232,7 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        return rule;
 
 err_add_rule:
-       if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5_modify_header_dealloc(priv->mdev,
                                           attr->mod_hdr_id);
 err_mod_hdr:
@@ -249,17 +251,17 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
 
        if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
                flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED;
-               mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr);
+               mlx5_eswitch_del_offloaded_rule(esw, flow->rule, attr);
        }
 
-       mlx5_eswitch_del_vlan_action(esw, flow->esw_attr);
+       mlx5_eswitch_del_vlan_action(esw, attr);
 
-       if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) {
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) {
                mlx5e_detach_encap(priv, flow);
-               kvfree(flow->esw_attr->parse_attr);
+               kvfree(attr->parse_attr);
        }
 
-       if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5_modify_header_dealloc(priv->mdev,
                                           attr->mod_hdr_id);
 }
@@ -384,7 +386,7 @@ static void mlx5e_detach_encap(struct mlx5e_priv *priv,
                if (e->flags & MLX5_ENCAP_ENTRY_VALID)
                        mlx5_encap_dealloc(priv->mdev, e->encap_id);
 
-               hlist_del_rcu(&e->encap_hlist);
+               hash_del_rcu(&e->encap_hlist);
                kfree(e->encap_header);
                kfree(e);
        }
@@ -580,7 +582,9 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
              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))) {
+             BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) |
+             BIT(FLOW_DISSECTOR_KEY_TCP) |
+             BIT(FLOW_DISSECTOR_KEY_IP))) {
                netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
                            f->dissector->used_keys);
                return -EOPNOTSUPP;
@@ -807,6 +811,48 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                        *min_inline = MLX5_INLINE_MODE_TCP_UDP;
        }
 
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) {
+               struct flow_dissector_key_ip *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IP,
+                                                 f->key);
+               struct flow_dissector_key_ip *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_IP,
+                                                 f->mask);
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, mask->tos & 0x3);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, key->tos & 0x3);
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, mask->tos >> 2);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, key->tos  >> 2);
+
+               if (mask->tos)
+                       *min_inline = MLX5_INLINE_MODE_IP;
+
+               if (mask->ttl) /* currently not supported */
+                       return -EOPNOTSUPP;
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP)) {
+               struct flow_dissector_key_tcp *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_TCP,
+                                                 f->key);
+               struct flow_dissector_key_tcp *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_TCP,
+                                                 f->mask);
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_flags,
+                        ntohs(mask->flags));
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, tcp_flags,
+                        ntohs(key->flags));
+
+               if (mask->flags)
+                       *min_inline = MLX5_INLINE_MODE_TCP_UDP;
+       }
+
        return 0;
 }
 
@@ -925,11 +971,11 @@ static int offload_pedit_fields(struct pedit_headers *masks,
                                struct mlx5e_tc_flow_parse_attr *parse_attr)
 {
        struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals;
-       int i, action_size, nactions, max_actions, first, last;
+       int i, action_size, nactions, max_actions, first, last, first_z;
        void *s_masks_p, *a_masks_p, *vals_p;
-       u32 s_mask, a_mask, val;
        struct mlx5_fields *f;
        u8 cmd, field_bsize;
+       u32 s_mask, a_mask;
        unsigned long mask;
        void *action;
 
@@ -946,7 +992,8 @@ static int offload_pedit_fields(struct pedit_headers *masks,
        for (i = 0; i < ARRAY_SIZE(fields); i++) {
                f = &fields[i];
                /* avoid seeing bits set from previous iterations */
-               s_mask = a_mask = mask = val = 0;
+               s_mask = 0;
+               a_mask = 0;
 
                s_masks_p = (void *)set_masks + f->offset;
                a_masks_p = (void *)add_masks + f->offset;
@@ -981,12 +1028,12 @@ static int offload_pedit_fields(struct pedit_headers *masks,
                        memset(a_masks_p, 0, f->size);
                }
 
-               memcpy(&val, vals_p, f->size);
-
                field_bsize = f->size * BITS_PER_BYTE;
+
+               first_z = find_first_zero_bit(&mask, field_bsize);
                first = find_first_bit(&mask, field_bsize);
                last  = find_last_bit(&mask, field_bsize);
-               if (first > 0 || last != (field_bsize - 1)) {
+               if (first > 0 || last != (field_bsize - 1) || first_z < last) {
                        printk(KERN_WARNING "mlx5: partial rewrite (mask %lx) is currently not offloaded\n",
                               mask);
                        return -EOPNOTSUPP;
@@ -1002,11 +1049,11 @@ static int offload_pedit_fields(struct pedit_headers *masks,
                }
 
                if (field_bsize == 32)
-                       MLX5_SET(set_action_in, action, data, ntohl(val));
+                       MLX5_SET(set_action_in, action, data, ntohl(*(__be32 *)vals_p));
                else if (field_bsize == 16)
-                       MLX5_SET(set_action_in, action, data, ntohs(val));
+                       MLX5_SET(set_action_in, action, data, ntohs(*(__be16 *)vals_p));
                else if (field_bsize == 8)
-                       MLX5_SET(set_action_in, action, data, val);
+                       MLX5_SET(set_action_in, action, data, *(u8 *)vals_p);
 
                action += action_size;
                nactions++;
@@ -1109,6 +1156,28 @@ out_err:
        return err;
 }
 
+static bool csum_offload_supported(struct mlx5e_priv *priv, u32 action, u32 update_flags)
+{
+       u32 prot_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR | TCA_CSUM_UPDATE_FLAG_TCP |
+                        TCA_CSUM_UPDATE_FLAG_UDP;
+
+       /*  The HW recalcs checksums only if re-writing headers */
+       if (!(action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)) {
+               netdev_warn(priv->netdev,
+                           "TC csum action is only offloaded with pedit\n");
+               return false;
+       }
+
+       if (update_flags & ~prot_flags) {
+               netdev_warn(priv->netdev,
+                           "can't offload TC csum action for some header/s - flags %#x\n",
+                           update_flags);
+               return false;
+       }
+
+       return true;
+}
+
 static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                struct mlx5e_tc_flow_parse_attr *parse_attr,
                                struct mlx5e_tc_flow *flow)
@@ -1126,10 +1195,6 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
 
        tcf_exts_to_list(exts, &actions);
        list_for_each_entry(a, &actions, list) {
-               /* Only support a single action per rule */
-               if (attr->action)
-                       return -EINVAL;
-
                if (is_tcf_gact_shot(a)) {
                        attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
                        if (MLX5_CAP_FLOWTABLE(priv->mdev,
@@ -1149,6 +1214,14 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        continue;
                }
 
+               if (is_tcf_csum(a)) {
+                       if (csum_offload_supported(priv, attr->action,
+                                                  tcf_csum_update_flags(a)))
+                               continue;
+
+                       return -EOPNOTSUPP;
+               }
+
                if (is_tcf_skbedit_mark(a)) {
                        u32 mark = tcf_skbedit_mark(a);
 
@@ -1404,8 +1477,8 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv,
 
        if (!(nud_state & NUD_VALID)) {
                neigh_event_send(n, NULL);
-               neigh_release(n);
-               return -EAGAIN;
+               err = -EAGAIN;
+               goto out;
        }
 
        err = mlx5_encap_alloc(priv->mdev, e->tunnel_type,
@@ -1510,8 +1583,8 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv,
 
        if (!(nud_state & NUD_VALID)) {
                neigh_event_send(n, NULL);
-               neigh_release(n);
-               return -EAGAIN;
+               err = -EAGAIN;
+               goto out;
        }
 
        err = mlx5_encap_alloc(priv->mdev, e->tunnel_type,
@@ -1651,6 +1724,14 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        continue;
                }
 
+               if (is_tcf_csum(a)) {
+                       if (csum_offload_supported(priv, attr->action,
+                                                  tcf_csum_update_flags(a)))
+                               continue;
+
+                       return -EOPNOTSUPP;
+               }
+
                if (is_tcf_mirred_egress_redirect(a)) {
                        int ifindex = tcf_mirred_ifindex(a);
                        struct net_device *out_dev, *encap_dev = NULL;
@@ -1738,7 +1819,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
        }
 
        flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL);
-       parse_attr = mlx5_vzalloc(sizeof(*parse_attr));
+       parse_attr = kvzalloc(sizeof(*parse_attr), GFP_KERNEL);
        if (!parse_attr || !flow) {
                err = -ENOMEM;
                goto err_free;
@@ -1823,9 +1904,7 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
 {
        struct mlx5e_tc_table *tc = &priv->fs.tc;
        struct mlx5e_tc_flow *flow;
-       struct tc_action *a;
        struct mlx5_fc *counter;
-       LIST_HEAD(actions);
        u64 bytes;
        u64 packets;
        u64 lastuse;
@@ -1844,13 +1923,7 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
 
        mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
 
-       preempt_disable();
-
-       tcf_exts_to_list(f->exts, &actions);
-       list_for_each_entry(a, &actions, list)
-               tcf_action_stats_update(a, bytes, packets, lastuse);
-
-       preempt_enable();
+       tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
 
        return 0;
 }