net/mlx5e: Split offloaded eswitch TC rules for port mirroring
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_tc.c
index 302c5500f9addfcb2dec9a2911df3342e6a6f861..9372d914abe5cdafdbdf79acb80940eb73ca83c9 100644 (file)
@@ -75,12 +75,14 @@ enum {
        MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(MLX5E_TC_FLOW_BASE + 4),
 };
 
+#define MLX5E_TC_MAX_SPLITS 1
+
 struct mlx5e_tc_flow {
        struct rhash_head       node;
        struct mlx5e_priv       *priv;
        u64                     cookie;
        u8                      flags;
-       struct mlx5_flow_handle *rule;
+       struct mlx5_flow_handle *rule[MLX5E_TC_MAX_SPLITS + 1];
        struct list_head        encap;   /* flows sharing the same encap ID */
        struct list_head        mod_hdr; /* flows sharing the same mod hdr ID */
        struct list_head        hairpin; /* flows sharing the same hairpin */
@@ -794,8 +796,8 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
        struct mlx5_nic_flow_attr *attr = flow->nic_attr;
        struct mlx5_fc *counter = NULL;
 
-       counter = mlx5_flow_rule_counter(flow->rule);
-       mlx5_del_flow_rules(flow->rule);
+       counter = mlx5_flow_rule_counter(flow->rule[0]);
+       mlx5_del_flow_rules(flow->rule[0]);
        mlx5_fc_destroy(priv->mdev, counter);
 
        if (!mlx5e_tc_num_filters(priv) && priv->fs.tc.t) {
@@ -870,9 +872,18 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
                rule = mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr);
                if (IS_ERR(rule))
                        goto err_add_rule;
+
+               if (attr->mirror_count) {
+                       flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, &parse_attr->spec, attr);
+                       if (IS_ERR(flow->rule[1]))
+                               goto err_fwd_rule;
+               }
        }
        return rule;
 
+err_fwd_rule:
+       mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
+       rule = flow->rule[1];
 err_add_rule:
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                mlx5e_detach_mod_hdr(priv, flow);
@@ -893,7 +904,9 @@ 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, attr);
+               if (attr->mirror_count)
+                       mlx5_eswitch_del_offloaded_rule(esw, flow->rule[1], attr);
+               mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
        }
 
        mlx5_eswitch_del_vlan_action(esw, attr);
@@ -929,13 +942,25 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
        list_for_each_entry(flow, &e->flows, encap) {
                esw_attr = flow->esw_attr;
                esw_attr->encap_id = e->encap_id;
-               flow->rule = mlx5_eswitch_add_offloaded_rule(esw, &esw_attr->parse_attr->spec, esw_attr);
-               if (IS_ERR(flow->rule)) {
-                       err = PTR_ERR(flow->rule);
+               flow->rule[0] = mlx5_eswitch_add_offloaded_rule(esw, &esw_attr->parse_attr->spec, esw_attr);
+               if (IS_ERR(flow->rule[0])) {
+                       err = PTR_ERR(flow->rule[0]);
                        mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n",
                                       err);
                        continue;
                }
+
+               if (esw_attr->mirror_count) {
+                       flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, &esw_attr->parse_attr->spec, esw_attr);
+                       if (IS_ERR(flow->rule[1])) {
+                               mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], esw_attr);
+                               err = PTR_ERR(flow->rule[1]);
+                               mlx5_core_warn(priv->mdev, "Failed to update cached mirror flow, %d\n",
+                                              err);
+                               continue;
+                       }
+               }
+
                flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
        }
 }
@@ -948,8 +973,12 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
 
        list_for_each_entry(flow, &e->flows, encap) {
                if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
+                       struct mlx5_esw_flow_attr *attr = flow->esw_attr;
+
                        flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED;
-                       mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr);
+                       if (attr->mirror_count)
+                               mlx5_eswitch_del_offloaded_rule(esw, flow->rule[1], attr);
+                       mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
                }
        }
 
@@ -984,7 +1013,7 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
                        continue;
                list_for_each_entry(flow, &e->flows, encap) {
                        if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
-                               counter = mlx5_flow_rule_counter(flow->rule);
+                               counter = mlx5_flow_rule_counter(flow->rule[0]);
                                mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
                                if (time_after((unsigned long)lastuse, nhe->reported_lastuse)) {
                                        neigh_used = true;
@@ -2714,16 +2743,16 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
                err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow);
                if (err < 0)
                        goto err_free;
-               flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow);
+               flow->rule[0] = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow);
        } else {
                err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow);
                if (err < 0)
                        goto err_free;
-               flow->rule = mlx5e_tc_add_nic_flow(priv, parse_attr, flow);
+               flow->rule[0] = mlx5e_tc_add_nic_flow(priv, parse_attr, flow);
        }
 
-       if (IS_ERR(flow->rule)) {
-               err = PTR_ERR(flow->rule);
+       if (IS_ERR(flow->rule[0])) {
+               err = PTR_ERR(flow->rule[0]);
                if (err != -EAGAIN)
                        goto err_free;
        }
@@ -2796,7 +2825,7 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
        if (!(flow->flags & MLX5E_TC_FLOW_OFFLOADED))
                return 0;
 
-       counter = mlx5_flow_rule_counter(flow->rule);
+       counter = mlx5_flow_rule_counter(flow->rule[0]);
        if (!counter)
                return 0;