mlxsw: core_acl_flex_actions: Add police action
authorIdo Schimmel <idosch@mellanox.com>
Wed, 15 Jul 2020 08:27:28 +0000 (11:27 +0300)
committerJakub Kicinski <kuba@kernel.org>
Thu, 16 Jul 2020 01:10:00 +0000 (18:10 -0700)
Add core functionality required to support police action in the policy
engine.

The utilized hardware policers are stored in a hash table keyed by the
flow action index. This allows to support policer sharing between
multiple ACL rules.

Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h

index 06a4391..4d699fe 100644 (file)
@@ -67,7 +67,9 @@ struct mlxsw_afa {
        struct rhashtable set_ht;
        struct rhashtable fwd_entry_ht;
        struct rhashtable cookie_ht;
+       struct rhashtable policer_ht;
        struct idr cookie_idr;
+       struct list_head policer_list;
 };
 
 #define MLXSW_AFA_SET_LEN 0xA8
@@ -177,6 +179,21 @@ static const struct rhashtable_params mlxsw_afa_cookie_ht_params = {
        .automatic_shrinking = true,
 };
 
+struct mlxsw_afa_policer {
+       struct rhash_head ht_node;
+       struct list_head list; /* Member of policer_list */
+       refcount_t ref_count;
+       u32 fa_index;
+       u16 policer_index;
+};
+
+static const struct rhashtable_params mlxsw_afa_policer_ht_params = {
+       .key_len = sizeof(u32),
+       .key_offset = offsetof(struct mlxsw_afa_policer, fa_index),
+       .head_offset = offsetof(struct mlxsw_afa_policer, ht_node),
+       .automatic_shrinking = true,
+};
+
 struct mlxsw_afa *mlxsw_afa_create(unsigned int max_acts_per_set,
                                   const struct mlxsw_afa_ops *ops,
                                   void *ops_priv)
@@ -198,12 +215,19 @@ struct mlxsw_afa *mlxsw_afa_create(unsigned int max_acts_per_set,
                              &mlxsw_afa_cookie_ht_params);
        if (err)
                goto err_cookie_rhashtable_init;
+       err = rhashtable_init(&mlxsw_afa->policer_ht,
+                             &mlxsw_afa_policer_ht_params);
+       if (err)
+               goto err_policer_rhashtable_init;
        idr_init(&mlxsw_afa->cookie_idr);
+       INIT_LIST_HEAD(&mlxsw_afa->policer_list);
        mlxsw_afa->max_acts_per_set = max_acts_per_set;
        mlxsw_afa->ops = ops;
        mlxsw_afa->ops_priv = ops_priv;
        return mlxsw_afa;
 
+err_policer_rhashtable_init:
+       rhashtable_destroy(&mlxsw_afa->cookie_ht);
 err_cookie_rhashtable_init:
        rhashtable_destroy(&mlxsw_afa->fwd_entry_ht);
 err_fwd_entry_rhashtable_init:
@@ -216,8 +240,10 @@ EXPORT_SYMBOL(mlxsw_afa_create);
 
 void mlxsw_afa_destroy(struct mlxsw_afa *mlxsw_afa)
 {
+       WARN_ON(!list_empty(&mlxsw_afa->policer_list));
        WARN_ON(!idr_is_empty(&mlxsw_afa->cookie_idr));
        idr_destroy(&mlxsw_afa->cookie_idr);
+       rhashtable_destroy(&mlxsw_afa->policer_ht);
        rhashtable_destroy(&mlxsw_afa->cookie_ht);
        rhashtable_destroy(&mlxsw_afa->fwd_entry_ht);
        rhashtable_destroy(&mlxsw_afa->set_ht);
@@ -838,6 +864,137 @@ err_cookie_get:
        return ERR_PTR(err);
 }
 
+static struct mlxsw_afa_policer *
+mlxsw_afa_policer_create(struct mlxsw_afa *mlxsw_afa, u32 fa_index,
+                        u64 rate_bytes_ps, u32 burst,
+                        struct netlink_ext_ack *extack)
+{
+       struct mlxsw_afa_policer *policer;
+       int err;
+
+       policer = kzalloc(sizeof(*policer), GFP_KERNEL);
+       if (!policer)
+               return ERR_PTR(-ENOMEM);
+
+       err = mlxsw_afa->ops->policer_add(mlxsw_afa->ops_priv, rate_bytes_ps,
+                                         burst, &policer->policer_index,
+                                         extack);
+       if (err)
+               goto err_policer_add;
+
+       refcount_set(&policer->ref_count, 1);
+       policer->fa_index = fa_index;
+
+       err = rhashtable_insert_fast(&mlxsw_afa->policer_ht, &policer->ht_node,
+                                    mlxsw_afa_policer_ht_params);
+       if (err)
+               goto err_rhashtable_insert;
+
+       list_add_tail(&policer->list, &mlxsw_afa->policer_list);
+
+       return policer;
+
+err_rhashtable_insert:
+       mlxsw_afa->ops->policer_del(mlxsw_afa->ops_priv,
+                                   policer->policer_index);
+err_policer_add:
+       kfree(policer);
+       return ERR_PTR(err);
+}
+
+static void mlxsw_afa_policer_destroy(struct mlxsw_afa *mlxsw_afa,
+                                     struct mlxsw_afa_policer *policer)
+{
+       list_del(&policer->list);
+       rhashtable_remove_fast(&mlxsw_afa->policer_ht, &policer->ht_node,
+                              mlxsw_afa_policer_ht_params);
+       mlxsw_afa->ops->policer_del(mlxsw_afa->ops_priv,
+                                   policer->policer_index);
+       kfree(policer);
+}
+
+static struct mlxsw_afa_policer *
+mlxsw_afa_policer_get(struct mlxsw_afa *mlxsw_afa, u32 fa_index,
+                     u64 rate_bytes_ps, u32 burst,
+                     struct netlink_ext_ack *extack)
+{
+       struct mlxsw_afa_policer *policer;
+
+       policer = rhashtable_lookup_fast(&mlxsw_afa->policer_ht, &fa_index,
+                                        mlxsw_afa_policer_ht_params);
+       if (policer) {
+               refcount_inc(&policer->ref_count);
+               return policer;
+       }
+
+       return mlxsw_afa_policer_create(mlxsw_afa, fa_index, rate_bytes_ps,
+                                       burst, extack);
+}
+
+static void mlxsw_afa_policer_put(struct mlxsw_afa *mlxsw_afa,
+                                 struct mlxsw_afa_policer *policer)
+{
+       if (!refcount_dec_and_test(&policer->ref_count))
+               return;
+       mlxsw_afa_policer_destroy(mlxsw_afa, policer);
+}
+
+struct mlxsw_afa_policer_ref {
+       struct mlxsw_afa_resource resource;
+       struct mlxsw_afa_policer *policer;
+};
+
+static void
+mlxsw_afa_policer_ref_destroy(struct mlxsw_afa_block *block,
+                             struct mlxsw_afa_policer_ref *policer_ref)
+{
+       mlxsw_afa_resource_del(&policer_ref->resource);
+       mlxsw_afa_policer_put(block->afa, policer_ref->policer);
+       kfree(policer_ref);
+}
+
+static void
+mlxsw_afa_policer_ref_destructor(struct mlxsw_afa_block *block,
+                                struct mlxsw_afa_resource *resource)
+{
+       struct mlxsw_afa_policer_ref *policer_ref;
+
+       policer_ref = container_of(resource, struct mlxsw_afa_policer_ref,
+                                  resource);
+       mlxsw_afa_policer_ref_destroy(block, policer_ref);
+}
+
+static struct mlxsw_afa_policer_ref *
+mlxsw_afa_policer_ref_create(struct mlxsw_afa_block *block, u32 fa_index,
+                            u64 rate_bytes_ps, u32 burst,
+                            struct netlink_ext_ack *extack)
+{
+       struct mlxsw_afa_policer_ref *policer_ref;
+       struct mlxsw_afa_policer *policer;
+       int err;
+
+       policer_ref = kzalloc(sizeof(*policer_ref), GFP_KERNEL);
+       if (!policer_ref)
+               return ERR_PTR(-ENOMEM);
+
+       policer = mlxsw_afa_policer_get(block->afa, fa_index, rate_bytes_ps,
+                                       burst, extack);
+       if (IS_ERR(policer)) {
+               err = PTR_ERR(policer);
+               goto err_policer_get;
+       }
+
+       policer_ref->policer = policer;
+       policer_ref->resource.destructor = mlxsw_afa_policer_ref_destructor;
+       mlxsw_afa_resource_add(block, &policer_ref->resource);
+
+       return policer_ref;
+
+err_policer_get:
+       kfree(policer_ref);
+       return ERR_PTR(err);
+}
+
 #define MLXSW_AFA_ONE_ACTION_LEN 32
 #define MLXSW_AFA_PAYLOAD_OFFSET 4
 
@@ -1551,6 +1708,19 @@ EXPORT_SYMBOL(mlxsw_afa_block_append_fwd);
 #define MLXSW_AFA_POLCNT_CODE 0x08
 #define MLXSW_AFA_POLCNT_SIZE 1
 
+enum {
+       MLXSW_AFA_POLCNT_COUNTER,
+       MLXSW_AFA_POLCNT_POLICER,
+};
+
+/* afa_polcnt_c_p
+ * Counter or policer.
+ * Indicates whether the action binds a policer or a counter to the flow.
+ * 0: Counter
+ * 1: Policer
+ */
+MLXSW_ITEM32(afa, polcnt, c_p, 0x00, 31, 1);
+
 enum mlxsw_afa_polcnt_counter_set_type {
        /* No count */
        MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_NO_COUNT = 0x00,
@@ -1570,15 +1740,28 @@ MLXSW_ITEM32(afa, polcnt, counter_set_type, 0x04, 24, 8);
  */
 MLXSW_ITEM32(afa, polcnt, counter_index, 0x04, 0, 24);
 
+/* afa_polcnt_pid
+ * Policer ID.
+ * Reserved when c_p = 0
+ */
+MLXSW_ITEM32(afa, polcnt, pid, 0x08, 0, 14);
+
 static inline void
 mlxsw_afa_polcnt_pack(char *payload,
                      enum mlxsw_afa_polcnt_counter_set_type set_type,
                      u32 counter_index)
 {
+       mlxsw_afa_polcnt_c_p_set(payload, MLXSW_AFA_POLCNT_COUNTER);
        mlxsw_afa_polcnt_counter_set_type_set(payload, set_type);
        mlxsw_afa_polcnt_counter_index_set(payload, counter_index);
 }
 
+static void mlxsw_afa_polcnt_policer_pack(char *payload, u16 policer_index)
+{
+       mlxsw_afa_polcnt_c_p_set(payload, MLXSW_AFA_POLCNT_POLICER);
+       mlxsw_afa_polcnt_pid_set(payload, policer_index);
+}
+
 int mlxsw_afa_block_append_allocated_counter(struct mlxsw_afa_block *block,
                                             u32 counter_index)
 {
@@ -1622,6 +1805,40 @@ err_append_allocated_counter:
 }
 EXPORT_SYMBOL(mlxsw_afa_block_append_counter);
 
+int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
+                                 u32 fa_index, u64 rate_bytes_ps, u32 burst,
+                                 u16 *p_policer_index,
+                                 struct netlink_ext_ack *extack)
+{
+       struct mlxsw_afa_policer_ref *policer_ref;
+       char *act;
+       int err;
+
+       policer_ref = mlxsw_afa_policer_ref_create(block, fa_index,
+                                                  rate_bytes_ps,
+                                                  burst, extack);
+       if (IS_ERR(policer_ref))
+               return PTR_ERR(policer_ref);
+       *p_policer_index = policer_ref->policer->policer_index;
+
+       act = mlxsw_afa_block_append_action_ext(block, MLXSW_AFA_POLCNT_CODE,
+                                               MLXSW_AFA_POLCNT_SIZE,
+                                               MLXSW_AFA_ACTION_TYPE_POLICE);
+       if (IS_ERR(act)) {
+               NL_SET_ERR_MSG_MOD(extack, "Cannot append police action");
+               err = PTR_ERR(act);
+               goto err_append_action;
+       }
+       mlxsw_afa_polcnt_policer_pack(act, *p_policer_index);
+
+       return 0;
+
+err_append_action:
+       mlxsw_afa_policer_ref_destroy(block, policer_ref);
+       return err;
+}
+EXPORT_SYMBOL(mlxsw_afa_block_append_police);
+
 /* Virtual Router and Forwarding Domain Action
  * -------------------------------------------
  * Virtual Switch action is used for manipulate the Virtual Router (VR),
index a723503..b652497 100644 (file)
@@ -26,6 +26,10 @@ struct mlxsw_afa_ops {
                          bool ingress, int *p_span_id);
        void (*mirror_del)(void *priv, u8 local_in_port, int span_id,
                           bool ingress);
+       int (*policer_add)(void *priv, u64 rate_bytes_ps, u32 burst,
+                          u16 *p_policer_index,
+                          struct netlink_ext_ack *extack);
+       void (*policer_del)(void *priv, u16 policer_index);
        bool dummy_first_set;
 };
 
@@ -84,5 +88,9 @@ int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block,
                                    bool rmid_valid, u32 kvdl_index);
 int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port,
                                  struct netlink_ext_ack *extack);
+int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
+                                 u32 fa_index, u64 rate_bytes_ps, u32 burst,
+                                 u16 *p_policer_index,
+                                 struct netlink_ext_ack *extack);
 
 #endif