net/mlx5e: TC, Add sampler object API
authorChris Mi <cmi@nvidia.com>
Mon, 21 Sep 2020 05:25:54 +0000 (13:25 +0800)
committerSaeed Mahameed <saeedm@nvidia.com>
Wed, 7 Apr 2021 04:36:03 +0000 (21:36 -0700)
In order to offload sample action, HW introduces sampler object. The
sampler object samples packets according to the provided sample ratio.
Sampled packets are duplicated. One copy is processed by a termination
table, named the sample table, which sends the packet up to software.
The second copy is processed by the default table.

Instantiate sampler object. Re-use identical sampler object for
the same sample ratio, sample table and default table as a prestep for
offloading tc sample actions.

Signed-off-by: Chris Mi <cmi@nvidia.com>
Reviewed-by: Oz Shlomo <ozsh@nvidia.com>
Reviewed-by: Mark Bloch <mbloch@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/esw/sample.c

index 9bd996e..37e3367 100644 (file)
@@ -3,11 +3,28 @@
 
 #include "esw/sample.h"
 #include "eswitch.h"
+#include "en_tc.h"
+#include "fs_core.h"
 
 struct mlx5_esw_psample {
        struct mlx5e_priv *priv;
        struct mlx5_flow_table *termtbl;
        struct mlx5_flow_handle *termtbl_rule;
+       DECLARE_HASHTABLE(hashtbl, 8);
+       struct mutex ht_lock; /* protect hashtbl */
+};
+
+struct mlx5_sampler {
+       struct hlist_node hlist;
+       u32 sampler_id;
+       u32 sample_ratio;
+       u32 sample_table_id;
+       u32 default_table_id;
+       int count;
+};
+
+struct mlx5_sample_flow {
+       struct mlx5_sampler *sampler;
 };
 
 static int
@@ -64,6 +81,117 @@ sampler_termtbl_destroy(struct mlx5_esw_psample *esw_psample)
        mlx5_destroy_flow_table(esw_psample->termtbl);
 }
 
+static int
+sampler_obj_create(struct mlx5_core_dev *mdev, struct mlx5_sampler *sampler)
+{
+       u32 in[MLX5_ST_SZ_DW(create_sampler_obj_in)] = {};
+       u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
+       u64 general_obj_types;
+       void *obj;
+       int err;
+
+       general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types);
+       if (!(general_obj_types & MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_SAMPLER))
+               return -EOPNOTSUPP;
+       if (!MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, ignore_flow_level))
+               return -EOPNOTSUPP;
+
+       obj = MLX5_ADDR_OF(create_sampler_obj_in, in, sampler_object);
+       MLX5_SET(sampler_obj, obj, table_type, FS_FT_FDB);
+       MLX5_SET(sampler_obj, obj, ignore_flow_level, 1);
+       MLX5_SET(sampler_obj, obj, level, 1);
+       MLX5_SET(sampler_obj, obj, sample_ratio, sampler->sample_ratio);
+       MLX5_SET(sampler_obj, obj, sample_table_id, sampler->sample_table_id);
+       MLX5_SET(sampler_obj, obj, default_table_id, sampler->default_table_id);
+       MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
+       MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
+
+       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       if (!err)
+               sampler->sampler_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
+
+       return err;
+}
+
+static void
+sampler_obj_destroy(struct mlx5_core_dev *mdev, u32 sampler_id)
+{
+       u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
+       u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
+
+       MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
+       MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
+       MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sampler_id);
+
+       mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+}
+
+static u32
+sampler_hash(u32 sample_ratio, u32 default_table_id)
+{
+       return jhash_2words(sample_ratio, default_table_id, 0);
+}
+
+static int
+sampler_cmp(u32 sample_ratio1, u32 default_table_id1, u32 sample_ratio2, u32 default_table_id2)
+{
+       return sample_ratio1 != sample_ratio2 || default_table_id1 != default_table_id2;
+}
+
+static struct mlx5_sampler *
+sampler_get(struct mlx5_esw_psample *esw_psample, u32 sample_ratio, u32 default_table_id)
+{
+       struct mlx5_sampler *sampler;
+       u32 hash_key;
+       int err;
+
+       mutex_lock(&esw_psample->ht_lock);
+       hash_key = sampler_hash(sample_ratio, default_table_id);
+       hash_for_each_possible(esw_psample->hashtbl, sampler, hlist, hash_key)
+               if (!sampler_cmp(sampler->sample_ratio, sampler->default_table_id,
+                                sample_ratio, default_table_id))
+                       goto add_ref;
+
+       sampler = kzalloc(sizeof(*sampler), GFP_KERNEL);
+       if (!sampler) {
+               err = -ENOMEM;
+               goto err_alloc;
+       }
+
+       sampler->sample_table_id = esw_psample->termtbl->id;
+       sampler->default_table_id = default_table_id;
+       sampler->sample_ratio = sample_ratio;
+
+       err = sampler_obj_create(esw_psample->priv->mdev, sampler);
+       if (err)
+               goto err_create;
+
+       hash_add(esw_psample->hashtbl, &sampler->hlist, hash_key);
+
+add_ref:
+       sampler->count++;
+       mutex_unlock(&esw_psample->ht_lock);
+       return sampler;
+
+err_create:
+       kfree(sampler);
+err_alloc:
+       mutex_unlock(&esw_psample->ht_lock);
+       return ERR_PTR(err);
+}
+
+static void
+sampler_put(struct mlx5_esw_psample *esw_psample, struct mlx5_sampler *sampler)
+{
+       mutex_lock(&esw_psample->ht_lock);
+       if (--sampler->count == 0) {
+               hash_del(&sampler->hlist);
+               sampler_obj_destroy(esw_psample->priv->mdev, sampler->sampler_id);
+               kfree(sampler);
+       }
+       mutex_unlock(&esw_psample->ht_lock);
+}
+
 struct mlx5_esw_psample *
 mlx5_esw_sample_init(struct mlx5e_priv *priv)
 {
@@ -78,6 +206,8 @@ mlx5_esw_sample_init(struct mlx5e_priv *priv)
        if (err)
                goto err_termtbl;
 
+       mutex_init(&esw_psample->ht_lock);
+
        return esw_psample;
 
 err_termtbl:
@@ -91,6 +221,7 @@ mlx5_esw_sample_cleanup(struct mlx5_esw_psample *esw_psample)
        if (IS_ERR_OR_NULL(esw_psample))
                return;
 
+       mutex_destroy(&esw_psample->ht_lock);
        sampler_termtbl_destroy(esw_psample);
        kfree(esw_psample);
 }