1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3
4 #include "dr_types.h"
5
mlx5dr_table_set_miss_action(struct mlx5dr_table * tbl,struct mlx5dr_action * action)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 = -EOPNOTSUPP;
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 list_node);
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 if (ret)
72 goto out;
73
74 /* Release old action */
75 if (tbl->miss_action)
76 refcount_dec(&tbl->miss_action->refcount);
77
78 /* Set new miss action */
79 tbl->miss_action = action;
80 if (tbl->miss_action)
81 refcount_inc(&action->refcount);
82
83 out:
84 mlx5dr_domain_unlock(tbl->dmn);
85 return ret;
86 }
87
dr_table_uninit_nic(struct mlx5dr_table_rx_tx * nic_tbl)88 static void dr_table_uninit_nic(struct mlx5dr_table_rx_tx *nic_tbl)
89 {
90 mlx5dr_htbl_put(nic_tbl->s_anchor);
91 }
92
dr_table_uninit_fdb(struct mlx5dr_table * tbl)93 static void dr_table_uninit_fdb(struct mlx5dr_table *tbl)
94 {
95 dr_table_uninit_nic(&tbl->rx);
96 dr_table_uninit_nic(&tbl->tx);
97 }
98
dr_table_uninit(struct mlx5dr_table * tbl)99 static void dr_table_uninit(struct mlx5dr_table *tbl)
100 {
101 mlx5dr_domain_lock(tbl->dmn);
102
103 switch (tbl->dmn->type) {
104 case MLX5DR_DOMAIN_TYPE_NIC_RX:
105 dr_table_uninit_nic(&tbl->rx);
106 break;
107 case MLX5DR_DOMAIN_TYPE_NIC_TX:
108 dr_table_uninit_nic(&tbl->tx);
109 break;
110 case MLX5DR_DOMAIN_TYPE_FDB:
111 dr_table_uninit_fdb(tbl);
112 break;
113 default:
114 WARN_ON(true);
115 break;
116 }
117
118 mlx5dr_domain_unlock(tbl->dmn);
119 }
120
dr_table_init_nic(struct mlx5dr_domain * dmn,struct mlx5dr_table_rx_tx * nic_tbl)121 static int dr_table_init_nic(struct mlx5dr_domain *dmn,
122 struct mlx5dr_table_rx_tx *nic_tbl)
123 {
124 struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
125 struct mlx5dr_htbl_connect_info info;
126 int ret;
127
128 nic_tbl->default_icm_addr = nic_dmn->default_icm_addr;
129
130 nic_tbl->s_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
131 DR_CHUNK_SIZE_1,
132 MLX5DR_STE_LU_TYPE_DONT_CARE,
133 0);
134 if (!nic_tbl->s_anchor) {
135 mlx5dr_err(dmn, "Failed allocating htbl\n");
136 return -ENOMEM;
137 }
138
139 info.type = CONNECT_MISS;
140 info.miss_icm_addr = nic_dmn->default_icm_addr;
141 ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
142 nic_tbl->s_anchor,
143 &info, true);
144 if (ret) {
145 mlx5dr_err(dmn, "Failed int and send htbl\n");
146 goto free_s_anchor;
147 }
148
149 mlx5dr_htbl_get(nic_tbl->s_anchor);
150
151 return 0;
152
153 free_s_anchor:
154 mlx5dr_ste_htbl_free(nic_tbl->s_anchor);
155 return ret;
156 }
157
dr_table_init_fdb(struct mlx5dr_table * tbl)158 static int dr_table_init_fdb(struct mlx5dr_table *tbl)
159 {
160 int ret;
161
162 ret = dr_table_init_nic(tbl->dmn, &tbl->rx);
163 if (ret)
164 return ret;
165
166 ret = dr_table_init_nic(tbl->dmn, &tbl->tx);
167 if (ret)
168 goto destroy_rx;
169
170 return 0;
171
172 destroy_rx:
173 dr_table_uninit_nic(&tbl->rx);
174 return ret;
175 }
176
dr_table_init(struct mlx5dr_table * tbl)177 static int dr_table_init(struct mlx5dr_table *tbl)
178 {
179 int ret = 0;
180
181 INIT_LIST_HEAD(&tbl->matcher_list);
182
183 mlx5dr_domain_lock(tbl->dmn);
184
185 switch (tbl->dmn->type) {
186 case MLX5DR_DOMAIN_TYPE_NIC_RX:
187 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_RX;
188 tbl->rx.nic_dmn = &tbl->dmn->info.rx;
189 ret = dr_table_init_nic(tbl->dmn, &tbl->rx);
190 break;
191 case MLX5DR_DOMAIN_TYPE_NIC_TX:
192 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_TX;
193 tbl->tx.nic_dmn = &tbl->dmn->info.tx;
194 ret = dr_table_init_nic(tbl->dmn, &tbl->tx);
195 break;
196 case MLX5DR_DOMAIN_TYPE_FDB:
197 tbl->table_type = MLX5_FLOW_TABLE_TYPE_FDB;
198 tbl->rx.nic_dmn = &tbl->dmn->info.rx;
199 tbl->tx.nic_dmn = &tbl->dmn->info.tx;
200 ret = dr_table_init_fdb(tbl);
201 break;
202 default:
203 WARN_ON(true);
204 break;
205 }
206
207 mlx5dr_domain_unlock(tbl->dmn);
208
209 return ret;
210 }
211
dr_table_destroy_sw_owned_tbl(struct mlx5dr_table * tbl)212 static int dr_table_destroy_sw_owned_tbl(struct mlx5dr_table *tbl)
213 {
214 return mlx5dr_cmd_destroy_flow_table(tbl->dmn->mdev,
215 tbl->table_id,
216 tbl->table_type);
217 }
218
dr_table_create_sw_owned_tbl(struct mlx5dr_table * tbl)219 static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl)
220 {
221 bool en_encap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT);
222 bool en_decap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_DECAP);
223 struct mlx5dr_cmd_create_flow_table_attr ft_attr = {};
224 u64 icm_addr_rx = 0;
225 u64 icm_addr_tx = 0;
226 int ret;
227
228 if (tbl->rx.s_anchor)
229 icm_addr_rx = tbl->rx.s_anchor->chunk->icm_addr;
230
231 if (tbl->tx.s_anchor)
232 icm_addr_tx = tbl->tx.s_anchor->chunk->icm_addr;
233
234 ft_attr.table_type = tbl->table_type;
235 ft_attr.icm_addr_rx = icm_addr_rx;
236 ft_attr.icm_addr_tx = icm_addr_tx;
237 ft_attr.level = tbl->dmn->info.caps.max_ft_level - 1;
238 ft_attr.sw_owner = true;
239 ft_attr.decap_en = en_decap;
240 ft_attr.reformat_en = en_encap;
241
242 ret = mlx5dr_cmd_create_flow_table(tbl->dmn->mdev, &ft_attr,
243 NULL, &tbl->table_id);
244
245 return ret;
246 }
247
mlx5dr_table_create(struct mlx5dr_domain * dmn,u32 level,u32 flags)248 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, u32 flags)
249 {
250 struct mlx5dr_table *tbl;
251 int ret;
252
253 refcount_inc(&dmn->refcount);
254
255 tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
256 if (!tbl)
257 goto dec_ref;
258
259 tbl->dmn = dmn;
260 tbl->level = level;
261 tbl->flags = flags;
262 refcount_set(&tbl->refcount, 1);
263
264 ret = dr_table_init(tbl);
265 if (ret)
266 goto free_tbl;
267
268 ret = dr_table_create_sw_owned_tbl(tbl);
269 if (ret)
270 goto uninit_tbl;
271
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 (refcount_read(&tbl->refcount) > 1)
288 return -EBUSY;
289
290 ret = dr_table_destroy_sw_owned_tbl(tbl);
291 if (ret)
292 return ret;
293
294 dr_table_uninit(tbl);
295
296 if (tbl->miss_action)
297 refcount_dec(&tbl->miss_action->refcount);
298
299 refcount_dec(&tbl->dmn->refcount);
300 kfree(tbl);
301
302 return ret;
303 }
304
mlx5dr_table_get_id(struct mlx5dr_table * tbl)305 u32 mlx5dr_table_get_id(struct mlx5dr_table *tbl)
306 {
307 return tbl->table_id;
308 }
309