1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2// Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4#include <linux/debugfs.h>
5#include <linux/kernel.h>
6#include <linux/seq_file.h>
7#include <linux/version.h>
8#include "dr_types.h"
9
10#define DR_DBG_PTR_TO_ID(p) ((u64)(uintptr_t)(p) & 0xFFFFFFFFULL)
11
12enum dr_dump_rec_type {
13	DR_DUMP_REC_TYPE_DOMAIN = 3000,
14	DR_DUMP_REC_TYPE_DOMAIN_INFO_FLEX_PARSER = 3001,
15	DR_DUMP_REC_TYPE_DOMAIN_INFO_DEV_ATTR = 3002,
16	DR_DUMP_REC_TYPE_DOMAIN_INFO_VPORT = 3003,
17	DR_DUMP_REC_TYPE_DOMAIN_INFO_CAPS = 3004,
18	DR_DUMP_REC_TYPE_DOMAIN_SEND_RING = 3005,
19
20	DR_DUMP_REC_TYPE_TABLE = 3100,
21	DR_DUMP_REC_TYPE_TABLE_RX = 3101,
22	DR_DUMP_REC_TYPE_TABLE_TX = 3102,
23
24	DR_DUMP_REC_TYPE_MATCHER = 3200,
25	DR_DUMP_REC_TYPE_MATCHER_MASK_DEPRECATED = 3201,
26	DR_DUMP_REC_TYPE_MATCHER_RX = 3202,
27	DR_DUMP_REC_TYPE_MATCHER_TX = 3203,
28	DR_DUMP_REC_TYPE_MATCHER_BUILDER = 3204,
29	DR_DUMP_REC_TYPE_MATCHER_MASK = 3205,
30
31	DR_DUMP_REC_TYPE_RULE = 3300,
32	DR_DUMP_REC_TYPE_RULE_RX_ENTRY_V0 = 3301,
33	DR_DUMP_REC_TYPE_RULE_TX_ENTRY_V0 = 3302,
34	DR_DUMP_REC_TYPE_RULE_RX_ENTRY_V1 = 3303,
35	DR_DUMP_REC_TYPE_RULE_TX_ENTRY_V1 = 3304,
36
37	DR_DUMP_REC_TYPE_ACTION_ENCAP_L2 = 3400,
38	DR_DUMP_REC_TYPE_ACTION_ENCAP_L3 = 3401,
39	DR_DUMP_REC_TYPE_ACTION_MODIFY_HDR = 3402,
40	DR_DUMP_REC_TYPE_ACTION_DROP = 3403,
41	DR_DUMP_REC_TYPE_ACTION_QP = 3404,
42	DR_DUMP_REC_TYPE_ACTION_FT = 3405,
43	DR_DUMP_REC_TYPE_ACTION_CTR = 3406,
44	DR_DUMP_REC_TYPE_ACTION_TAG = 3407,
45	DR_DUMP_REC_TYPE_ACTION_VPORT = 3408,
46	DR_DUMP_REC_TYPE_ACTION_DECAP_L2 = 3409,
47	DR_DUMP_REC_TYPE_ACTION_DECAP_L3 = 3410,
48	DR_DUMP_REC_TYPE_ACTION_DEVX_TIR = 3411,
49	DR_DUMP_REC_TYPE_ACTION_PUSH_VLAN = 3412,
50	DR_DUMP_REC_TYPE_ACTION_POP_VLAN = 3413,
51	DR_DUMP_REC_TYPE_ACTION_SAMPLER = 3415,
52	DR_DUMP_REC_TYPE_ACTION_INSERT_HDR = 3420,
53	DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR = 3421,
54	DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE = 3425,
55};
56
57static struct mlx5dr_dbg_dump_buff *
58mlx5dr_dbg_dump_data_init_new_buff(struct mlx5dr_dbg_dump_data *dump_data)
59{
60	struct mlx5dr_dbg_dump_buff *new_buff;
61
62	new_buff = kzalloc(sizeof(*new_buff), GFP_KERNEL);
63	if (!new_buff)
64		return NULL;
65
66	new_buff->buff = kvzalloc(MLX5DR_DEBUG_DUMP_BUFF_SIZE, GFP_KERNEL);
67	if (!new_buff->buff) {
68		kfree(new_buff);
69		return NULL;
70	}
71
72	INIT_LIST_HEAD(&new_buff->node);
73	list_add_tail(&new_buff->node, &dump_data->buff_list);
74
75	return new_buff;
76}
77
78static struct mlx5dr_dbg_dump_data *
79mlx5dr_dbg_create_dump_data(void)
80{
81	struct mlx5dr_dbg_dump_data *dump_data;
82
83	dump_data = kzalloc(sizeof(*dump_data), GFP_KERNEL);
84	if (!dump_data)
85		return NULL;
86
87	INIT_LIST_HEAD(&dump_data->buff_list);
88
89	if (!mlx5dr_dbg_dump_data_init_new_buff(dump_data)) {
90		kfree(dump_data);
91		return NULL;
92	}
93
94	return dump_data;
95}
96
97static void
98mlx5dr_dbg_destroy_dump_data(struct mlx5dr_dbg_dump_data *dump_data)
99{
100	struct mlx5dr_dbg_dump_buff *dump_buff, *tmp_buff;
101
102	if (!dump_data)
103		return;
104
105	list_for_each_entry_safe(dump_buff, tmp_buff, &dump_data->buff_list, node) {
106		kvfree(dump_buff->buff);
107		list_del(&dump_buff->node);
108		kfree(dump_buff);
109	}
110
111	kfree(dump_data);
112}
113
114static int
115mlx5dr_dbg_dump_data_print(struct seq_file *file, char *str, u32 size)
116{
117	struct mlx5dr_domain *dmn = file->private;
118	struct mlx5dr_dbg_dump_data *dump_data;
119	struct mlx5dr_dbg_dump_buff *buff;
120	u32 buff_capacity, write_size;
121	int remain_size, ret;
122
123	if (size >= MLX5DR_DEBUG_DUMP_BUFF_SIZE)
124		return -EINVAL;
125
126	dump_data = dmn->dump_info.dump_data;
127	buff = list_last_entry(&dump_data->buff_list,
128			       struct mlx5dr_dbg_dump_buff, node);
129
130	buff_capacity = (MLX5DR_DEBUG_DUMP_BUFF_SIZE - 1) - buff->index;
131	remain_size = buff_capacity - size;
132	write_size = (remain_size > 0) ? size : buff_capacity;
133
134	if (likely(write_size)) {
135		ret = snprintf(buff->buff + buff->index, write_size + 1, "%s", str);
136		if (ret < 0)
137			return ret;
138
139		buff->index += write_size;
140	}
141
142	if (remain_size < 0) {
143		remain_size *= -1;
144		buff = mlx5dr_dbg_dump_data_init_new_buff(dump_data);
145		if (!buff)
146			return -ENOMEM;
147
148		ret = snprintf(buff->buff, remain_size + 1, "%s", str + write_size);
149		if (ret < 0)
150			return ret;
151
152		buff->index += remain_size;
153	}
154
155	return 0;
156}
157
158void mlx5dr_dbg_tbl_add(struct mlx5dr_table *tbl)
159{
160	mutex_lock(&tbl->dmn->dump_info.dbg_mutex);
161	list_add_tail(&tbl->dbg_node, &tbl->dmn->dbg_tbl_list);
162	mutex_unlock(&tbl->dmn->dump_info.dbg_mutex);
163}
164
165void mlx5dr_dbg_tbl_del(struct mlx5dr_table *tbl)
166{
167	mutex_lock(&tbl->dmn->dump_info.dbg_mutex);
168	list_del(&tbl->dbg_node);
169	mutex_unlock(&tbl->dmn->dump_info.dbg_mutex);
170}
171
172void mlx5dr_dbg_rule_add(struct mlx5dr_rule *rule)
173{
174	struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
175
176	mutex_lock(&dmn->dump_info.dbg_mutex);
177	list_add_tail(&rule->dbg_node, &rule->matcher->dbg_rule_list);
178	mutex_unlock(&dmn->dump_info.dbg_mutex);
179}
180
181void mlx5dr_dbg_rule_del(struct mlx5dr_rule *rule)
182{
183	struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
184
185	mutex_lock(&dmn->dump_info.dbg_mutex);
186	list_del(&rule->dbg_node);
187	mutex_unlock(&dmn->dump_info.dbg_mutex);
188}
189
190static u64 dr_dump_icm_to_idx(u64 icm_addr)
191{
192	return (icm_addr >> 6) & 0xffffffff;
193}
194
195#define DR_HEX_SIZE 256
196
197static void
198dr_dump_hex_print(char hex[DR_HEX_SIZE], char *src, u32 size)
199{
200	if (WARN_ON_ONCE(DR_HEX_SIZE < 2 * size + 1))
201		size = DR_HEX_SIZE / 2 - 1; /* truncate */
202
203	bin2hex(hex, src, size);
204	hex[2 * size] = 0; /* NULL-terminate */
205}
206
207static int
208dr_dump_rule_action_mem(struct seq_file *file, char *buff, const u64 rule_id,
209			struct mlx5dr_rule_action_member *action_mem)
210{
211	struct mlx5dr_action *action = action_mem->action;
212	const u64 action_id = DR_DBG_PTR_TO_ID(action);
213	u64 hit_tbl_ptr, miss_tbl_ptr;
214	u32 hit_tbl_id, miss_tbl_id;
215	int ret;
216
217	switch (action->action_type) {
218	case DR_ACTION_TYP_DROP:
219		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
220			       "%d,0x%llx,0x%llx\n",
221			       DR_DUMP_REC_TYPE_ACTION_DROP, action_id,
222			       rule_id);
223		if (ret < 0)
224			return ret;
225
226		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
227		if (ret)
228			return ret;
229		break;
230	case DR_ACTION_TYP_FT:
231		if (action->dest_tbl->is_fw_tbl)
232			ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
233				       "%d,0x%llx,0x%llx,0x%x,0x%x\n",
234				       DR_DUMP_REC_TYPE_ACTION_FT, action_id,
235				       rule_id, action->dest_tbl->fw_tbl.id,
236				       -1);
237		else
238			ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
239				       "%d,0x%llx,0x%llx,0x%x,0x%llx\n",
240				       DR_DUMP_REC_TYPE_ACTION_FT, action_id,
241				       rule_id, action->dest_tbl->tbl->table_id,
242				       DR_DBG_PTR_TO_ID(action->dest_tbl->tbl));
243
244		if (ret < 0)
245			return ret;
246
247		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
248		if (ret)
249			return ret;
250		break;
251	case DR_ACTION_TYP_CTR:
252		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
253			       "%d,0x%llx,0x%llx,0x%x\n",
254			       DR_DUMP_REC_TYPE_ACTION_CTR, action_id, rule_id,
255			       action->ctr->ctr_id + action->ctr->offset);
256		if (ret < 0)
257			return ret;
258
259		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
260		if (ret)
261			return ret;
262		break;
263	case DR_ACTION_TYP_TAG:
264		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
265			       "%d,0x%llx,0x%llx,0x%x\n",
266			       DR_DUMP_REC_TYPE_ACTION_TAG, action_id, rule_id,
267			       action->flow_tag->flow_tag);
268		if (ret < 0)
269			return ret;
270
271		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
272		if (ret)
273			return ret;
274		break;
275	case DR_ACTION_TYP_MODIFY_HDR:
276	{
277		struct mlx5dr_ptrn_obj *ptrn = action->rewrite->ptrn;
278		struct mlx5dr_arg_obj *arg = action->rewrite->arg;
279		u8 *rewrite_data = action->rewrite->data;
280		bool ptrn_arg;
281		int i;
282
283		ptrn_arg = !action->rewrite->single_action_opt && ptrn && arg;
284
285		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
286			       "%d,0x%llx,0x%llx,0x%x,%d,0x%x,0x%x,0x%x",
287			       DR_DUMP_REC_TYPE_ACTION_MODIFY_HDR, action_id,
288			       rule_id, action->rewrite->index,
289			       action->rewrite->single_action_opt,
290			       ptrn_arg ? action->rewrite->num_of_actions : 0,
291			       ptrn_arg ? ptrn->index : 0,
292			       ptrn_arg ? mlx5dr_arg_get_obj_id(arg) : 0);
293		if (ret < 0)
294			return ret;
295
296		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
297		if (ret)
298			return ret;
299
300		if (ptrn_arg) {
301			for (i = 0; i < action->rewrite->num_of_actions; i++) {
302				ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
303					       ",0x%016llx",
304					       be64_to_cpu(((__be64 *)rewrite_data)[i]));
305				if (ret < 0)
306					return ret;
307
308				ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
309				if (ret)
310					return ret;
311			}
312		}
313
314		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, "\n");
315		if (ret < 0)
316			return ret;
317		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
318		if (ret)
319			return ret;
320		break;
321	}
322	case DR_ACTION_TYP_VPORT:
323		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
324			       "%d,0x%llx,0x%llx,0x%x\n",
325			       DR_DUMP_REC_TYPE_ACTION_VPORT, action_id, rule_id,
326			       action->vport->caps->num);
327		if (ret < 0)
328			return ret;
329
330		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
331		if (ret)
332			return ret;
333		break;
334	case DR_ACTION_TYP_TNL_L2_TO_L2:
335		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
336			       "%d,0x%llx,0x%llx\n",
337			       DR_DUMP_REC_TYPE_ACTION_DECAP_L2, action_id,
338			       rule_id);
339		if (ret < 0)
340			return ret;
341
342		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
343		if (ret)
344			return ret;
345		break;
346	case DR_ACTION_TYP_TNL_L3_TO_L2:
347		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
348			       "%d,0x%llx,0x%llx,0x%x\n",
349			       DR_DUMP_REC_TYPE_ACTION_DECAP_L3, action_id,
350			       rule_id,
351			       (action->rewrite->ptrn && action->rewrite->arg) ?
352			       mlx5dr_arg_get_obj_id(action->rewrite->arg) :
353			       action->rewrite->index);
354		if (ret < 0)
355			return ret;
356
357		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
358		if (ret)
359			return ret;
360		break;
361	case DR_ACTION_TYP_L2_TO_TNL_L2:
362		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
363			       "%d,0x%llx,0x%llx,0x%x\n",
364			       DR_DUMP_REC_TYPE_ACTION_ENCAP_L2, action_id,
365			       rule_id, action->reformat->id);
366		if (ret < 0)
367			return ret;
368
369		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
370		if (ret)
371			return ret;
372		break;
373	case DR_ACTION_TYP_L2_TO_TNL_L3:
374		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
375			       "%d,0x%llx,0x%llx,0x%x\n",
376			       DR_DUMP_REC_TYPE_ACTION_ENCAP_L3, action_id,
377			       rule_id, action->reformat->id);
378		if (ret < 0)
379			return ret;
380
381		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
382		if (ret)
383			return ret;
384		break;
385	case DR_ACTION_TYP_POP_VLAN:
386		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
387			       "%d,0x%llx,0x%llx\n",
388			       DR_DUMP_REC_TYPE_ACTION_POP_VLAN, action_id,
389			       rule_id);
390		if (ret < 0)
391			return ret;
392
393		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
394		if (ret)
395			return ret;
396		break;
397	case DR_ACTION_TYP_PUSH_VLAN:
398		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
399			       "%d,0x%llx,0x%llx,0x%x\n",
400			       DR_DUMP_REC_TYPE_ACTION_PUSH_VLAN, action_id,
401			       rule_id, action->push_vlan->vlan_hdr);
402		if (ret < 0)
403			return ret;
404
405		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
406		if (ret)
407			return ret;
408		break;
409	case DR_ACTION_TYP_INSERT_HDR:
410		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
411			       "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n",
412			       DR_DUMP_REC_TYPE_ACTION_INSERT_HDR, action_id,
413			       rule_id, action->reformat->id,
414			       action->reformat->param_0,
415			       action->reformat->param_1);
416		if (ret < 0)
417			return ret;
418
419		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
420		if (ret)
421			return ret;
422		break;
423	case DR_ACTION_TYP_REMOVE_HDR:
424		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
425			       "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x\n",
426			       DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR, action_id,
427			       rule_id, action->reformat->id,
428			       action->reformat->param_0,
429			       action->reformat->param_1);
430		if (ret < 0)
431			return ret;
432
433		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
434		if (ret)
435			return ret;
436		break;
437	case DR_ACTION_TYP_SAMPLER:
438		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
439			       "%d,0x%llx,0x%llx,0x%x,0x%x,0x%x,0x%llx,0x%llx\n",
440			       DR_DUMP_REC_TYPE_ACTION_SAMPLER, action_id,
441			       rule_id, 0, 0, action->sampler->sampler_id,
442			       action->sampler->rx_icm_addr,
443			       action->sampler->tx_icm_addr);
444		if (ret < 0)
445			return ret;
446
447		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
448		if (ret)
449			return ret;
450		break;
451	case DR_ACTION_TYP_RANGE:
452		if (action->range->hit_tbl_action->dest_tbl->is_fw_tbl) {
453			hit_tbl_id = action->range->hit_tbl_action->dest_tbl->fw_tbl.id;
454			hit_tbl_ptr = 0;
455		} else {
456			hit_tbl_id = action->range->hit_tbl_action->dest_tbl->tbl->table_id;
457			hit_tbl_ptr =
458				DR_DBG_PTR_TO_ID(action->range->hit_tbl_action->dest_tbl->tbl);
459		}
460
461		if (action->range->miss_tbl_action->dest_tbl->is_fw_tbl) {
462			miss_tbl_id = action->range->miss_tbl_action->dest_tbl->fw_tbl.id;
463			miss_tbl_ptr = 0;
464		} else {
465			miss_tbl_id = action->range->miss_tbl_action->dest_tbl->tbl->table_id;
466			miss_tbl_ptr =
467				DR_DBG_PTR_TO_ID(action->range->miss_tbl_action->dest_tbl->tbl);
468		}
469
470		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
471			       "%d,0x%llx,0x%llx,0x%x,0x%llx,0x%x,0x%llx,0x%x\n",
472			       DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE, action_id,
473			       rule_id, hit_tbl_id, hit_tbl_ptr, miss_tbl_id,
474			       miss_tbl_ptr, action->range->definer_id);
475		if (ret < 0)
476			return ret;
477
478		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
479		if (ret)
480			return ret;
481		break;
482	default:
483		return 0;
484	}
485
486	return 0;
487}
488
489static int
490dr_dump_rule_mem(struct seq_file *file, char *buff, struct mlx5dr_ste *ste,
491		 bool is_rx, const u64 rule_id, u8 format_ver)
492{
493	char hw_ste_dump[DR_HEX_SIZE];
494	u32 mem_rec_type;
495	int ret;
496
497	if (format_ver == MLX5_STEERING_FORMAT_CONNECTX_5) {
498		mem_rec_type = is_rx ? DR_DUMP_REC_TYPE_RULE_RX_ENTRY_V0 :
499				       DR_DUMP_REC_TYPE_RULE_TX_ENTRY_V0;
500	} else {
501		mem_rec_type = is_rx ? DR_DUMP_REC_TYPE_RULE_RX_ENTRY_V1 :
502				       DR_DUMP_REC_TYPE_RULE_TX_ENTRY_V1;
503	}
504
505	dr_dump_hex_print(hw_ste_dump, (char *)mlx5dr_ste_get_hw_ste(ste),
506			  DR_STE_SIZE_REDUCED);
507
508	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
509		       "%d,0x%llx,0x%llx,%s\n", mem_rec_type,
510		       dr_dump_icm_to_idx(mlx5dr_ste_get_icm_addr(ste)),
511		       rule_id, hw_ste_dump);
512	if (ret < 0)
513		return ret;
514
515	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
516	if (ret)
517		return ret;
518
519	return 0;
520}
521
522static int
523dr_dump_rule_rx_tx(struct seq_file *file, char *buff,
524		   struct mlx5dr_rule_rx_tx *rule_rx_tx,
525		   bool is_rx, const u64 rule_id, u8 format_ver)
526{
527	struct mlx5dr_ste *ste_arr[DR_RULE_MAX_STES + DR_ACTION_MAX_STES];
528	struct mlx5dr_ste *curr_ste = rule_rx_tx->last_rule_ste;
529	int ret, i;
530
531	if (mlx5dr_rule_get_reverse_rule_members(ste_arr, curr_ste, &i))
532		return 0;
533
534	while (i--) {
535		ret = dr_dump_rule_mem(file, buff, ste_arr[i], is_rx, rule_id,
536				       format_ver);
537		if (ret < 0)
538			return ret;
539	}
540
541	return 0;
542}
543
544static noinline_for_stack int
545dr_dump_rule(struct seq_file *file, struct mlx5dr_rule *rule)
546{
547	struct mlx5dr_rule_action_member *action_mem;
548	const u64 rule_id = DR_DBG_PTR_TO_ID(rule);
549	char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH];
550	struct mlx5dr_rule_rx_tx *rx = &rule->rx;
551	struct mlx5dr_rule_rx_tx *tx = &rule->tx;
552	u8 format_ver;
553	int ret;
554
555	format_ver = rule->matcher->tbl->dmn->info.caps.sw_format_ver;
556
557	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
558		       "%d,0x%llx,0x%llx\n", DR_DUMP_REC_TYPE_RULE,
559		       rule_id, DR_DBG_PTR_TO_ID(rule->matcher));
560	if (ret < 0)
561		return ret;
562
563	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
564	if (ret)
565		return ret;
566
567	if (rx->nic_matcher) {
568		ret = dr_dump_rule_rx_tx(file, buff, rx, true, rule_id, format_ver);
569		if (ret < 0)
570			return ret;
571	}
572
573	if (tx->nic_matcher) {
574		ret = dr_dump_rule_rx_tx(file, buff, tx, false, rule_id, format_ver);
575		if (ret < 0)
576			return ret;
577	}
578
579	list_for_each_entry(action_mem, &rule->rule_actions_list, list) {
580		ret = dr_dump_rule_action_mem(file, buff, rule_id, action_mem);
581		if (ret < 0)
582			return ret;
583	}
584
585	return 0;
586}
587
588static int
589dr_dump_matcher_mask(struct seq_file *file, char *buff,
590		     struct mlx5dr_match_param *mask,
591		     u8 criteria, const u64 matcher_id)
592{
593	char dump[DR_HEX_SIZE];
594	int ret;
595
596	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, "%d,0x%llx,",
597		       DR_DUMP_REC_TYPE_MATCHER_MASK, matcher_id);
598	if (ret < 0)
599		return ret;
600
601	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
602	if (ret)
603		return ret;
604
605	if (criteria & DR_MATCHER_CRITERIA_OUTER) {
606		dr_dump_hex_print(dump, (char *)&mask->outer, sizeof(mask->outer));
607		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
608			       "%s,", dump);
609	} else {
610		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",");
611	}
612
613	if (ret < 0)
614		return ret;
615
616	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
617	if (ret)
618		return ret;
619
620	if (criteria & DR_MATCHER_CRITERIA_INNER) {
621		dr_dump_hex_print(dump, (char *)&mask->inner, sizeof(mask->inner));
622		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
623			       "%s,", dump);
624	} else {
625		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",");
626	}
627
628	if (ret < 0)
629		return ret;
630
631	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
632	if (ret)
633		return ret;
634
635	if (criteria & DR_MATCHER_CRITERIA_MISC) {
636		dr_dump_hex_print(dump, (char *)&mask->misc, sizeof(mask->misc));
637		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
638			       "%s,", dump);
639	} else {
640		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",");
641	}
642
643	if (ret < 0)
644		return ret;
645
646	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
647	if (ret)
648		return ret;
649
650	if (criteria & DR_MATCHER_CRITERIA_MISC2) {
651		dr_dump_hex_print(dump, (char *)&mask->misc2, sizeof(mask->misc2));
652		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
653			       "%s,", dump);
654	} else {
655		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",");
656	}
657
658	if (ret < 0)
659		return ret;
660
661	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
662	if (ret)
663		return ret;
664
665	if (criteria & DR_MATCHER_CRITERIA_MISC3) {
666		dr_dump_hex_print(dump, (char *)&mask->misc3, sizeof(mask->misc3));
667		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
668			       "%s\n", dump);
669	} else {
670		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH, ",\n");
671	}
672
673	if (ret < 0)
674		return ret;
675
676	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
677	if (ret)
678		return ret;
679
680	return 0;
681}
682
683static int
684dr_dump_matcher_builder(struct seq_file *file, char *buff,
685			struct mlx5dr_ste_build *builder,
686			u32 index, bool is_rx, const u64 matcher_id)
687{
688	int ret;
689
690	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
691		       "%d,0x%llx,%d,%d,0x%x\n",
692		       DR_DUMP_REC_TYPE_MATCHER_BUILDER, matcher_id, index,
693		       is_rx, builder->lu_type);
694	if (ret < 0)
695		return ret;
696
697	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
698	if (ret)
699		return ret;
700
701	return 0;
702}
703
704static int
705dr_dump_matcher_rx_tx(struct seq_file *file, char *buff, bool is_rx,
706		      struct mlx5dr_matcher_rx_tx *matcher_rx_tx,
707		      const u64 matcher_id)
708{
709	enum dr_dump_rec_type rec_type;
710	u64 s_icm_addr, e_icm_addr;
711	int i, ret;
712
713	rec_type = is_rx ? DR_DUMP_REC_TYPE_MATCHER_RX :
714			   DR_DUMP_REC_TYPE_MATCHER_TX;
715
716	s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->s_htbl->chunk);
717	e_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(matcher_rx_tx->e_anchor->chunk);
718	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
719		       "%d,0x%llx,0x%llx,%d,0x%llx,0x%llx\n",
720		       rec_type, DR_DBG_PTR_TO_ID(matcher_rx_tx),
721		       matcher_id, matcher_rx_tx->num_of_builders,
722		       dr_dump_icm_to_idx(s_icm_addr),
723		       dr_dump_icm_to_idx(e_icm_addr));
724
725	if (ret < 0)
726		return ret;
727
728	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
729	if (ret)
730		return ret;
731
732	for (i = 0; i < matcher_rx_tx->num_of_builders; i++) {
733		ret = dr_dump_matcher_builder(file, buff,
734					      &matcher_rx_tx->ste_builder[i],
735					      i, is_rx, matcher_id);
736		if (ret < 0)
737			return ret;
738	}
739
740	return 0;
741}
742
743static noinline_for_stack int
744dr_dump_matcher(struct seq_file *file, struct mlx5dr_matcher *matcher)
745{
746	struct mlx5dr_matcher_rx_tx *rx = &matcher->rx;
747	struct mlx5dr_matcher_rx_tx *tx = &matcher->tx;
748	char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH];
749	u64 matcher_id;
750	int ret;
751
752	matcher_id = DR_DBG_PTR_TO_ID(matcher);
753
754	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
755		       "%d,0x%llx,0x%llx,%d\n", DR_DUMP_REC_TYPE_MATCHER,
756		       matcher_id, DR_DBG_PTR_TO_ID(matcher->tbl),
757		       matcher->prio);
758	if (ret < 0)
759		return ret;
760
761	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
762	if (ret)
763		return ret;
764
765	ret = dr_dump_matcher_mask(file, buff, &matcher->mask,
766				   matcher->match_criteria, matcher_id);
767	if (ret < 0)
768		return ret;
769
770	if (rx->nic_tbl) {
771		ret = dr_dump_matcher_rx_tx(file, buff, true, rx, matcher_id);
772		if (ret < 0)
773			return ret;
774	}
775
776	if (tx->nic_tbl) {
777		ret = dr_dump_matcher_rx_tx(file, buff, false, tx, matcher_id);
778		if (ret < 0)
779			return ret;
780	}
781
782	return 0;
783}
784
785static int
786dr_dump_matcher_all(struct seq_file *file, struct mlx5dr_matcher *matcher)
787{
788	struct mlx5dr_rule *rule;
789	int ret;
790
791	ret = dr_dump_matcher(file, matcher);
792	if (ret < 0)
793		return ret;
794
795	list_for_each_entry(rule, &matcher->dbg_rule_list, dbg_node) {
796		ret = dr_dump_rule(file, rule);
797		if (ret < 0)
798			return ret;
799	}
800
801	return 0;
802}
803
804static int
805dr_dump_table_rx_tx(struct seq_file *file, char *buff, bool is_rx,
806		    struct mlx5dr_table_rx_tx *table_rx_tx,
807		    const u64 table_id)
808{
809	enum dr_dump_rec_type rec_type;
810	u64 s_icm_addr;
811	int ret;
812
813	rec_type = is_rx ? DR_DUMP_REC_TYPE_TABLE_RX :
814			   DR_DUMP_REC_TYPE_TABLE_TX;
815
816	s_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(table_rx_tx->s_anchor->chunk);
817	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
818		       "%d,0x%llx,0x%llx\n", rec_type, table_id,
819		       dr_dump_icm_to_idx(s_icm_addr));
820	if (ret < 0)
821		return ret;
822
823	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
824	if (ret)
825		return ret;
826
827	return 0;
828}
829
830static noinline_for_stack int
831dr_dump_table(struct seq_file *file, struct mlx5dr_table *table)
832{
833	struct mlx5dr_table_rx_tx *rx = &table->rx;
834	struct mlx5dr_table_rx_tx *tx = &table->tx;
835	char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH];
836	int ret;
837
838	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
839		       "%d,0x%llx,0x%llx,%d,%d\n", DR_DUMP_REC_TYPE_TABLE,
840		       DR_DBG_PTR_TO_ID(table), DR_DBG_PTR_TO_ID(table->dmn),
841		       table->table_type, table->level);
842	if (ret < 0)
843		return ret;
844
845	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
846	if (ret)
847		return ret;
848
849	if (rx->nic_dmn) {
850		ret = dr_dump_table_rx_tx(file, buff, true, rx,
851					  DR_DBG_PTR_TO_ID(table));
852		if (ret < 0)
853			return ret;
854	}
855
856	if (tx->nic_dmn) {
857		ret = dr_dump_table_rx_tx(file, buff, false, tx,
858					  DR_DBG_PTR_TO_ID(table));
859		if (ret < 0)
860			return ret;
861	}
862	return 0;
863}
864
865static int dr_dump_table_all(struct seq_file *file, struct mlx5dr_table *tbl)
866{
867	struct mlx5dr_matcher *matcher;
868	int ret;
869
870	ret = dr_dump_table(file, tbl);
871	if (ret < 0)
872		return ret;
873
874	list_for_each_entry(matcher, &tbl->matcher_list, list_node) {
875		ret = dr_dump_matcher_all(file, matcher);
876		if (ret < 0)
877			return ret;
878	}
879	return 0;
880}
881
882static int
883dr_dump_send_ring(struct seq_file *file, char *buff,
884		  struct mlx5dr_send_ring *ring,
885		  const u64 domain_id)
886{
887	int ret;
888
889	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
890		       "%d,0x%llx,0x%llx,0x%x,0x%x\n",
891		       DR_DUMP_REC_TYPE_DOMAIN_SEND_RING,
892		       DR_DBG_PTR_TO_ID(ring), domain_id,
893		       ring->cq->mcq.cqn, ring->qp->qpn);
894	if (ret < 0)
895		return ret;
896
897	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
898	if (ret)
899		return ret;
900
901	return 0;
902}
903
904static int
905dr_dump_domain_info_flex_parser(struct seq_file *file,
906				char *buff,
907				const char *flex_parser_name,
908				const u8 flex_parser_value,
909				const u64 domain_id)
910{
911	int ret;
912
913	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
914		       "%d,0x%llx,%s,0x%x\n",
915		       DR_DUMP_REC_TYPE_DOMAIN_INFO_FLEX_PARSER, domain_id,
916		       flex_parser_name, flex_parser_value);
917	if (ret < 0)
918		return ret;
919
920	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
921	if (ret)
922		return ret;
923
924	return 0;
925}
926
927static int
928dr_dump_domain_info_caps(struct seq_file *file, char *buff,
929			 struct mlx5dr_cmd_caps *caps,
930			 const u64 domain_id)
931{
932	struct mlx5dr_cmd_vport_cap *vport_caps;
933	unsigned long i, vports_num;
934	int ret;
935
936	xa_for_each(&caps->vports.vports_caps_xa, vports_num, vport_caps)
937		; /* count the number of vports in xarray */
938
939	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
940		       "%d,0x%llx,0x%x,0x%llx,0x%llx,0x%x,%lu,%d\n",
941		       DR_DUMP_REC_TYPE_DOMAIN_INFO_CAPS, domain_id, caps->gvmi,
942		       caps->nic_rx_drop_address, caps->nic_tx_drop_address,
943		       caps->flex_protocols, vports_num, caps->eswitch_manager);
944	if (ret < 0)
945		return ret;
946
947	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
948	if (ret)
949		return ret;
950
951	xa_for_each(&caps->vports.vports_caps_xa, i, vport_caps) {
952		vport_caps = xa_load(&caps->vports.vports_caps_xa, i);
953
954		ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
955			       "%d,0x%llx,%lu,0x%x,0x%llx,0x%llx\n",
956			       DR_DUMP_REC_TYPE_DOMAIN_INFO_VPORT,
957			       domain_id, i, vport_caps->vport_gvmi,
958			       vport_caps->icm_address_rx,
959			       vport_caps->icm_address_tx);
960		if (ret < 0)
961			return ret;
962
963		ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
964		if (ret)
965			return ret;
966	}
967	return 0;
968}
969
970static int
971dr_dump_domain_info(struct seq_file *file, char *buff,
972		    struct mlx5dr_domain_info *info,
973		    const u64 domain_id)
974{
975	int ret;
976
977	ret = dr_dump_domain_info_caps(file, buff, &info->caps, domain_id);
978	if (ret < 0)
979		return ret;
980
981	ret = dr_dump_domain_info_flex_parser(file, buff, "icmp_dw0",
982					      info->caps.flex_parser_id_icmp_dw0,
983					      domain_id);
984	if (ret < 0)
985		return ret;
986
987	ret = dr_dump_domain_info_flex_parser(file, buff, "icmp_dw1",
988					      info->caps.flex_parser_id_icmp_dw1,
989					      domain_id);
990	if (ret < 0)
991		return ret;
992
993	ret = dr_dump_domain_info_flex_parser(file, buff, "icmpv6_dw0",
994					      info->caps.flex_parser_id_icmpv6_dw0,
995					      domain_id);
996	if (ret < 0)
997		return ret;
998
999	ret = dr_dump_domain_info_flex_parser(file, buff, "icmpv6_dw1",
1000					      info->caps.flex_parser_id_icmpv6_dw1,
1001					      domain_id);
1002	if (ret < 0)
1003		return ret;
1004
1005	return 0;
1006}
1007
1008static noinline_for_stack int
1009dr_dump_domain(struct seq_file *file, struct mlx5dr_domain *dmn)
1010{
1011	char buff[MLX5DR_DEBUG_DUMP_BUFF_LENGTH];
1012	u64 domain_id = DR_DBG_PTR_TO_ID(dmn);
1013	int ret;
1014
1015	ret = snprintf(buff, MLX5DR_DEBUG_DUMP_BUFF_LENGTH,
1016		       "%d,0x%llx,%d,0%x,%d,%u.%u.%u,%s,%d,%u,%u,%u\n",
1017		       DR_DUMP_REC_TYPE_DOMAIN,
1018		       domain_id, dmn->type, dmn->info.caps.gvmi,
1019		       dmn->info.supp_sw_steering,
1020		       /* package version */
1021		       LINUX_VERSION_MAJOR, LINUX_VERSION_PATCHLEVEL,
1022		       LINUX_VERSION_SUBLEVEL,
1023		       pci_name(dmn->mdev->pdev),
1024		       0, /* domain flags */
1025		       dmn->num_buddies[DR_ICM_TYPE_STE],
1026		       dmn->num_buddies[DR_ICM_TYPE_MODIFY_ACTION],
1027		       dmn->num_buddies[DR_ICM_TYPE_MODIFY_HDR_PTRN]);
1028	if (ret < 0)
1029		return ret;
1030
1031	ret = mlx5dr_dbg_dump_data_print(file, buff, ret);
1032	if (ret)
1033		return ret;
1034
1035	ret = dr_dump_domain_info(file, buff, &dmn->info, domain_id);
1036	if (ret < 0)
1037		return ret;
1038
1039	if (dmn->info.supp_sw_steering) {
1040		ret = dr_dump_send_ring(file, buff, dmn->send_ring, domain_id);
1041		if (ret < 0)
1042			return ret;
1043	}
1044
1045	return 0;
1046}
1047
1048static int dr_dump_domain_all(struct seq_file *file, struct mlx5dr_domain *dmn)
1049{
1050	struct mlx5dr_table *tbl;
1051	int ret;
1052
1053	mutex_lock(&dmn->dump_info.dbg_mutex);
1054	mlx5dr_domain_lock(dmn);
1055
1056	ret = dr_dump_domain(file, dmn);
1057	if (ret < 0)
1058		goto unlock_mutex;
1059
1060	list_for_each_entry(tbl, &dmn->dbg_tbl_list, dbg_node) {
1061		ret = dr_dump_table_all(file, tbl);
1062		if (ret < 0)
1063			break;
1064	}
1065
1066unlock_mutex:
1067	mlx5dr_domain_unlock(dmn);
1068	mutex_unlock(&dmn->dump_info.dbg_mutex);
1069	return ret;
1070}
1071
1072static void *
1073dr_dump_start(struct seq_file *file, loff_t *pos)
1074{
1075	struct mlx5dr_domain *dmn = file->private;
1076	struct mlx5dr_dbg_dump_data *dump_data;
1077
1078	if (atomic_read(&dmn->dump_info.state) != MLX5DR_DEBUG_DUMP_STATE_FREE) {
1079		mlx5_core_warn(dmn->mdev, "Dump already in progress\n");
1080		return ERR_PTR(-EBUSY);
1081	}
1082
1083	atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_IN_PROGRESS);
1084	dump_data = dmn->dump_info.dump_data;
1085
1086	if (dump_data) {
1087		return seq_list_start(&dump_data->buff_list, *pos);
1088	} else if (*pos == 0) {
1089		dump_data = mlx5dr_dbg_create_dump_data();
1090		if (!dump_data)
1091			goto exit;
1092
1093		dmn->dump_info.dump_data = dump_data;
1094		if (dr_dump_domain_all(file, dmn)) {
1095			mlx5dr_dbg_destroy_dump_data(dump_data);
1096			dmn->dump_info.dump_data = NULL;
1097			goto exit;
1098		}
1099
1100		return seq_list_start(&dump_data->buff_list, *pos);
1101	}
1102
1103exit:
1104	atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_FREE);
1105	return NULL;
1106}
1107
1108static void *
1109dr_dump_next(struct seq_file *file, void *v, loff_t *pos)
1110{
1111	struct mlx5dr_domain *dmn = file->private;
1112	struct mlx5dr_dbg_dump_data *dump_data;
1113
1114	dump_data = dmn->dump_info.dump_data;
1115
1116	return seq_list_next(v, &dump_data->buff_list, pos);
1117}
1118
1119static void
1120dr_dump_stop(struct seq_file *file, void *v)
1121{
1122	struct mlx5dr_domain *dmn = file->private;
1123	struct mlx5dr_dbg_dump_data *dump_data;
1124
1125	if (v && IS_ERR(v))
1126		return;
1127
1128	if (!v) {
1129		dump_data = dmn->dump_info.dump_data;
1130		if (dump_data) {
1131			mlx5dr_dbg_destroy_dump_data(dump_data);
1132			dmn->dump_info.dump_data = NULL;
1133		}
1134	}
1135
1136	atomic_set(&dmn->dump_info.state, MLX5DR_DEBUG_DUMP_STATE_FREE);
1137}
1138
1139static int
1140dr_dump_show(struct seq_file *file, void *v)
1141{
1142	struct mlx5dr_dbg_dump_buff *entry;
1143
1144	entry = list_entry(v, struct mlx5dr_dbg_dump_buff, node);
1145	seq_printf(file, "%s", entry->buff);
1146
1147	return 0;
1148}
1149
1150static const struct seq_operations dr_dump_sops = {
1151	.start	= dr_dump_start,
1152	.next	= dr_dump_next,
1153	.stop	= dr_dump_stop,
1154	.show	= dr_dump_show,
1155};
1156DEFINE_SEQ_ATTRIBUTE(dr_dump);
1157
1158void mlx5dr_dbg_init_dump(struct mlx5dr_domain *dmn)
1159{
1160	struct mlx5_core_dev *dev = dmn->mdev;
1161	char file_name[128];
1162
1163	if (dmn->type != MLX5DR_DOMAIN_TYPE_FDB) {
1164		mlx5_core_warn(dev,
1165			       "Steering dump is not supported for NIC RX/TX domains\n");
1166		return;
1167	}
1168
1169	dmn->dump_info.steering_debugfs =
1170		debugfs_create_dir("steering", mlx5_debugfs_get_dev_root(dev));
1171	dmn->dump_info.fdb_debugfs =
1172		debugfs_create_dir("fdb", dmn->dump_info.steering_debugfs);
1173
1174	sprintf(file_name, "dmn_%p", dmn);
1175	debugfs_create_file(file_name, 0444, dmn->dump_info.fdb_debugfs,
1176			    dmn, &dr_dump_fops);
1177
1178	INIT_LIST_HEAD(&dmn->dbg_tbl_list);
1179	mutex_init(&dmn->dump_info.dbg_mutex);
1180}
1181
1182void mlx5dr_dbg_uninit_dump(struct mlx5dr_domain *dmn)
1183{
1184	debugfs_remove_recursive(dmn->dump_info.steering_debugfs);
1185	mutex_destroy(&dmn->dump_info.dbg_mutex);
1186}
1187