mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-20 06:20:41 +00:00
net: airoha: Introduce PPE debugfs support
Similar to PPE support for Mediatek devices, introduce PPE debugfs in order to dump binded and unbinded flows. Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
9cd451d414
commit
3fe15c640f
4 changed files with 209 additions and 4 deletions
|
@ -5,4 +5,5 @@
|
||||||
|
|
||||||
obj-$(CONFIG_NET_AIROHA) += airoha-eth.o
|
obj-$(CONFIG_NET_AIROHA) += airoha-eth.o
|
||||||
airoha-eth-y := airoha_eth.o airoha_ppe.o
|
airoha-eth-y := airoha_eth.o airoha_ppe.o
|
||||||
|
airoha-eth-$(CONFIG_DEBUG_FS) += airoha_ppe_debugfs.o
|
||||||
obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o
|
obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#ifndef AIROHA_ETH_H
|
#ifndef AIROHA_ETH_H
|
||||||
#define AIROHA_ETH_H
|
#define AIROHA_ETH_H
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -480,6 +481,8 @@ struct airoha_ppe {
|
||||||
|
|
||||||
struct hlist_head *foe_flow;
|
struct hlist_head *foe_flow;
|
||||||
u16 foe_check_time[PPE_NUM_ENTRIES];
|
u16 foe_check_time[PPE_NUM_ENTRIES];
|
||||||
|
|
||||||
|
struct dentry *debugfs_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct airoha_eth {
|
struct airoha_eth {
|
||||||
|
@ -533,5 +536,16 @@ int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
|
||||||
void *cb_priv);
|
void *cb_priv);
|
||||||
int airoha_ppe_init(struct airoha_eth *eth);
|
int airoha_ppe_init(struct airoha_eth *eth);
|
||||||
void airoha_ppe_deinit(struct airoha_eth *eth);
|
void airoha_ppe_deinit(struct airoha_eth *eth);
|
||||||
|
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
||||||
|
u32 hash);
|
||||||
|
|
||||||
|
#if CONFIG_DEBUG_FS
|
||||||
|
int airoha_ppe_debugfs_init(struct airoha_ppe *ppe);
|
||||||
|
#else
|
||||||
|
static inline int airoha_ppe_debugfs_init(struct airoha_ppe *ppe)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* AIROHA_ETH_H */
|
#endif /* AIROHA_ETH_H */
|
||||||
|
|
|
@ -390,8 +390,8 @@ static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct airoha_foe_entry *
|
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
||||||
airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash)
|
u32 hash)
|
||||||
{
|
{
|
||||||
if (hash < PPE_SRAM_NUM_ENTRIES) {
|
if (hash < PPE_SRAM_NUM_ENTRIES) {
|
||||||
u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
|
u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
|
||||||
|
@ -861,7 +861,7 @@ void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash)
|
||||||
int airoha_ppe_init(struct airoha_eth *eth)
|
int airoha_ppe_init(struct airoha_eth *eth)
|
||||||
{
|
{
|
||||||
struct airoha_ppe *ppe;
|
struct airoha_ppe *ppe;
|
||||||
int foe_size;
|
int foe_size, err;
|
||||||
|
|
||||||
ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
|
ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
|
||||||
if (!ppe)
|
if (!ppe)
|
||||||
|
@ -882,7 +882,15 @@ int airoha_ppe_init(struct airoha_eth *eth)
|
||||||
if (!ppe->foe_flow)
|
if (!ppe->foe_flow)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
return rhashtable_init(ð->flow_table, &airoha_flow_table_params);
|
err = rhashtable_init(ð->flow_table, &airoha_flow_table_params);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = airoha_ppe_debugfs_init(ppe);
|
||||||
|
if (err)
|
||||||
|
rhashtable_destroy(ð->flow_table);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void airoha_ppe_deinit(struct airoha_eth *eth)
|
void airoha_ppe_deinit(struct airoha_eth *eth)
|
||||||
|
@ -898,4 +906,5 @@ void airoha_ppe_deinit(struct airoha_eth *eth)
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
rhashtable_destroy(ð->flow_table);
|
rhashtable_destroy(ð->flow_table);
|
||||||
|
debugfs_remove(eth->ppe->debugfs_dir);
|
||||||
}
|
}
|
||||||
|
|
181
drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
Normal file
181
drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 AIROHA Inc
|
||||||
|
* Author: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "airoha_eth.h"
|
||||||
|
|
||||||
|
static void airoha_debugfs_ppe_print_tuple(struct seq_file *m,
|
||||||
|
void *src_addr, void *dest_addr,
|
||||||
|
u16 *src_port, u16 *dest_port,
|
||||||
|
bool ipv6)
|
||||||
|
{
|
||||||
|
__be32 n_addr[IPV6_ADDR_WORDS];
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
ipv6_addr_cpu_to_be32(n_addr, src_addr);
|
||||||
|
seq_printf(m, "%pI6", n_addr);
|
||||||
|
} else {
|
||||||
|
seq_printf(m, "%pI4h", src_addr);
|
||||||
|
}
|
||||||
|
if (src_port)
|
||||||
|
seq_printf(m, ":%d", *src_port);
|
||||||
|
|
||||||
|
seq_puts(m, "->");
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
ipv6_addr_cpu_to_be32(n_addr, dest_addr);
|
||||||
|
seq_printf(m, "%pI6", n_addr);
|
||||||
|
} else {
|
||||||
|
seq_printf(m, "%pI4h", dest_addr);
|
||||||
|
}
|
||||||
|
if (dest_port)
|
||||||
|
seq_printf(m, ":%d", *dest_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
|
||||||
|
bool bind)
|
||||||
|
{
|
||||||
|
static const char *const ppe_type_str[] = {
|
||||||
|
[PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
|
||||||
|
[PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
|
||||||
|
[PPE_PKT_TYPE_BRIDGE] = "L2B",
|
||||||
|
[PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
|
||||||
|
[PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
|
||||||
|
[PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
|
||||||
|
[PPE_PKT_TYPE_IPV6_6RD] = "6RD",
|
||||||
|
};
|
||||||
|
static const char *const ppe_state_str[] = {
|
||||||
|
[AIROHA_FOE_STATE_INVALID] = "INV",
|
||||||
|
[AIROHA_FOE_STATE_UNBIND] = "UNB",
|
||||||
|
[AIROHA_FOE_STATE_BIND] = "BND",
|
||||||
|
[AIROHA_FOE_STATE_FIN] = "FIN",
|
||||||
|
};
|
||||||
|
struct airoha_ppe *ppe = m->private;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < PPE_NUM_ENTRIES; i++) {
|
||||||
|
const char *state_str, *type_str = "UNKNOWN";
|
||||||
|
void *src_addr = NULL, *dest_addr = NULL;
|
||||||
|
u16 *src_port = NULL, *dest_port = NULL;
|
||||||
|
struct airoha_foe_mac_info_common *l2;
|
||||||
|
unsigned char h_source[ETH_ALEN] = {};
|
||||||
|
unsigned char h_dest[ETH_ALEN];
|
||||||
|
struct airoha_foe_entry *hwe;
|
||||||
|
u32 type, state, ib2, data;
|
||||||
|
bool ipv6 = false;
|
||||||
|
|
||||||
|
hwe = airoha_ppe_foe_get_entry(ppe, i);
|
||||||
|
if (!hwe)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
|
||||||
|
if (!state)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bind && state != AIROHA_FOE_STATE_BIND)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)];
|
||||||
|
type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
|
||||||
|
if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type])
|
||||||
|
type_str = ppe_type_str[type];
|
||||||
|
|
||||||
|
seq_printf(m, "%05x %s %7s", i, state_str, type_str);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case PPE_PKT_TYPE_IPV4_HNAPT:
|
||||||
|
case PPE_PKT_TYPE_IPV4_DSLITE:
|
||||||
|
src_port = &hwe->ipv4.orig_tuple.src_port;
|
||||||
|
dest_port = &hwe->ipv4.orig_tuple.dest_port;
|
||||||
|
fallthrough;
|
||||||
|
case PPE_PKT_TYPE_IPV4_ROUTE:
|
||||||
|
src_addr = &hwe->ipv4.orig_tuple.src_ip;
|
||||||
|
dest_addr = &hwe->ipv4.orig_tuple.dest_ip;
|
||||||
|
break;
|
||||||
|
case PPE_PKT_TYPE_IPV6_ROUTE_5T:
|
||||||
|
src_port = &hwe->ipv6.src_port;
|
||||||
|
dest_port = &hwe->ipv6.dest_port;
|
||||||
|
fallthrough;
|
||||||
|
case PPE_PKT_TYPE_IPV6_ROUTE_3T:
|
||||||
|
case PPE_PKT_TYPE_IPV6_6RD:
|
||||||
|
src_addr = &hwe->ipv6.src_ip;
|
||||||
|
dest_addr = &hwe->ipv6.dest_ip;
|
||||||
|
ipv6 = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_addr && dest_addr) {
|
||||||
|
seq_puts(m, " orig=");
|
||||||
|
airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
|
||||||
|
src_port, dest_port, ipv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case PPE_PKT_TYPE_IPV4_HNAPT:
|
||||||
|
case PPE_PKT_TYPE_IPV4_DSLITE:
|
||||||
|
src_port = &hwe->ipv4.new_tuple.src_port;
|
||||||
|
dest_port = &hwe->ipv4.new_tuple.dest_port;
|
||||||
|
fallthrough;
|
||||||
|
case PPE_PKT_TYPE_IPV4_ROUTE:
|
||||||
|
src_addr = &hwe->ipv4.new_tuple.src_ip;
|
||||||
|
dest_addr = &hwe->ipv4.new_tuple.dest_ip;
|
||||||
|
seq_puts(m, " new=");
|
||||||
|
airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
|
||||||
|
src_port, dest_port,
|
||||||
|
ipv6);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
|
||||||
|
data = hwe->ipv6.data;
|
||||||
|
ib2 = hwe->ipv6.ib2;
|
||||||
|
l2 = &hwe->ipv6.l2;
|
||||||
|
} else {
|
||||||
|
data = hwe->ipv4.data;
|
||||||
|
ib2 = hwe->ipv4.ib2;
|
||||||
|
l2 = &hwe->ipv4.l2.common;
|
||||||
|
*((__be16 *)&h_source[4]) =
|
||||||
|
cpu_to_be16(hwe->ipv4.l2.src_mac_lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
*((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi);
|
||||||
|
*((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo);
|
||||||
|
*((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi);
|
||||||
|
|
||||||
|
seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x"
|
||||||
|
" vlan=%d,%d ib1=%08x ib2=%08x\n",
|
||||||
|
h_source, h_dest, l2->etype, data,
|
||||||
|
l2->vlan1, l2->vlan2, hwe->ib1, ib2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private)
|
||||||
|
{
|
||||||
|
return airoha_ppe_debugfs_foe_show(m, private, false);
|
||||||
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all);
|
||||||
|
|
||||||
|
static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private)
|
||||||
|
{
|
||||||
|
return airoha_ppe_debugfs_foe_show(m, private, true);
|
||||||
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind);
|
||||||
|
|
||||||
|
int airoha_ppe_debugfs_init(struct airoha_ppe *ppe)
|
||||||
|
{
|
||||||
|
ppe->debugfs_dir = debugfs_create_dir("ppe", NULL);
|
||||||
|
debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe,
|
||||||
|
&airoha_ppe_debugfs_foe_all_fops);
|
||||||
|
debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe,
|
||||||
|
&airoha_ppe_debugfs_foe_bind_fops);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue