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