xref: /linux/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c (revision 0e50474fa514822e9d990874e554bf8043a201d7)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2025 AIROHA Inc
4  * Author: Lorenzo Bianconi <lorenzo@kernel.org>
5  */
6 
7 #include "airoha_eth.h"
8 
9 static void airoha_debugfs_ppe_print_tuple(struct seq_file *m,
10 					   void *src_addr, void *dest_addr,
11 					   u16 *src_port, u16 *dest_port,
12 					   bool ipv6)
13 {
14 	__be32 n_addr[IPV6_ADDR_WORDS];
15 
16 	if (ipv6) {
17 		ipv6_addr_cpu_to_be32(n_addr, src_addr);
18 		seq_printf(m, "%pI6", n_addr);
19 	} else {
20 		seq_printf(m, "%pI4h", src_addr);
21 	}
22 	if (src_port)
23 		seq_printf(m, ":%d", *src_port);
24 
25 	seq_puts(m, "->");
26 
27 	if (ipv6) {
28 		ipv6_addr_cpu_to_be32(n_addr, dest_addr);
29 		seq_printf(m, "%pI6", n_addr);
30 	} else {
31 		seq_printf(m, "%pI4h", dest_addr);
32 	}
33 	if (dest_port)
34 		seq_printf(m, ":%d", *dest_port);
35 }
36 
37 static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
38 				       bool bind)
39 {
40 	static const char *const ppe_type_str[] = {
41 		[PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
42 		[PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
43 		[PPE_PKT_TYPE_BRIDGE] = "L2B",
44 		[PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
45 		[PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
46 		[PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
47 		[PPE_PKT_TYPE_IPV6_6RD] = "6RD",
48 	};
49 	static const char *const ppe_state_str[] = {
50 		[AIROHA_FOE_STATE_INVALID] = "INV",
51 		[AIROHA_FOE_STATE_UNBIND] = "UNB",
52 		[AIROHA_FOE_STATE_BIND] = "BND",
53 		[AIROHA_FOE_STATE_FIN] = "FIN",
54 	};
55 	struct airoha_ppe *ppe = m->private;
56 	u32 ppe_num_entries = airoha_ppe_get_total_num_entries(ppe);
57 	int i;
58 
59 	for (i = 0; i < ppe_num_entries; i++) {
60 		const char *state_str, *type_str = "UNKNOWN";
61 		void *src_addr = NULL, *dest_addr = NULL;
62 		u16 *src_port = NULL, *dest_port = NULL;
63 		struct airoha_foe_mac_info_common *l2;
64 		unsigned char h_source[ETH_ALEN] = {};
65 		struct airoha_foe_stats64 stats = {};
66 		unsigned char h_dest[ETH_ALEN];
67 		struct airoha_foe_entry *hwe;
68 		u32 type, state, ib2, data;
69 		bool ipv6 = false;
70 
71 		hwe = airoha_ppe_foe_get_entry(ppe, i);
72 		if (!hwe)
73 			continue;
74 
75 		state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
76 		if (!state)
77 			continue;
78 
79 		if (bind && state != AIROHA_FOE_STATE_BIND)
80 			continue;
81 
82 		state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)];
83 		type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
84 		if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type])
85 			type_str = ppe_type_str[type];
86 
87 		seq_printf(m, "%05x %s %7s", i, state_str, type_str);
88 
89 		switch (type) {
90 		case PPE_PKT_TYPE_IPV4_HNAPT:
91 		case PPE_PKT_TYPE_IPV4_DSLITE:
92 			src_port = &hwe->ipv4.orig_tuple.src_port;
93 			dest_port = &hwe->ipv4.orig_tuple.dest_port;
94 			fallthrough;
95 		case PPE_PKT_TYPE_IPV4_ROUTE:
96 			src_addr = &hwe->ipv4.orig_tuple.src_ip;
97 			dest_addr = &hwe->ipv4.orig_tuple.dest_ip;
98 			break;
99 		case PPE_PKT_TYPE_IPV6_ROUTE_5T:
100 			src_port = &hwe->ipv6.src_port;
101 			dest_port = &hwe->ipv6.dest_port;
102 			fallthrough;
103 		case PPE_PKT_TYPE_IPV6_ROUTE_3T:
104 		case PPE_PKT_TYPE_IPV6_6RD:
105 			src_addr = &hwe->ipv6.src_ip;
106 			dest_addr = &hwe->ipv6.dest_ip;
107 			ipv6 = true;
108 			break;
109 		default:
110 			break;
111 		}
112 
113 		if (src_addr && dest_addr) {
114 			seq_puts(m, " orig=");
115 			airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
116 						       src_port, dest_port, ipv6);
117 		}
118 
119 		switch (type) {
120 		case PPE_PKT_TYPE_IPV4_HNAPT:
121 		case PPE_PKT_TYPE_IPV4_DSLITE:
122 			src_port = &hwe->ipv4.new_tuple.src_port;
123 			dest_port = &hwe->ipv4.new_tuple.dest_port;
124 			fallthrough;
125 		case PPE_PKT_TYPE_IPV4_ROUTE:
126 			src_addr = &hwe->ipv4.new_tuple.src_ip;
127 			dest_addr = &hwe->ipv4.new_tuple.dest_ip;
128 			seq_puts(m, " new=");
129 			airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
130 						       src_port, dest_port,
131 						       ipv6);
132 			break;
133 		default:
134 			break;
135 		}
136 
137 		if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
138 			data = hwe->ipv6.data;
139 			ib2 = hwe->ipv6.ib2;
140 			l2 = &hwe->ipv6.l2;
141 		} else {
142 			data = hwe->ipv4.data;
143 			ib2 = hwe->ipv4.ib2;
144 			l2 = &hwe->ipv4.l2.common;
145 			*((__be16 *)&h_source[4]) =
146 				cpu_to_be16(hwe->ipv4.l2.src_mac_lo);
147 		}
148 
149 		airoha_ppe_foe_entry_get_stats(ppe, i, &stats);
150 
151 		*((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi);
152 		*((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo);
153 		*((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi);
154 
155 		seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x"
156 			      " vlan=%d,%d ib1=%08x ib2=%08x"
157 			      " packets=%llu bytes=%llu\n",
158 			   h_source, h_dest, l2->etype, data,
159 			   l2->vlan1, l2->vlan2, hwe->ib1, ib2,
160 			   stats.packets, stats.bytes);
161 	}
162 
163 	return 0;
164 }
165 
166 static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private)
167 {
168 	return airoha_ppe_debugfs_foe_show(m, private, false);
169 }
170 DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all);
171 
172 static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private)
173 {
174 	return airoha_ppe_debugfs_foe_show(m, private, true);
175 }
176 DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind);
177 
178 int airoha_ppe_debugfs_init(struct airoha_ppe *ppe)
179 {
180 	ppe->debugfs_dir = debugfs_create_dir("ppe", NULL);
181 	debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe,
182 			    &airoha_ppe_debugfs_foe_all_fops);
183 	debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe,
184 			    &airoha_ppe_debugfs_foe_bind_fops);
185 
186 	return 0;
187 }
188