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