1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* Copyright (c) 2019 Mellanox Technologies. */ 3 4 #include "dr_types.h" 5 6 static int dr_table_set_miss_action_nic(struct mlx5dr_domain *dmn, 7 struct mlx5dr_table_rx_tx *nic_tbl, 8 struct mlx5dr_action *action) 9 { 10 struct mlx5dr_matcher_rx_tx *last_nic_matcher = NULL; 11 struct mlx5dr_htbl_connect_info info; 12 struct mlx5dr_ste_htbl *last_htbl; 13 int ret; 14 15 if (!list_empty(&nic_tbl->nic_matcher_list)) 16 last_nic_matcher = list_last_entry(&nic_tbl->nic_matcher_list, 17 struct mlx5dr_matcher_rx_tx, 18 list_node); 19 20 if (last_nic_matcher) 21 last_htbl = last_nic_matcher->e_anchor; 22 else 23 last_htbl = nic_tbl->s_anchor; 24 25 if (action) 26 nic_tbl->default_icm_addr = 27 nic_tbl->nic_dmn->type == DR_DOMAIN_NIC_TYPE_RX ? 28 action->dest_tbl->tbl->rx.s_anchor->chunk->icm_addr : 29 action->dest_tbl->tbl->tx.s_anchor->chunk->icm_addr; 30 else 31 nic_tbl->default_icm_addr = nic_tbl->nic_dmn->default_icm_addr; 32 33 info.type = CONNECT_MISS; 34 info.miss_icm_addr = nic_tbl->default_icm_addr; 35 36 ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_tbl->nic_dmn, 37 last_htbl, &info, true); 38 if (ret) 39 mlx5dr_dbg(dmn, "Failed to set NIC RX/TX miss action, ret %d\n", ret); 40 41 return ret; 42 } 43 44 int mlx5dr_table_set_miss_action(struct mlx5dr_table *tbl, 45 struct mlx5dr_action *action) 46 { 47 int ret; 48 49 if (action && action->action_type != DR_ACTION_TYP_FT) 50 return -EOPNOTSUPP; 51 52 mlx5dr_domain_lock(tbl->dmn); 53 54 if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX || 55 tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) { 56 ret = dr_table_set_miss_action_nic(tbl->dmn, &tbl->rx, action); 57 if (ret) 58 goto out; 59 } 60 61 if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX || 62 tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) { 63 ret = dr_table_set_miss_action_nic(tbl->dmn, &tbl->tx, action); 64 if (ret) 65 goto out; 66 } 67 68 /* Release old action */ 69 if (tbl->miss_action) 70 refcount_dec(&tbl->miss_action->refcount); 71 72 /* Set new miss action */ 73 tbl->miss_action = action; 74 if (tbl->miss_action) 75 refcount_inc(&action->refcount); 76 77 out: 78 mlx5dr_domain_unlock(tbl->dmn); 79 return ret; 80 } 81 82 static void dr_table_uninit_nic(struct mlx5dr_table_rx_tx *nic_tbl) 83 { 84 mlx5dr_htbl_put(nic_tbl->s_anchor); 85 } 86 87 static void dr_table_uninit_fdb(struct mlx5dr_table *tbl) 88 { 89 dr_table_uninit_nic(&tbl->rx); 90 dr_table_uninit_nic(&tbl->tx); 91 } 92 93 static void dr_table_uninit(struct mlx5dr_table *tbl) 94 { 95 mlx5dr_domain_lock(tbl->dmn); 96 97 switch (tbl->dmn->type) { 98 case MLX5DR_DOMAIN_TYPE_NIC_RX: 99 dr_table_uninit_nic(&tbl->rx); 100 break; 101 case MLX5DR_DOMAIN_TYPE_NIC_TX: 102 dr_table_uninit_nic(&tbl->tx); 103 break; 104 case MLX5DR_DOMAIN_TYPE_FDB: 105 dr_table_uninit_fdb(tbl); 106 break; 107 default: 108 WARN_ON(true); 109 break; 110 } 111 112 mlx5dr_domain_unlock(tbl->dmn); 113 } 114 115 static int dr_table_init_nic(struct mlx5dr_domain *dmn, 116 struct mlx5dr_table_rx_tx *nic_tbl) 117 { 118 struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn; 119 struct mlx5dr_htbl_connect_info info; 120 int ret; 121 122 INIT_LIST_HEAD(&nic_tbl->nic_matcher_list); 123 124 nic_tbl->default_icm_addr = nic_dmn->default_icm_addr; 125 126 nic_tbl->s_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, 127 DR_CHUNK_SIZE_1, 128 MLX5DR_STE_LU_TYPE_DONT_CARE, 129 0); 130 if (!nic_tbl->s_anchor) { 131 mlx5dr_err(dmn, "Failed allocating htbl\n"); 132 return -ENOMEM; 133 } 134 135 info.type = CONNECT_MISS; 136 info.miss_icm_addr = nic_dmn->default_icm_addr; 137 ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, 138 nic_tbl->s_anchor, 139 &info, true); 140 if (ret) { 141 mlx5dr_err(dmn, "Failed int and send htbl\n"); 142 goto free_s_anchor; 143 } 144 145 mlx5dr_htbl_get(nic_tbl->s_anchor); 146 147 return 0; 148 149 free_s_anchor: 150 mlx5dr_ste_htbl_free(nic_tbl->s_anchor); 151 return ret; 152 } 153 154 static int dr_table_init_fdb(struct mlx5dr_table *tbl) 155 { 156 int ret; 157 158 ret = dr_table_init_nic(tbl->dmn, &tbl->rx); 159 if (ret) 160 return ret; 161 162 ret = dr_table_init_nic(tbl->dmn, &tbl->tx); 163 if (ret) 164 goto destroy_rx; 165 166 return 0; 167 168 destroy_rx: 169 dr_table_uninit_nic(&tbl->rx); 170 return ret; 171 } 172 173 static int dr_table_init(struct mlx5dr_table *tbl) 174 { 175 int ret = 0; 176 177 INIT_LIST_HEAD(&tbl->matcher_list); 178 179 mlx5dr_domain_lock(tbl->dmn); 180 181 switch (tbl->dmn->type) { 182 case MLX5DR_DOMAIN_TYPE_NIC_RX: 183 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_RX; 184 tbl->rx.nic_dmn = &tbl->dmn->info.rx; 185 ret = dr_table_init_nic(tbl->dmn, &tbl->rx); 186 break; 187 case MLX5DR_DOMAIN_TYPE_NIC_TX: 188 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_TX; 189 tbl->tx.nic_dmn = &tbl->dmn->info.tx; 190 ret = dr_table_init_nic(tbl->dmn, &tbl->tx); 191 break; 192 case MLX5DR_DOMAIN_TYPE_FDB: 193 tbl->table_type = MLX5_FLOW_TABLE_TYPE_FDB; 194 tbl->rx.nic_dmn = &tbl->dmn->info.rx; 195 tbl->tx.nic_dmn = &tbl->dmn->info.tx; 196 ret = dr_table_init_fdb(tbl); 197 break; 198 default: 199 WARN_ON(true); 200 break; 201 } 202 203 mlx5dr_domain_unlock(tbl->dmn); 204 205 return ret; 206 } 207 208 static int dr_table_destroy_sw_owned_tbl(struct mlx5dr_table *tbl) 209 { 210 return mlx5dr_cmd_destroy_flow_table(tbl->dmn->mdev, 211 tbl->table_id, 212 tbl->table_type); 213 } 214 215 static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl) 216 { 217 bool en_encap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT); 218 bool en_decap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); 219 struct mlx5dr_cmd_create_flow_table_attr ft_attr = {}; 220 u64 icm_addr_rx = 0; 221 u64 icm_addr_tx = 0; 222 int ret; 223 224 if (tbl->rx.s_anchor) 225 icm_addr_rx = tbl->rx.s_anchor->chunk->icm_addr; 226 227 if (tbl->tx.s_anchor) 228 icm_addr_tx = tbl->tx.s_anchor->chunk->icm_addr; 229 230 ft_attr.table_type = tbl->table_type; 231 ft_attr.icm_addr_rx = icm_addr_rx; 232 ft_attr.icm_addr_tx = icm_addr_tx; 233 ft_attr.level = tbl->dmn->info.caps.max_ft_level - 1; 234 ft_attr.sw_owner = true; 235 ft_attr.decap_en = en_decap; 236 ft_attr.reformat_en = en_encap; 237 238 ret = mlx5dr_cmd_create_flow_table(tbl->dmn->mdev, &ft_attr, 239 NULL, &tbl->table_id); 240 241 return ret; 242 } 243 244 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, u32 flags) 245 { 246 struct mlx5dr_table *tbl; 247 int ret; 248 249 refcount_inc(&dmn->refcount); 250 251 tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); 252 if (!tbl) 253 goto dec_ref; 254 255 tbl->dmn = dmn; 256 tbl->level = level; 257 tbl->flags = flags; 258 refcount_set(&tbl->refcount, 1); 259 260 ret = dr_table_init(tbl); 261 if (ret) 262 goto free_tbl; 263 264 ret = dr_table_create_sw_owned_tbl(tbl); 265 if (ret) 266 goto uninit_tbl; 267 268 INIT_LIST_HEAD(&tbl->dbg_node); 269 mlx5dr_dbg_tbl_add(tbl); 270 return tbl; 271 272 uninit_tbl: 273 dr_table_uninit(tbl); 274 free_tbl: 275 kfree(tbl); 276 dec_ref: 277 refcount_dec(&dmn->refcount); 278 return NULL; 279 } 280 281 int mlx5dr_table_destroy(struct mlx5dr_table *tbl) 282 { 283 int ret; 284 285 if (WARN_ON_ONCE(refcount_read(&tbl->refcount) > 1)) 286 return -EBUSY; 287 288 mlx5dr_dbg_tbl_del(tbl); 289 ret = dr_table_destroy_sw_owned_tbl(tbl); 290 if (ret) 291 return ret; 292 293 dr_table_uninit(tbl); 294 295 if (tbl->miss_action) 296 refcount_dec(&tbl->miss_action->refcount); 297 298 refcount_dec(&tbl->dmn->refcount); 299 kfree(tbl); 300 301 return ret; 302 } 303 304 u32 mlx5dr_table_get_id(struct mlx5dr_table *tbl) 305 { 306 return tbl->table_id; 307 } 308