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