mlxsw: spectrum_acl: Add support for mirror action
authorArkadi Sharshevsky <arkadis@mellanox.com>
Fri, 19 Jan 2018 08:24:52 +0000 (09:24 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 21 Jan 2018 23:21:30 +0000 (18:21 -0500)
Add support for mirror action. Only one mirror action can be set per rule.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c

index 51ac406..b698fb4 100644 (file)
@@ -834,8 +834,68 @@ int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
 }
 EXPORT_SYMBOL(mlxsw_afa_block_append_trap_and_forward);
 
-int mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
-                                 u8 mirror_agent)
+struct mlxsw_afa_mirror {
+       struct mlxsw_afa_resource resource;
+       int span_id;
+       u8 local_in_port;
+       u8 local_out_port;
+       bool ingress;
+};
+
+static void
+mlxsw_afa_mirror_destroy(struct mlxsw_afa_block *block,
+                        struct mlxsw_afa_mirror *mirror)
+{
+       block->afa->ops->mirror_del(block->afa->ops_priv,
+                                   mirror->local_in_port,
+                                   mirror->local_out_port,
+                                   mirror->ingress);
+       kfree(mirror);
+}
+
+static void
+mlxsw_afa_mirror_destructor(struct mlxsw_afa_block *block,
+                           struct mlxsw_afa_resource *resource)
+{
+       struct mlxsw_afa_mirror *mirror;
+
+       mirror = container_of(resource, struct mlxsw_afa_mirror, resource);
+       mlxsw_afa_mirror_destroy(block, mirror);
+}
+
+static struct mlxsw_afa_mirror *
+mlxsw_afa_mirror_create(struct mlxsw_afa_block *block,
+                       u8 local_in_port, u8 local_out_port,
+                       bool ingress)
+{
+       struct mlxsw_afa_mirror *mirror;
+       int err;
+
+       mirror = kzalloc(sizeof(*mirror), GFP_KERNEL);
+       if (!mirror)
+               return ERR_PTR(-ENOMEM);
+
+       err = block->afa->ops->mirror_add(block->afa->ops_priv,
+                                         local_in_port, local_out_port,
+                                         ingress, &mirror->span_id);
+       if (err)
+               goto err_mirror_add;
+
+       mirror->ingress = ingress;
+       mirror->local_out_port = local_out_port;
+       mirror->local_in_port = local_in_port;
+       mirror->resource.destructor = mlxsw_afa_mirror_destructor;
+       mlxsw_afa_resource_add(block, &mirror->resource);
+       return mirror;
+
+err_mirror_add:
+       kfree(mirror);
+       return ERR_PTR(err);
+}
+
+static int
+mlxsw_afa_block_append_allocated_mirror(struct mlxsw_afa_block *block,
+                                       u8 mirror_agent)
 {
        char *act = mlxsw_afa_block_append_action(block,
                                                  MLXSW_AFA_TRAPDISC_CODE,
@@ -847,6 +907,29 @@ int mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
        mlxsw_afa_trapdisc_mirror_pack(act, true, mirror_agent);
        return 0;
 }
+
+int
+mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
+                             u8 local_in_port, u8 local_out_port, bool ingress)
+{
+       struct mlxsw_afa_mirror *mirror;
+       int err;
+
+       mirror = mlxsw_afa_mirror_create(block, local_in_port, local_out_port,
+                                        ingress);
+       if (IS_ERR(mirror))
+               return PTR_ERR(mirror);
+
+       err = mlxsw_afa_block_append_allocated_mirror(block, mirror->span_id);
+       if (err)
+               goto err_append_allocated_mirror;
+
+       return 0;
+
+err_append_allocated_mirror:
+       mlxsw_afa_mirror_destroy(block, mirror);
+       return err;
+}
 EXPORT_SYMBOL(mlxsw_afa_block_append_mirror);
 
 /* Forwarding Action
index ad48dda..4313229 100644 (file)
@@ -70,7 +70,8 @@ int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id);
 int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
                                            u16 trap_id);
 int mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
-                                 u8 mirror_agent);
+                                 u8 local_in_port, u8 local_out_port,
+                                 bool ingress);
 int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block,
                               u8 local_port, bool in_port);
 int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block,
index 00c14b1..bdd8f94 100644 (file)
@@ -554,6 +554,10 @@ int mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
                                u16 group_id);
 int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei);
 int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei);
+int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_rule_info *rulei,
+                                 struct mlxsw_sp_acl_block *block,
+                                 struct net_device *out_dev);
 int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_rule_info *rulei,
                               struct net_device *out_dev);
index fac7bf5..0897a54 100644 (file)
@@ -566,6 +566,34 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
                                          local_port, in_port);
 }
 
+int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_rule_info *rulei,
+                                 struct mlxsw_sp_acl_block *block,
+                                 struct net_device *out_dev)
+{
+       struct mlxsw_sp_acl_block_binding *binding;
+       struct mlxsw_sp_port *out_port;
+       struct mlxsw_sp_port *in_port;
+
+       if (!list_is_singular(&block->binding_list))
+               return -EOPNOTSUPP;
+
+       binding = list_first_entry(&block->binding_list,
+                                  struct mlxsw_sp_acl_block_binding, list);
+       in_port = binding->mlxsw_sp_port;
+       if (!mlxsw_sp_port_dev_check(out_dev))
+               return -EINVAL;
+
+       out_port = netdev_priv(out_dev);
+       if (out_port->mlxsw_sp != mlxsw_sp)
+               return -EINVAL;
+
+       return mlxsw_afa_block_append_mirror(rulei->act_block,
+                                            in_port->local_port,
+                                            out_port->local_port,
+                                            binding->ingress);
+}
+
 int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_rule_info *rulei,
                                u32 action, u16 vid, u16 proto, u8 prio)
index cf7b97d..6ce00e2 100644 (file)
@@ -108,6 +108,13 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                                                         out_dev);
                        if (err)
                                return err;
+               } else if (is_tcf_mirred_egress_mirror(a)) {
+                       struct net_device *out_dev = tcf_mirred_dev(a);
+
+                       err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
+                                                           block, out_dev);
+                       if (err)
+                               return err;
                } else if (is_tcf_vlan(a)) {
                        u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
                        u32 action = tcf_vlan_action(a);