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