net/mlx5_core: Flow counters infrastructure
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_core.c
index cfb35c3..8b5f0b2 100644 (file)
 #define BY_PASS_MIN_LEVEL (KERNEL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
                           LEFTOVERS_NUM_PRIOS)
 
-#define KERNEL_NUM_LEVELS 3
-#define KERNEL_NUM_PRIOS 2
-#define KERNEL_MIN_LEVEL 2
+/* Vlan, mac, ttc, aRFS */
+#define KERNEL_NIC_PRIO_NUM_LEVELS 4
+#define KERNEL_NIC_NUM_PRIOS 1
+/* One more level for tc */
+#define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1)
 
 #define ANCHOR_NUM_LEVELS 1
 #define ANCHOR_NUM_PRIOS 1
@@ -106,8 +108,9 @@ static struct init_tree_node {
                         ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS,
                                                  BY_PASS_PRIO_NUM_LEVELS))),
                ADD_PRIO(0, KERNEL_MIN_LEVEL, 0, {},
-                        ADD_NS(ADD_MULTIPLE_PRIO(KERNEL_NUM_PRIOS,
-                                                 KERNEL_NUM_LEVELS))),
+                        ADD_NS(ADD_MULTIPLE_PRIO(1, 1),
+                               ADD_MULTIPLE_PRIO(KERNEL_NIC_NUM_PRIOS,
+                                                 KERNEL_NIC_PRIO_NUM_LEVELS))),
                ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
                         FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
                                          FS_CAP(flow_table_properties_nic_receive.modify_root),
@@ -225,19 +228,6 @@ static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
        return NULL;
 }
 
-static unsigned int find_next_free_level(struct fs_prio *prio)
-{
-       if (!list_empty(&prio->node.children)) {
-               struct mlx5_flow_table *ft;
-
-               ft = list_last_entry(&prio->node.children,
-                                    struct mlx5_flow_table,
-                                    node.list);
-               return ft->level + 1;
-       }
-       return prio->start_level;
-}
-
 static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
 {
        unsigned int i;
@@ -354,6 +344,7 @@ static void del_rule(struct fs_node *node)
        struct mlx5_flow_group *fg;
        struct fs_fte *fte;
        u32     *match_value;
+       int modify_mask;
        struct mlx5_core_dev *dev = get_dev(node);
        int match_len = MLX5_ST_SZ_BYTES(fte_match_param);
        int err;
@@ -377,8 +368,11 @@ static void del_rule(struct fs_node *node)
        }
        if ((fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
            --fte->dests_size) {
+               modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST),
                err = mlx5_cmd_update_fte(dev, ft,
-                                         fg->id, fte);
+                                         fg->id,
+                                         modify_mask,
+                                         fte);
                if (err)
                        pr_warn("%s can't del rule fg id=%d fte_index=%d\n",
                                __func__, fg->id, fte->index);
@@ -467,7 +461,7 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in)
        return fg;
 }
 
-static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
+static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_fte,
                                                enum fs_flow_table_type table_type)
 {
        struct mlx5_flow_table *ft;
@@ -479,6 +473,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
        ft->level = level;
        ft->node.type = FS_TYPE_FLOW_TABLE;
        ft->type = table_type;
+       ft->vport = vport;
        ft->max_fte = max_fte;
        INIT_LIST_HEAD(&ft->fwd_rules);
        mutex_init(&ft->lock);
@@ -624,6 +619,7 @@ int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
        struct mlx5_flow_table *ft;
        struct mlx5_flow_group *fg;
        struct fs_fte *fte;
+       int modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST);
        int err = 0;
 
        fs_get_obj(fte, rule->node.parent);
@@ -635,7 +631,9 @@ int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
 
        memcpy(&rule->dest_attr, dest, sizeof(*dest));
        err = mlx5_cmd_update_fte(get_dev(&ft->node),
-                                 ft, fg->id, fte);
+                                 ft, fg->id,
+                                 modify_mask,
+                                 fte);
        unlock_ref_node(&fte->node);
 
        return err;
@@ -696,9 +694,23 @@ static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table
        return err;
 }
 
-struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
-                                              int prio,
-                                              int max_fte)
+static void list_add_flow_table(struct mlx5_flow_table *ft,
+                               struct fs_prio *prio)
+{
+       struct list_head *prev = &prio->node.children;
+       struct mlx5_flow_table *iter;
+
+       fs_for_each_ft(iter, prio) {
+               if (iter->level > ft->level)
+                       break;
+               prev = &iter->node.list;
+       }
+       list_add(&ft->node.list, prev);
+}
+
+static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+                                                       u16 vport, int prio,
+                                                       int max_fte, u32 level)
 {
        struct mlx5_flow_table *next_ft = NULL;
        struct mlx5_flow_table *ft;
@@ -719,12 +731,16 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                err = -EINVAL;
                goto unlock_root;
        }
-       if (fs_prio->num_ft == fs_prio->num_levels) {
+       if (level >= fs_prio->num_levels) {
                err = -ENOSPC;
                goto unlock_root;
        }
-
-       ft = alloc_flow_table(find_next_free_level(fs_prio),
+       /* The level is related to the
+        * priority level range.
+        */
+       level += fs_prio->start_level;
+       ft = alloc_flow_table(level,
+                             vport,
                              roundup_pow_of_two(max_fte),
                              root->table_type);
        if (!ft) {
@@ -735,7 +751,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
        tree_init_node(&ft->node, 1, del_flow_table);
        log_table_sz = ilog2(ft->max_fte);
        next_ft = find_next_chained_ft(fs_prio);
-       err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level,
+       err = mlx5_cmd_create_flow_table(root->dev, ft->vport, ft->type, ft->level,
                                         log_table_sz, next_ft, &ft->id);
        if (err)
                goto free_ft;
@@ -745,7 +761,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                goto destroy_ft;
        lock_ref_node(&fs_prio->node);
        tree_add_node(&ft->node, &fs_prio->node);
-       list_add_tail(&ft->node.list, &fs_prio->node.children);
+       list_add_flow_table(ft, fs_prio);
        fs_prio->num_ft++;
        unlock_ref_node(&fs_prio->node);
        mutex_unlock(&root->chain_lock);
@@ -759,17 +775,32 @@ unlock_root:
        return ERR_PTR(err);
 }
 
+struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+                                              int prio, int max_fte,
+                                              u32 level)
+{
+       return __mlx5_create_flow_table(ns, 0, prio, max_fte, level);
+}
+
+struct mlx5_flow_table *mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns,
+                                                    int prio, int max_fte,
+                                                    u32 level, u16 vport)
+{
+       return __mlx5_create_flow_table(ns, vport, prio, max_fte, level);
+}
+
 struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
                                                            int prio,
                                                            int num_flow_table_entries,
-                                                           int max_num_groups)
+                                                           int max_num_groups,
+                                                           u32 level)
 {
        struct mlx5_flow_table *ft;
 
        if (max_num_groups > num_flow_table_entries)
                return ERR_PTR(-EINVAL);
 
-       ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries);
+       ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries, level);
        if (IS_ERR(ft))
                return ft;
 
@@ -853,6 +884,7 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
 {
        struct mlx5_flow_table *ft;
        struct mlx5_flow_rule *rule;
+       int modify_mask = 0;
        int err;
 
        rule = alloc_rule(dest);
@@ -868,14 +900,20 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
                list_add(&rule->node.list, &fte->node.children);
        else
                list_add_tail(&rule->node.list, &fte->node.children);
-       if (dest)
+       if (dest) {
                fte->dests_size++;
+
+               modify_mask |= dest->type == MLX5_FLOW_DESTINATION_TYPE_COUNTER ?
+                       BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS) :
+                       BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST);
+       }
+
        if (fte->dests_size == 1 || !dest)
                err = mlx5_cmd_create_fte(get_dev(&ft->node),
                                          ft, fg->id, fte);
        else
                err = mlx5_cmd_update_fte(get_dev(&ft->node),
-                                         ft, fg->id, fte);
+                                         ft, fg->id, modify_mask, fte);
        if (err)
                goto free_rule;
 
@@ -1068,6 +1106,50 @@ unlock_fg:
        return rule;
 }
 
+struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_rule *rule)
+{
+       struct mlx5_flow_rule *dst;
+       struct fs_fte *fte;
+
+       fs_get_obj(fte, rule->node.parent);
+
+       fs_for_each_dst(dst, fte) {
+               if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
+                       return dst->dest_attr.counter;
+       }
+
+       return NULL;
+}
+
+static bool counter_is_valid(struct mlx5_fc *counter, u32 action)
+{
+       if (!(action & MLX5_FLOW_CONTEXT_ACTION_COUNT))
+               return !counter;
+
+       if (!counter)
+               return false;
+
+       /* Hardware support counter for a drop action only */
+       return action == (MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT);
+}
+
+static bool dest_is_valid(struct mlx5_flow_destination *dest,
+                         u32 action,
+                         struct mlx5_flow_table *ft)
+{
+       if (dest && (dest->type == MLX5_FLOW_DESTINATION_TYPE_COUNTER))
+               return counter_is_valid(dest->counter, action);
+
+       if (!(action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
+               return true;
+
+       if (!dest || ((dest->type ==
+           MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) &&
+           (dest->ft->level <= ft->level)))
+               return false;
+       return true;
+}
+
 static struct mlx5_flow_rule *
 _mlx5_add_flow_rule(struct mlx5_flow_table *ft,
                    u8 match_criteria_enable,
@@ -1080,7 +1162,7 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft,
        struct mlx5_flow_group *g;
        struct mlx5_flow_rule *rule;
 
-       if ((action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && !dest)
+       if (!dest_is_valid(dest, action, ft))
                return ERR_PTR(-EINVAL);
 
        nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
@@ -1297,6 +1379,16 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
                        return &dev->priv.fdb_root_ns->ns;
                else
                        return NULL;
+       case MLX5_FLOW_NAMESPACE_ESW_EGRESS:
+               if (dev->priv.esw_egress_root_ns)
+                       return &dev->priv.esw_egress_root_ns->ns;
+               else
+                       return NULL;
+       case MLX5_FLOW_NAMESPACE_ESW_INGRESS:
+               if (dev->priv.esw_ingress_root_ns)
+                       return &dev->priv.esw_ingress_root_ns->ns;
+               else
+                       return NULL;
        default:
                return NULL;
        }
@@ -1356,14 +1448,14 @@ static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio)
        return ns;
 }
 
-static int create_leaf_prios(struct mlx5_flow_namespace *ns, struct init_tree_node
-                            *prio_metadata)
+static int create_leaf_prios(struct mlx5_flow_namespace *ns, int prio,
+                            struct init_tree_node *prio_metadata)
 {
        struct fs_prio *fs_prio;
        int i;
 
        for (i = 0; i < prio_metadata->num_leaf_prios; i++) {
-               fs_prio = fs_create_prio(ns, i, prio_metadata->num_levels);
+               fs_prio = fs_create_prio(ns, prio++, prio_metadata->num_levels);
                if (IS_ERR(fs_prio))
                        return PTR_ERR(fs_prio);
        }
@@ -1390,7 +1482,7 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
                                    struct init_tree_node *init_node,
                                    struct fs_node *fs_parent_node,
                                    struct init_tree_node *init_parent_node,
-                                   int index)
+                                   int prio)
 {
        int max_ft_level = MLX5_CAP_FLOWTABLE(dev,
                                              flow_table_properties_nic_receive.
@@ -1408,8 +1500,8 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
 
                fs_get_obj(fs_ns, fs_parent_node);
                if (init_node->num_leaf_prios)
-                       return create_leaf_prios(fs_ns, init_node);
-               fs_prio = fs_create_prio(fs_ns, index, init_node->num_levels);
+                       return create_leaf_prios(fs_ns, prio, init_node);
+               fs_prio = fs_create_prio(fs_ns, prio, init_node->num_levels);
                if (IS_ERR(fs_prio))
                        return PTR_ERR(fs_prio);
                base = &fs_prio->node;
@@ -1422,11 +1514,16 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
        } else {
                return -EINVAL;
        }
+       prio = 0;
        for (i = 0; i < init_node->ar_size; i++) {
                err = init_root_tree_recursive(dev, &init_node->children[i],
-                                              base, init_node, i);
+                                              base, init_node, prio);
                if (err)
                        return err;
+               if (init_node->children[i].type == FS_TYPE_PRIO &&
+                   init_node->children[i].num_leaf_prios) {
+                       prio += init_node->children[i].num_leaf_prios;
+               }
        }
 
        return 0;
@@ -1517,6 +1614,7 @@ static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns)
 
 #define ANCHOR_PRIO 0
 #define ANCHOR_SIZE 1
+#define ANCHOR_LEVEL 0
 static int create_anchor_flow_table(struct mlx5_core_dev
                                                        *dev)
 {
@@ -1526,7 +1624,7 @@ static int create_anchor_flow_table(struct mlx5_core_dev
        ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ANCHOR);
        if (!ns)
                return -EINVAL;
-       ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE);
+       ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE, ANCHOR_LEVEL);
        if (IS_ERR(ft)) {
                mlx5_core_err(dev, "Failed to create last anchor flow table");
                return PTR_ERR(ft);
@@ -1671,6 +1769,9 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev)
 {
        cleanup_root_ns(dev);
        cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+       cleanup_single_prio_root_ns(dev, dev->priv.esw_egress_root_ns);
+       cleanup_single_prio_root_ns(dev, dev->priv.esw_ingress_root_ns);
+       mlx5_cleanup_fc_stats(dev);
 }
 
 static int init_fdb_root_ns(struct mlx5_core_dev *dev)
@@ -1691,20 +1792,69 @@ static int init_fdb_root_ns(struct mlx5_core_dev *dev)
        }
 }
 
+static int init_egress_acl_root_ns(struct mlx5_core_dev *dev)
+{
+       struct fs_prio *prio;
+
+       dev->priv.esw_egress_root_ns = create_root_ns(dev, FS_FT_ESW_EGRESS_ACL);
+       if (!dev->priv.esw_egress_root_ns)
+               return -ENOMEM;
+
+       /* create 1 prio*/
+       prio = fs_create_prio(&dev->priv.esw_egress_root_ns->ns, 0, MLX5_TOTAL_VPORTS(dev));
+       if (IS_ERR(prio))
+               return PTR_ERR(prio);
+       else
+               return 0;
+}
+
+static int init_ingress_acl_root_ns(struct mlx5_core_dev *dev)
+{
+       struct fs_prio *prio;
+
+       dev->priv.esw_ingress_root_ns = create_root_ns(dev, FS_FT_ESW_INGRESS_ACL);
+       if (!dev->priv.esw_ingress_root_ns)
+               return -ENOMEM;
+
+       /* create 1 prio*/
+       prio = fs_create_prio(&dev->priv.esw_ingress_root_ns->ns, 0, MLX5_TOTAL_VPORTS(dev));
+       if (IS_ERR(prio))
+               return PTR_ERR(prio);
+       else
+               return 0;
+}
+
 int mlx5_init_fs(struct mlx5_core_dev *dev)
 {
        int err = 0;
 
+       err = mlx5_init_fc_stats(dev);
+       if (err)
+               return err;
+
        if (MLX5_CAP_GEN(dev, nic_flow_table)) {
                err = init_root_ns(dev);
                if (err)
-                       return err;
+                       goto err;
        }
        if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
                err = init_fdb_root_ns(dev);
                if (err)
-                       cleanup_root_ns(dev);
+                       goto err;
+       }
+       if (MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support)) {
+               err = init_egress_acl_root_ns(dev);
+               if (err)
+                       goto err;
+       }
+       if (MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support)) {
+               err = init_ingress_acl_root_ns(dev);
+               if (err)
+                       goto err;
        }
 
+       return 0;
+err:
+       mlx5_cleanup_fs(dev);
        return err;
 }