diff options
Diffstat (limited to 'drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c')
| -rw-r--r-- | drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | 217 | 
1 files changed, 217 insertions, 0 deletions
| diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c new file mode 100644 index 000000000000..98b1d3577bcd --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */ + +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include "mtk_eth_soc.h" + +struct mtk_flow_addr_info +{ +	void *src, *dest; +	u16 *src_port, *dest_port; +	bool ipv6; +}; + +static const char *mtk_foe_entry_state_str(int state) +{ +	static const char * const state_str[] = { +		[MTK_FOE_STATE_INVALID] = "INV", +		[MTK_FOE_STATE_UNBIND] = "UNB", +		[MTK_FOE_STATE_BIND] = "BND", +		[MTK_FOE_STATE_FIN] = "FIN", +	}; + +	if (state >= ARRAY_SIZE(state_str) || !state_str[state]) +		return "UNK"; + +	return state_str[state]; +} + +static const char *mtk_foe_pkt_type_str(int type) +{ +	static const char * const type_str[] = { +		[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T", +		[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T", +		[MTK_PPE_PKT_TYPE_BRIDGE] = "L2", +		[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE", +		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T", +		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T", +		[MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD", +	}; + +	if (type >= ARRAY_SIZE(type_str) || !type_str[type]) +		return "UNKNOWN"; + +	return type_str[type]; +} + +static void +mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6) +{ +	u32 n_addr[4]; +	int i; + +	if (!ipv6) { +		seq_printf(m, "%pI4h", addr); +		return; +	} + +	for (i = 0; i < ARRAY_SIZE(n_addr); i++) +		n_addr[i] = htonl(addr[i]); +	seq_printf(m, "%pI6", n_addr); +} + +static void +mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai) +{ +	mtk_print_addr(m, ai->src, ai->ipv6); +	if (ai->src_port) +		seq_printf(m, ":%d", *ai->src_port); +	seq_printf(m, "->"); +	mtk_print_addr(m, ai->dest, ai->ipv6); +	if (ai->dest_port) +		seq_printf(m, ":%d", *ai->dest_port); +} + +static int +mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) +{ +	struct mtk_ppe *ppe = m->private; +	int i; + +	for (i = 0; i < MTK_PPE_ENTRIES; i++) { +		struct mtk_foe_entry *entry = &ppe->foe_table[i]; +		struct mtk_foe_mac_info *l2; +		struct mtk_flow_addr_info ai = {}; +		unsigned char h_source[ETH_ALEN]; +		unsigned char h_dest[ETH_ALEN]; +		int type, state; +		u32 ib2; + + +		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1); +		if (!state) +			continue; + +		if (bind && state != MTK_FOE_STATE_BIND) +			continue; + +		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); +		seq_printf(m, "%05x %s %7s", i, +			   mtk_foe_entry_state_str(state), +			   mtk_foe_pkt_type_str(type)); + +		switch (type) { +		case MTK_PPE_PKT_TYPE_IPV4_HNAPT: +		case MTK_PPE_PKT_TYPE_IPV4_DSLITE: +			ai.src_port = &entry->ipv4.orig.src_port; +			ai.dest_port = &entry->ipv4.orig.dest_port; +			fallthrough; +		case MTK_PPE_PKT_TYPE_IPV4_ROUTE: +			ai.src = &entry->ipv4.orig.src_ip; +			ai.dest = &entry->ipv4.orig.dest_ip; +			break; +		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T: +			ai.src_port = &entry->ipv6.src_port; +			ai.dest_port = &entry->ipv6.dest_port; +			fallthrough; +		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T: +		case MTK_PPE_PKT_TYPE_IPV6_6RD: +			ai.src = &entry->ipv6.src_ip; +			ai.dest = &entry->ipv6.dest_ip; +			ai.ipv6 = true; +			break; +		} + +		seq_printf(m, " orig="); +		mtk_print_addr_info(m, &ai); + +		switch (type) { +		case MTK_PPE_PKT_TYPE_IPV4_HNAPT: +		case MTK_PPE_PKT_TYPE_IPV4_DSLITE: +			ai.src_port = &entry->ipv4.new.src_port; +			ai.dest_port = &entry->ipv4.new.dest_port; +			fallthrough; +		case MTK_PPE_PKT_TYPE_IPV4_ROUTE: +			ai.src = &entry->ipv4.new.src_ip; +			ai.dest = &entry->ipv4.new.dest_ip; +			seq_printf(m, " new="); +			mtk_print_addr_info(m, &ai); +			break; +		} + +		if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { +			l2 = &entry->ipv6.l2; +			ib2 = entry->ipv6.ib2; +		} else { +			l2 = &entry->ipv4.l2; +			ib2 = entry->ipv4.ib2; +		} + +		*((__be32 *)h_source) = htonl(l2->src_mac_hi); +		*((__be16 *)&h_source[4]) = htons(l2->src_mac_lo); +		*((__be32 *)h_dest) = htonl(l2->dest_mac_hi); +		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo); + +		seq_printf(m, " eth=%pM->%pM etype=%04x" +			      " vlan=%d,%d ib1=%08x ib2=%08x\n", +			   h_source, h_dest, ntohs(l2->etype), +			   l2->vlan1, l2->vlan2, entry->ib1, ib2); +	} + +	return 0; +} + +static int +mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private) +{ +	return mtk_ppe_debugfs_foe_show(m, private, false); +} + +static int +mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private) +{ +	return mtk_ppe_debugfs_foe_show(m, private, true); +} + +static int +mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file) +{ +	return single_open(file, mtk_ppe_debugfs_foe_show_all, +			   inode->i_private); +} + +static int +mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file) +{ +	return single_open(file, mtk_ppe_debugfs_foe_show_bind, +			   inode->i_private); +} + +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) +{ +	static const struct file_operations fops_all = { +		.open = mtk_ppe_debugfs_foe_open_all, +		.read = seq_read, +		.llseek = seq_lseek, +		.release = single_release, +	}; + +	static const struct file_operations fops_bind = { +		.open = mtk_ppe_debugfs_foe_open_bind, +		.read = seq_read, +		.llseek = seq_lseek, +		.release = single_release, +	}; + +	struct dentry *root; + +	root = debugfs_create_dir("mtk_ppe", NULL); +	if (!root) +		return -ENOMEM; + +	debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all); +	debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind); + +	return 0; +} | 
