| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2019 Mellanox Technologies. */ |
| |
| #include "dr_types.h" |
| |
| static int dr_table_set_miss_action_nic(struct mlx5dr_domain *dmn, |
| struct mlx5dr_table_rx_tx *nic_tbl, |
| struct mlx5dr_action *action) |
| { |
| struct mlx5dr_matcher_rx_tx *last_nic_matcher = NULL; |
| struct mlx5dr_htbl_connect_info info; |
| struct mlx5dr_ste_htbl *last_htbl; |
| struct mlx5dr_icm_chunk *chunk; |
| int ret; |
| |
| if (!list_empty(&nic_tbl->nic_matcher_list)) |
| last_nic_matcher = list_last_entry(&nic_tbl->nic_matcher_list, |
| struct mlx5dr_matcher_rx_tx, |
| list_node); |
| |
| if (last_nic_matcher) |
| last_htbl = last_nic_matcher->e_anchor; |
| else |
| last_htbl = nic_tbl->s_anchor; |
| |
| if (action) { |
| chunk = nic_tbl->nic_dmn->type == DR_DOMAIN_NIC_TYPE_RX ? |
| action->dest_tbl->tbl->rx.s_anchor->chunk : |
| action->dest_tbl->tbl->tx.s_anchor->chunk; |
| nic_tbl->default_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(chunk); |
| } else { |
| nic_tbl->default_icm_addr = nic_tbl->nic_dmn->default_icm_addr; |
| } |
| |
| info.type = CONNECT_MISS; |
| info.miss_icm_addr = nic_tbl->default_icm_addr; |
| |
| ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_tbl->nic_dmn, |
| last_htbl, &info, true); |
| if (ret) |
| mlx5dr_dbg(dmn, "Failed to set NIC RX/TX miss action, ret %d\n", ret); |
| |
| return ret; |
| } |
| |
| int mlx5dr_table_set_miss_action(struct mlx5dr_table *tbl, |
| struct mlx5dr_action *action) |
| { |
| int ret = -EOPNOTSUPP; |
| |
| if (action && action->action_type != DR_ACTION_TYP_FT) |
| return -EOPNOTSUPP; |
| |
| mlx5dr_domain_lock(tbl->dmn); |
| |
| if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX || |
| tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) { |
| ret = dr_table_set_miss_action_nic(tbl->dmn, &tbl->rx, action); |
| if (ret) |
| goto out; |
| } |
| |
| if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX || |
| tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) { |
| ret = dr_table_set_miss_action_nic(tbl->dmn, &tbl->tx, action); |
| if (ret) |
| goto out; |
| } |
| |
| if (ret) |
| goto out; |
| |
| /* Release old action */ |
| if (tbl->miss_action) |
| refcount_dec(&tbl->miss_action->refcount); |
| |
| /* Set new miss action */ |
| tbl->miss_action = action; |
| if (tbl->miss_action) |
| refcount_inc(&action->refcount); |
| |
| out: |
| mlx5dr_domain_unlock(tbl->dmn); |
| return ret; |
| } |
| |
| static void dr_table_uninit_nic(struct mlx5dr_table_rx_tx *nic_tbl) |
| { |
| mlx5dr_htbl_put(nic_tbl->s_anchor); |
| } |
| |
| static void dr_table_uninit_fdb(struct mlx5dr_table *tbl) |
| { |
| dr_table_uninit_nic(&tbl->rx); |
| dr_table_uninit_nic(&tbl->tx); |
| } |
| |
| static void dr_table_uninit(struct mlx5dr_table *tbl) |
| { |
| mlx5dr_domain_lock(tbl->dmn); |
| |
| switch (tbl->dmn->type) { |
| case MLX5DR_DOMAIN_TYPE_NIC_RX: |
| dr_table_uninit_nic(&tbl->rx); |
| break; |
| case MLX5DR_DOMAIN_TYPE_NIC_TX: |
| dr_table_uninit_nic(&tbl->tx); |
| break; |
| case MLX5DR_DOMAIN_TYPE_FDB: |
| dr_table_uninit_fdb(tbl); |
| break; |
| default: |
| WARN_ON(true); |
| break; |
| } |
| |
| mlx5dr_domain_unlock(tbl->dmn); |
| } |
| |
| static int dr_table_init_nic(struct mlx5dr_domain *dmn, |
| struct mlx5dr_table_rx_tx *nic_tbl) |
| { |
| struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn; |
| struct mlx5dr_htbl_connect_info info; |
| int ret; |
| |
| INIT_LIST_HEAD(&nic_tbl->nic_matcher_list); |
| |
| nic_tbl->default_icm_addr = nic_dmn->default_icm_addr; |
| |
| nic_tbl->s_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, |
| DR_CHUNK_SIZE_1, |
| MLX5DR_STE_LU_TYPE_DONT_CARE, |
| 0); |
| if (!nic_tbl->s_anchor) { |
| mlx5dr_err(dmn, "Failed allocating htbl\n"); |
| return -ENOMEM; |
| } |
| |
| info.type = CONNECT_MISS; |
| info.miss_icm_addr = nic_dmn->default_icm_addr; |
| ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, |
| nic_tbl->s_anchor, |
| &info, true); |
| if (ret) { |
| mlx5dr_err(dmn, "Failed int and send htbl\n"); |
| goto free_s_anchor; |
| } |
| |
| mlx5dr_htbl_get(nic_tbl->s_anchor); |
| |
| return 0; |
| |
| free_s_anchor: |
| mlx5dr_ste_htbl_free(nic_tbl->s_anchor); |
| return ret; |
| } |
| |
| static int dr_table_init_fdb(struct mlx5dr_table *tbl) |
| { |
| int ret; |
| |
| ret = dr_table_init_nic(tbl->dmn, &tbl->rx); |
| if (ret) |
| return ret; |
| |
| ret = dr_table_init_nic(tbl->dmn, &tbl->tx); |
| if (ret) |
| goto destroy_rx; |
| |
| return 0; |
| |
| destroy_rx: |
| dr_table_uninit_nic(&tbl->rx); |
| return ret; |
| } |
| |
| static int dr_table_init(struct mlx5dr_table *tbl) |
| { |
| int ret = 0; |
| |
| INIT_LIST_HEAD(&tbl->matcher_list); |
| |
| mlx5dr_domain_lock(tbl->dmn); |
| |
| switch (tbl->dmn->type) { |
| case MLX5DR_DOMAIN_TYPE_NIC_RX: |
| tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_RX; |
| tbl->rx.nic_dmn = &tbl->dmn->info.rx; |
| ret = dr_table_init_nic(tbl->dmn, &tbl->rx); |
| break; |
| case MLX5DR_DOMAIN_TYPE_NIC_TX: |
| tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_TX; |
| tbl->tx.nic_dmn = &tbl->dmn->info.tx; |
| ret = dr_table_init_nic(tbl->dmn, &tbl->tx); |
| break; |
| case MLX5DR_DOMAIN_TYPE_FDB: |
| tbl->table_type = MLX5_FLOW_TABLE_TYPE_FDB; |
| tbl->rx.nic_dmn = &tbl->dmn->info.rx; |
| tbl->tx.nic_dmn = &tbl->dmn->info.tx; |
| ret = dr_table_init_fdb(tbl); |
| break; |
| default: |
| WARN_ON(true); |
| break; |
| } |
| |
| mlx5dr_domain_unlock(tbl->dmn); |
| |
| return ret; |
| } |
| |
| static int dr_table_destroy_sw_owned_tbl(struct mlx5dr_table *tbl) |
| { |
| return mlx5dr_cmd_destroy_flow_table(tbl->dmn->mdev, |
| tbl->table_id, |
| tbl->table_type); |
| } |
| |
| static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl, u16 uid) |
| { |
| bool en_encap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT); |
| bool en_decap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); |
| struct mlx5dr_cmd_create_flow_table_attr ft_attr = {}; |
| u64 icm_addr_rx = 0; |
| u64 icm_addr_tx = 0; |
| int ret; |
| |
| if (tbl->rx.s_anchor) |
| icm_addr_rx = mlx5dr_icm_pool_get_chunk_icm_addr(tbl->rx.s_anchor->chunk); |
| |
| if (tbl->tx.s_anchor) |
| icm_addr_tx = mlx5dr_icm_pool_get_chunk_icm_addr(tbl->tx.s_anchor->chunk); |
| |
| ft_attr.table_type = tbl->table_type; |
| ft_attr.icm_addr_rx = icm_addr_rx; |
| ft_attr.icm_addr_tx = icm_addr_tx; |
| ft_attr.level = tbl->dmn->info.caps.max_ft_level - 1; |
| ft_attr.sw_owner = true; |
| ft_attr.decap_en = en_decap; |
| ft_attr.reformat_en = en_encap; |
| ft_attr.uid = uid; |
| |
| ret = mlx5dr_cmd_create_flow_table(tbl->dmn->mdev, &ft_attr, |
| NULL, &tbl->table_id); |
| |
| return ret; |
| } |
| |
| struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, |
| u32 flags, u16 uid) |
| { |
| struct mlx5dr_table *tbl; |
| int ret; |
| |
| refcount_inc(&dmn->refcount); |
| |
| tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); |
| if (!tbl) |
| goto dec_ref; |
| |
| tbl->dmn = dmn; |
| tbl->level = level; |
| tbl->flags = flags; |
| refcount_set(&tbl->refcount, 1); |
| |
| ret = dr_table_init(tbl); |
| if (ret) |
| goto free_tbl; |
| |
| ret = dr_table_create_sw_owned_tbl(tbl, uid); |
| if (ret) |
| goto uninit_tbl; |
| |
| INIT_LIST_HEAD(&tbl->dbg_node); |
| mlx5dr_dbg_tbl_add(tbl); |
| return tbl; |
| |
| uninit_tbl: |
| dr_table_uninit(tbl); |
| free_tbl: |
| kfree(tbl); |
| dec_ref: |
| refcount_dec(&dmn->refcount); |
| return NULL; |
| } |
| |
| int mlx5dr_table_destroy(struct mlx5dr_table *tbl) |
| { |
| int ret; |
| |
| if (WARN_ON_ONCE(refcount_read(&tbl->refcount) > 1)) |
| return -EBUSY; |
| |
| mlx5dr_dbg_tbl_del(tbl); |
| ret = dr_table_destroy_sw_owned_tbl(tbl); |
| if (ret) |
| mlx5dr_err(tbl->dmn, "Failed to destroy sw owned table\n"); |
| |
| dr_table_uninit(tbl); |
| |
| if (tbl->miss_action) |
| refcount_dec(&tbl->miss_action->refcount); |
| |
| refcount_dec(&tbl->dmn->refcount); |
| kfree(tbl); |
| |
| return ret; |
| } |
| |
| u32 mlx5dr_table_get_id(struct mlx5dr_table *tbl) |
| { |
| return tbl->table_id; |
| } |
| |
| struct mlx5dr_table *mlx5dr_table_get_from_fs_ft(struct mlx5_flow_table *ft) |
| { |
| return ft->fs_dr_table.dr_table; |
| } |