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