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_cmd_query_esw_vport_context(struct mlx5_core_dev *mdev,
7 				       bool other_vport,
8 				       u16 vport_number,
9 				       u64 *icm_address_rx,
10 				       u64 *icm_address_tx)
11 {
12 	u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {};
13 	u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {};
14 	int err;
15 
16 	MLX5_SET(query_esw_vport_context_in, in, opcode,
17 		 MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
18 	MLX5_SET(query_esw_vport_context_in, in, other_vport, other_vport);
19 	MLX5_SET(query_esw_vport_context_in, in, vport_number, vport_number);
20 
21 	err = mlx5_cmd_exec_inout(mdev, query_esw_vport_context, in, out);
22 	if (err)
23 		return err;
24 
25 	*icm_address_rx =
26 		MLX5_GET64(query_esw_vport_context_out, out,
27 			   esw_vport_context.sw_steering_vport_icm_address_rx);
28 	*icm_address_tx =
29 		MLX5_GET64(query_esw_vport_context_out, out,
30 			   esw_vport_context.sw_steering_vport_icm_address_tx);
31 	return 0;
32 }
33 
34 int mlx5dr_cmd_query_gvmi(struct mlx5_core_dev *mdev, bool other_vport,
35 			  u16 vport_number, u16 *gvmi)
36 {
37 	u32 in[MLX5_ST_SZ_DW(query_hca_cap_in)] = {};
38 	int out_size;
39 	void *out;
40 	int err;
41 
42 	out_size = MLX5_ST_SZ_BYTES(query_hca_cap_out);
43 	out = kzalloc(out_size, GFP_KERNEL);
44 	if (!out)
45 		return -ENOMEM;
46 
47 	MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP);
48 	MLX5_SET(query_hca_cap_in, in, other_function, other_vport);
49 	MLX5_SET(query_hca_cap_in, in, function_id, vport_number);
50 	MLX5_SET(query_hca_cap_in, in, op_mod,
51 		 MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1 |
52 		 HCA_CAP_OPMOD_GET_CUR);
53 
54 	err = mlx5_cmd_exec_inout(mdev, query_hca_cap, in, out);
55 	if (err) {
56 		kfree(out);
57 		return err;
58 	}
59 
60 	*gvmi = MLX5_GET(query_hca_cap_out, out, capability.cmd_hca_cap.vhca_id);
61 
62 	kfree(out);
63 	return 0;
64 }
65 
66 int mlx5dr_cmd_query_esw_caps(struct mlx5_core_dev *mdev,
67 			      struct mlx5dr_esw_caps *caps)
68 {
69 	caps->drop_icm_address_rx =
70 		MLX5_CAP64_ESW_FLOWTABLE(mdev,
71 					 sw_steering_fdb_action_drop_icm_address_rx);
72 	caps->drop_icm_address_tx =
73 		MLX5_CAP64_ESW_FLOWTABLE(mdev,
74 					 sw_steering_fdb_action_drop_icm_address_tx);
75 	caps->uplink_icm_address_rx =
76 		MLX5_CAP64_ESW_FLOWTABLE(mdev,
77 					 sw_steering_uplink_icm_address_rx);
78 	caps->uplink_icm_address_tx =
79 		MLX5_CAP64_ESW_FLOWTABLE(mdev,
80 					 sw_steering_uplink_icm_address_tx);
81 	caps->sw_owner =
82 		MLX5_CAP_ESW_FLOWTABLE_FDB(mdev,
83 					   sw_owner);
84 
85 	return 0;
86 }
87 
88 int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev,
89 			    struct mlx5dr_cmd_caps *caps)
90 {
91 	caps->prio_tag_required	= MLX5_CAP_GEN(mdev, prio_tag_required);
92 	caps->eswitch_manager	= MLX5_CAP_GEN(mdev, eswitch_manager);
93 	caps->gvmi		= MLX5_CAP_GEN(mdev, vhca_id);
94 	caps->flex_protocols	= MLX5_CAP_GEN(mdev, flex_parser_protocols);
95 
96 	if (mlx5dr_matcher_supp_flex_parser_icmp_v4(caps)) {
97 		caps->flex_parser_id_icmp_dw0 = MLX5_CAP_GEN(mdev, flex_parser_id_icmp_dw0);
98 		caps->flex_parser_id_icmp_dw1 = MLX5_CAP_GEN(mdev, flex_parser_id_icmp_dw1);
99 	}
100 
101 	if (mlx5dr_matcher_supp_flex_parser_icmp_v6(caps)) {
102 		caps->flex_parser_id_icmpv6_dw0 =
103 			MLX5_CAP_GEN(mdev, flex_parser_id_icmpv6_dw0);
104 		caps->flex_parser_id_icmpv6_dw1 =
105 			MLX5_CAP_GEN(mdev, flex_parser_id_icmpv6_dw1);
106 	}
107 
108 	caps->nic_rx_drop_address =
109 		MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_rx_action_drop_icm_address);
110 	caps->nic_tx_drop_address =
111 		MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_tx_action_drop_icm_address);
112 	caps->nic_tx_allow_address =
113 		MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_tx_action_allow_icm_address);
114 
115 	caps->rx_sw_owner = MLX5_CAP_FLOWTABLE_NIC_RX(mdev, sw_owner);
116 	caps->max_ft_level = MLX5_CAP_FLOWTABLE_NIC_RX(mdev, max_ft_level);
117 
118 	caps->tx_sw_owner = MLX5_CAP_FLOWTABLE_NIC_TX(mdev, sw_owner);
119 
120 	caps->log_icm_size = MLX5_CAP_DEV_MEM(mdev, log_steering_sw_icm_size);
121 	caps->hdr_modify_icm_addr =
122 		MLX5_CAP64_DEV_MEM(mdev, header_modify_sw_icm_start_address);
123 
124 	caps->roce_min_src_udp = MLX5_CAP_ROCE(mdev, r_roce_min_src_udp_port);
125 
126 	return 0;
127 }
128 
129 int mlx5dr_cmd_query_flow_table(struct mlx5_core_dev *dev,
130 				enum fs_flow_table_type type,
131 				u32 table_id,
132 				struct mlx5dr_cmd_query_flow_table_details *output)
133 {
134 	u32 out[MLX5_ST_SZ_DW(query_flow_table_out)] = {};
135 	u32 in[MLX5_ST_SZ_DW(query_flow_table_in)] = {};
136 	int err;
137 
138 	MLX5_SET(query_flow_table_in, in, opcode,
139 		 MLX5_CMD_OP_QUERY_FLOW_TABLE);
140 
141 	MLX5_SET(query_flow_table_in, in, table_type, type);
142 	MLX5_SET(query_flow_table_in, in, table_id, table_id);
143 
144 	err = mlx5_cmd_exec_inout(dev, query_flow_table, in, out);
145 	if (err)
146 		return err;
147 
148 	output->status = MLX5_GET(query_flow_table_out, out, status);
149 	output->level = MLX5_GET(query_flow_table_out, out, flow_table_context.level);
150 
151 	output->sw_owner_icm_root_1 = MLX5_GET64(query_flow_table_out, out,
152 						 flow_table_context.sw_owner_icm_root_1);
153 	output->sw_owner_icm_root_0 = MLX5_GET64(query_flow_table_out, out,
154 						 flow_table_context.sw_owner_icm_root_0);
155 
156 	return 0;
157 }
158 
159 int mlx5dr_cmd_sync_steering(struct mlx5_core_dev *mdev)
160 {
161 	u32 in[MLX5_ST_SZ_DW(sync_steering_in)] = {};
162 
163 	MLX5_SET(sync_steering_in, in, opcode, MLX5_CMD_OP_SYNC_STEERING);
164 
165 	return mlx5_cmd_exec_in(mdev, sync_steering, in);
166 }
167 
168 int mlx5dr_cmd_set_fte_modify_and_vport(struct mlx5_core_dev *mdev,
169 					u32 table_type,
170 					u32 table_id,
171 					u32 group_id,
172 					u32 modify_header_id,
173 					u32 vport_id)
174 {
175 	u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {};
176 	void *in_flow_context;
177 	unsigned int inlen;
178 	void *in_dests;
179 	u32 *in;
180 	int err;
181 
182 	inlen = MLX5_ST_SZ_BYTES(set_fte_in) +
183 		1 * MLX5_ST_SZ_BYTES(dest_format_struct); /* One destination only */
184 
185 	in = kvzalloc(inlen, GFP_KERNEL);
186 	if (!in)
187 		return -ENOMEM;
188 
189 	MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
190 	MLX5_SET(set_fte_in, in, table_type, table_type);
191 	MLX5_SET(set_fte_in, in, table_id, table_id);
192 
193 	in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
194 	MLX5_SET(flow_context, in_flow_context, group_id, group_id);
195 	MLX5_SET(flow_context, in_flow_context, modify_header_id, modify_header_id);
196 	MLX5_SET(flow_context, in_flow_context, destination_list_size, 1);
197 	MLX5_SET(flow_context, in_flow_context, action,
198 		 MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
199 		 MLX5_FLOW_CONTEXT_ACTION_MOD_HDR);
200 
201 	in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
202 	MLX5_SET(dest_format_struct, in_dests, destination_type,
203 		 MLX5_FLOW_DESTINATION_TYPE_VPORT);
204 	MLX5_SET(dest_format_struct, in_dests, destination_id, vport_id);
205 
206 	err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
207 	kvfree(in);
208 
209 	return err;
210 }
211 
212 int mlx5dr_cmd_del_flow_table_entry(struct mlx5_core_dev *mdev,
213 				    u32 table_type,
214 				    u32 table_id)
215 {
216 	u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {};
217 
218 	MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
219 	MLX5_SET(delete_fte_in, in, table_type, table_type);
220 	MLX5_SET(delete_fte_in, in, table_id, table_id);
221 
222 	return mlx5_cmd_exec_in(mdev, delete_fte, in);
223 }
224 
225 int mlx5dr_cmd_alloc_modify_header(struct mlx5_core_dev *mdev,
226 				   u32 table_type,
227 				   u8 num_of_actions,
228 				   u64 *actions,
229 				   u32 *modify_header_id)
230 {
231 	u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)] = {};
232 	void *p_actions;
233 	u32 inlen;
234 	u32 *in;
235 	int err;
236 
237 	inlen = MLX5_ST_SZ_BYTES(alloc_modify_header_context_in) +
238 		 num_of_actions * sizeof(u64);
239 	in = kvzalloc(inlen, GFP_KERNEL);
240 	if (!in)
241 		return -ENOMEM;
242 
243 	MLX5_SET(alloc_modify_header_context_in, in, opcode,
244 		 MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT);
245 	MLX5_SET(alloc_modify_header_context_in, in, table_type, table_type);
246 	MLX5_SET(alloc_modify_header_context_in, in, num_of_actions, num_of_actions);
247 	p_actions = MLX5_ADDR_OF(alloc_modify_header_context_in, in, actions);
248 	memcpy(p_actions, actions, num_of_actions * sizeof(u64));
249 
250 	err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
251 	if (err)
252 		goto out;
253 
254 	*modify_header_id = MLX5_GET(alloc_modify_header_context_out, out,
255 				     modify_header_id);
256 out:
257 	kvfree(in);
258 	return err;
259 }
260 
261 int mlx5dr_cmd_dealloc_modify_header(struct mlx5_core_dev *mdev,
262 				     u32 modify_header_id)
263 {
264 	u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)] = {};
265 
266 	MLX5_SET(dealloc_modify_header_context_in, in, opcode,
267 		 MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT);
268 	MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id,
269 		 modify_header_id);
270 
271 	return mlx5_cmd_exec_in(mdev, dealloc_modify_header_context, in);
272 }
273 
274 int mlx5dr_cmd_create_empty_flow_group(struct mlx5_core_dev *mdev,
275 				       u32 table_type,
276 				       u32 table_id,
277 				       u32 *group_id)
278 {
279 	u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {};
280 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
281 	u32 *in;
282 	int err;
283 
284 	in = kzalloc(inlen, GFP_KERNEL);
285 	if (!in)
286 		return -ENOMEM;
287 
288 	MLX5_SET(create_flow_group_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_GROUP);
289 	MLX5_SET(create_flow_group_in, in, table_type, table_type);
290 	MLX5_SET(create_flow_group_in, in, table_id, table_id);
291 
292 	err = mlx5_cmd_exec_inout(mdev, create_flow_group, in, out);
293 	if (err)
294 		goto out;
295 
296 	*group_id = MLX5_GET(create_flow_group_out, out, group_id);
297 
298 out:
299 	kfree(in);
300 	return err;
301 }
302 
303 int mlx5dr_cmd_destroy_flow_group(struct mlx5_core_dev *mdev,
304 				  u32 table_type,
305 				  u32 table_id,
306 				  u32 group_id)
307 {
308 	u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {};
309 
310 	MLX5_SET(destroy_flow_group_in, in, opcode,
311 		 MLX5_CMD_OP_DESTROY_FLOW_GROUP);
312 	MLX5_SET(destroy_flow_group_in, in, table_type, table_type);
313 	MLX5_SET(destroy_flow_group_in, in, table_id, table_id);
314 	MLX5_SET(destroy_flow_group_in, in, group_id, group_id);
315 
316 	return mlx5_cmd_exec_in(mdev, destroy_flow_group, in);
317 }
318 
319 int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev,
320 				 struct mlx5dr_cmd_create_flow_table_attr *attr,
321 				 u64 *fdb_rx_icm_addr,
322 				 u32 *table_id)
323 {
324 	u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {};
325 	u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {};
326 	void *ft_mdev;
327 	int err;
328 
329 	MLX5_SET(create_flow_table_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_TABLE);
330 	MLX5_SET(create_flow_table_in, in, table_type, attr->table_type);
331 
332 	ft_mdev = MLX5_ADDR_OF(create_flow_table_in, in, flow_table_context);
333 	MLX5_SET(flow_table_context, ft_mdev, termination_table, attr->term_tbl);
334 	MLX5_SET(flow_table_context, ft_mdev, sw_owner, attr->sw_owner);
335 	MLX5_SET(flow_table_context, ft_mdev, level, attr->level);
336 
337 	if (attr->sw_owner) {
338 		/* icm_addr_0 used for FDB RX / NIC TX / NIC_RX
339 		 * icm_addr_1 used for FDB TX
340 		 */
341 		if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_RX) {
342 			MLX5_SET64(flow_table_context, ft_mdev,
343 				   sw_owner_icm_root_0, attr->icm_addr_rx);
344 		} else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_TX) {
345 			MLX5_SET64(flow_table_context, ft_mdev,
346 				   sw_owner_icm_root_0, attr->icm_addr_tx);
347 		} else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_FDB) {
348 			MLX5_SET64(flow_table_context, ft_mdev,
349 				   sw_owner_icm_root_0, attr->icm_addr_rx);
350 			MLX5_SET64(flow_table_context, ft_mdev,
351 				   sw_owner_icm_root_1, attr->icm_addr_tx);
352 		}
353 	}
354 
355 	MLX5_SET(create_flow_table_in, in, flow_table_context.decap_en,
356 		 attr->decap_en);
357 	MLX5_SET(create_flow_table_in, in, flow_table_context.reformat_en,
358 		 attr->reformat_en);
359 
360 	err = mlx5_cmd_exec_inout(mdev, create_flow_table, in, out);
361 	if (err)
362 		return err;
363 
364 	*table_id = MLX5_GET(create_flow_table_out, out, table_id);
365 	if (!attr->sw_owner && attr->table_type == MLX5_FLOW_TABLE_TYPE_FDB &&
366 	    fdb_rx_icm_addr)
367 		*fdb_rx_icm_addr =
368 		(u64)MLX5_GET(create_flow_table_out, out, icm_address_31_0) |
369 		(u64)MLX5_GET(create_flow_table_out, out, icm_address_39_32) << 32 |
370 		(u64)MLX5_GET(create_flow_table_out, out, icm_address_63_40) << 40;
371 
372 	return 0;
373 }
374 
375 int mlx5dr_cmd_destroy_flow_table(struct mlx5_core_dev *mdev,
376 				  u32 table_id,
377 				  u32 table_type)
378 {
379 	u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {};
380 
381 	MLX5_SET(destroy_flow_table_in, in, opcode,
382 		 MLX5_CMD_OP_DESTROY_FLOW_TABLE);
383 	MLX5_SET(destroy_flow_table_in, in, table_type, table_type);
384 	MLX5_SET(destroy_flow_table_in, in, table_id, table_id);
385 
386 	return mlx5_cmd_exec_in(mdev, destroy_flow_table, in);
387 }
388 
389 int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev,
390 				   enum mlx5_reformat_ctx_type rt,
391 				   size_t reformat_size,
392 				   void *reformat_data,
393 				   u32 *reformat_id)
394 {
395 	u32 out[MLX5_ST_SZ_DW(alloc_packet_reformat_context_out)] = {};
396 	size_t inlen, cmd_data_sz, cmd_total_sz;
397 	void *prctx;
398 	void *pdata;
399 	void *in;
400 	int err;
401 
402 	cmd_total_sz = MLX5_ST_SZ_BYTES(alloc_packet_reformat_context_in);
403 	cmd_data_sz = MLX5_FLD_SZ_BYTES(alloc_packet_reformat_context_in,
404 					packet_reformat_context.reformat_data);
405 	inlen = ALIGN(cmd_total_sz + reformat_size - cmd_data_sz, 4);
406 	in = kvzalloc(inlen, GFP_KERNEL);
407 	if (!in)
408 		return -ENOMEM;
409 
410 	MLX5_SET(alloc_packet_reformat_context_in, in, opcode,
411 		 MLX5_CMD_OP_ALLOC_PACKET_REFORMAT_CONTEXT);
412 
413 	prctx = MLX5_ADDR_OF(alloc_packet_reformat_context_in, in, packet_reformat_context);
414 	pdata = MLX5_ADDR_OF(packet_reformat_context_in, prctx, reformat_data);
415 
416 	MLX5_SET(packet_reformat_context_in, prctx, reformat_type, rt);
417 	MLX5_SET(packet_reformat_context_in, prctx, reformat_data_size, reformat_size);
418 	memcpy(pdata, reformat_data, reformat_size);
419 
420 	err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
421 	if (err)
422 		return err;
423 
424 	*reformat_id = MLX5_GET(alloc_packet_reformat_context_out, out, packet_reformat_id);
425 	kvfree(in);
426 
427 	return err;
428 }
429 
430 void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev,
431 				     u32 reformat_id)
432 {
433 	u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)] = {};
434 
435 	MLX5_SET(dealloc_packet_reformat_context_in, in, opcode,
436 		 MLX5_CMD_OP_DEALLOC_PACKET_REFORMAT_CONTEXT);
437 	MLX5_SET(dealloc_packet_reformat_context_in, in, packet_reformat_id,
438 		 reformat_id);
439 
440 	mlx5_cmd_exec_in(mdev, dealloc_packet_reformat_context, in);
441 }
442 
443 int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num,
444 			 u16 index, struct mlx5dr_cmd_gid_attr *attr)
445 {
446 	u32 out[MLX5_ST_SZ_DW(query_roce_address_out)] = {};
447 	u32 in[MLX5_ST_SZ_DW(query_roce_address_in)] = {};
448 	int err;
449 
450 	MLX5_SET(query_roce_address_in, in, opcode,
451 		 MLX5_CMD_OP_QUERY_ROCE_ADDRESS);
452 
453 	MLX5_SET(query_roce_address_in, in, roce_address_index, index);
454 	MLX5_SET(query_roce_address_in, in, vhca_port_num, vhca_port_num);
455 
456 	err = mlx5_cmd_exec_inout(mdev, query_roce_address, in, out);
457 	if (err)
458 		return err;
459 
460 	memcpy(&attr->gid,
461 	       MLX5_ADDR_OF(query_roce_address_out,
462 			    out, roce_address.source_l3_address),
463 	       sizeof(attr->gid));
464 	memcpy(attr->mac,
465 	       MLX5_ADDR_OF(query_roce_address_out, out,
466 			    roce_address.source_mac_47_32),
467 	       sizeof(attr->mac));
468 
469 	if (MLX5_GET(query_roce_address_out, out,
470 		     roce_address.roce_version) == MLX5_ROCE_VERSION_2)
471 		attr->roce_ver = MLX5_ROCE_VERSION_2;
472 	else
473 		attr->roce_ver = MLX5_ROCE_VERSION_1;
474 
475 	return 0;
476 }
477 
478 static int mlx5dr_cmd_set_extended_dest(struct mlx5_core_dev *dev,
479 					struct mlx5dr_cmd_fte_info *fte,
480 					bool *extended_dest)
481 {
482 	int fw_log_max_fdb_encap_uplink = MLX5_CAP_ESW(dev, log_max_fdb_encap_uplink);
483 	int num_fwd_destinations = 0;
484 	int num_encap = 0;
485 	int i;
486 
487 	*extended_dest = false;
488 	if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
489 		return 0;
490 	for (i = 0; i < fte->dests_size; i++) {
491 		if (fte->dest_arr[i].type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
492 			continue;
493 		if (fte->dest_arr[i].type == MLX5_FLOW_DESTINATION_TYPE_VPORT &&
494 		    fte->dest_arr[i].vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID)
495 			num_encap++;
496 		num_fwd_destinations++;
497 	}
498 
499 	if (num_fwd_destinations > 1 && num_encap > 0)
500 		*extended_dest = true;
501 
502 	if (*extended_dest && !fw_log_max_fdb_encap_uplink) {
503 		mlx5_core_warn(dev, "FW does not support extended destination");
504 		return -EOPNOTSUPP;
505 	}
506 	if (num_encap > (1 << fw_log_max_fdb_encap_uplink)) {
507 		mlx5_core_warn(dev, "FW does not support more than %d encaps",
508 			       1 << fw_log_max_fdb_encap_uplink);
509 		return -EOPNOTSUPP;
510 	}
511 
512 	return 0;
513 }
514 
515 int mlx5dr_cmd_set_fte(struct mlx5_core_dev *dev,
516 		       int opmod, int modify_mask,
517 		       struct mlx5dr_cmd_ft_info *ft,
518 		       u32 group_id,
519 		       struct mlx5dr_cmd_fte_info *fte)
520 {
521 	u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {};
522 	void *in_flow_context, *vlan;
523 	bool extended_dest = false;
524 	void *in_match_value;
525 	unsigned int inlen;
526 	int dst_cnt_size;
527 	void *in_dests;
528 	u32 *in;
529 	int err;
530 	int i;
531 
532 	if (mlx5dr_cmd_set_extended_dest(dev, fte, &extended_dest))
533 		return -EOPNOTSUPP;
534 
535 	if (!extended_dest)
536 		dst_cnt_size = MLX5_ST_SZ_BYTES(dest_format_struct);
537 	else
538 		dst_cnt_size = MLX5_ST_SZ_BYTES(extended_dest_format);
539 
540 	inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fte->dests_size * dst_cnt_size;
541 	in = kvzalloc(inlen, GFP_KERNEL);
542 	if (!in)
543 		return -ENOMEM;
544 
545 	MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
546 	MLX5_SET(set_fte_in, in, op_mod, opmod);
547 	MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask);
548 	MLX5_SET(set_fte_in, in, table_type, ft->type);
549 	MLX5_SET(set_fte_in, in, table_id, ft->id);
550 	MLX5_SET(set_fte_in, in, flow_index, fte->index);
551 	if (ft->vport) {
552 		MLX5_SET(set_fte_in, in, vport_number, ft->vport);
553 		MLX5_SET(set_fte_in, in, other_vport, 1);
554 	}
555 
556 	in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
557 	MLX5_SET(flow_context, in_flow_context, group_id, group_id);
558 
559 	MLX5_SET(flow_context, in_flow_context, flow_tag,
560 		 fte->flow_context.flow_tag);
561 	MLX5_SET(flow_context, in_flow_context, flow_source,
562 		 fte->flow_context.flow_source);
563 
564 	MLX5_SET(flow_context, in_flow_context, extended_destination,
565 		 extended_dest);
566 	if (extended_dest) {
567 		u32 action;
568 
569 		action = fte->action.action &
570 			~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
571 		MLX5_SET(flow_context, in_flow_context, action, action);
572 	} else {
573 		MLX5_SET(flow_context, in_flow_context, action,
574 			 fte->action.action);
575 		if (fte->action.pkt_reformat)
576 			MLX5_SET(flow_context, in_flow_context, packet_reformat_id,
577 				 fte->action.pkt_reformat->id);
578 	}
579 	if (fte->action.modify_hdr)
580 		MLX5_SET(flow_context, in_flow_context, modify_header_id,
581 			 fte->action.modify_hdr->id);
582 
583 	vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan);
584 
585 	MLX5_SET(vlan, vlan, ethtype, fte->action.vlan[0].ethtype);
586 	MLX5_SET(vlan, vlan, vid, fte->action.vlan[0].vid);
587 	MLX5_SET(vlan, vlan, prio, fte->action.vlan[0].prio);
588 
589 	vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan_2);
590 
591 	MLX5_SET(vlan, vlan, ethtype, fte->action.vlan[1].ethtype);
592 	MLX5_SET(vlan, vlan, vid, fte->action.vlan[1].vid);
593 	MLX5_SET(vlan, vlan, prio, fte->action.vlan[1].prio);
594 
595 	in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context,
596 				      match_value);
597 	memcpy(in_match_value, fte->val, sizeof(u32) * MLX5_ST_SZ_DW_MATCH_PARAM);
598 
599 	in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
600 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
601 		int list_size = 0;
602 
603 		for (i = 0; i < fte->dests_size; i++) {
604 			unsigned int id, type = fte->dest_arr[i].type;
605 
606 			if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
607 				continue;
608 
609 			switch (type) {
610 			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM:
611 				id = fte->dest_arr[i].ft_num;
612 				type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
613 				break;
614 			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE:
615 				id = fte->dest_arr[i].ft_id;
616 				break;
617 			case MLX5_FLOW_DESTINATION_TYPE_VPORT:
618 				id = fte->dest_arr[i].vport.num;
619 				MLX5_SET(dest_format_struct, in_dests,
620 					 destination_eswitch_owner_vhca_id_valid,
621 					 !!(fte->dest_arr[i].vport.flags &
622 					    MLX5_FLOW_DEST_VPORT_VHCA_ID));
623 				MLX5_SET(dest_format_struct, in_dests,
624 					 destination_eswitch_owner_vhca_id,
625 					 fte->dest_arr[i].vport.vhca_id);
626 				if (extended_dest && (fte->dest_arr[i].vport.flags &
627 						    MLX5_FLOW_DEST_VPORT_REFORMAT_ID)) {
628 					MLX5_SET(dest_format_struct, in_dests,
629 						 packet_reformat,
630 						 !!(fte->dest_arr[i].vport.flags &
631 						    MLX5_FLOW_DEST_VPORT_REFORMAT_ID));
632 					MLX5_SET(extended_dest_format, in_dests,
633 						 packet_reformat_id,
634 						 fte->dest_arr[i].vport.reformat_id);
635 				}
636 				break;
637 			default:
638 				id = fte->dest_arr[i].tir_num;
639 			}
640 
641 			MLX5_SET(dest_format_struct, in_dests, destination_type,
642 				 type);
643 			MLX5_SET(dest_format_struct, in_dests, destination_id, id);
644 			in_dests += dst_cnt_size;
645 			list_size++;
646 		}
647 
648 		MLX5_SET(flow_context, in_flow_context, destination_list_size,
649 			 list_size);
650 	}
651 
652 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
653 		int max_list_size = BIT(MLX5_CAP_FLOWTABLE_TYPE(dev,
654 					log_max_flow_counter,
655 					ft->type));
656 		int list_size = 0;
657 
658 		for (i = 0; i < fte->dests_size; i++) {
659 			if (fte->dest_arr[i].type !=
660 			    MLX5_FLOW_DESTINATION_TYPE_COUNTER)
661 				continue;
662 
663 			MLX5_SET(flow_counter_list, in_dests, flow_counter_id,
664 				 fte->dest_arr[i].counter_id);
665 			in_dests += dst_cnt_size;
666 			list_size++;
667 		}
668 		if (list_size > max_list_size) {
669 			err = -EINVAL;
670 			goto err_out;
671 		}
672 
673 		MLX5_SET(flow_context, in_flow_context, flow_counter_list_size,
674 			 list_size);
675 	}
676 
677 	err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
678 err_out:
679 	kvfree(in);
680 	return err;
681 }
682