octeontx2-af: npc: cn20k: debugfs enhancements

Improve MCAM visibility and field debugging for CN20K NPC.

- Extend "mcam_layout" to show enabled (+) or disabled state per entry
  so status can be verified without parsing the full "mcam_entry" dump.
- Add "dstats" debugfs entry: for enabled MCAM indices, print hit deltas
  since the prior read by comparing hardware counters to a per-entry
  software baseline and advancing that baseline after each read (hardware
  counters are not cleared).
- Add "mismatch" debugfs entry: lists MCAM entries that are enabled
  but not explicitly allocated, helping diagnose allocation/field issues.

Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
Link: https://patch.msgid.link/20260609040453.711932-3-rkannoth@marvell.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Ratheesh Kannoth 2026-06-09 09:34:46 +05:30 committed by Jakub Kicinski
parent 6056a11b8d
commit 4655902dee
3 changed files with 197 additions and 15 deletions

View File

@ -13,6 +13,7 @@
#include "struct.h"
#include "rvu.h"
#include "debugfs.h"
#include "cn20k/reg.h"
#include "cn20k/npc.h"
static int npc_mcam_layout_show(struct seq_file *s, void *unused)
@ -58,7 +59,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
"v:%u", vidx0);
}
seq_printf(s, "\t%u(%#x) %s\n", idx0, pf1,
seq_printf(s, "\t%u(%#x)%c %s\n", idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
map ? buf0 : " ");
}
goto next;
@ -101,9 +103,13 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
vidx1);
}
seq_printf(s, "%05u(%#x) %s\t\t%05u(%#x) %s\n",
idx1, pf2, v1 ? buf1 : " ",
idx0, pf1, v0 ? buf0 : " ");
seq_printf(s, "%05u(%#x)%c %s\t\t%05u(%#x)%c %s\n",
idx1, pf2,
test_bit(idx1, npc_priv->en_map) ? '+' : ' ',
v1 ? buf1 : " ",
idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
v0 ? buf0 : " ");
continue;
}
@ -120,8 +126,9 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
vidx0);
}
seq_printf(s, "\t\t \t\t%05u(%#x) %s\n", idx0,
pf1, map ? buf0 : " ");
seq_printf(s, "\t\t \t\t%05u(%#x)%c %s\n", idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
map ? buf0 : " ");
continue;
}
@ -134,7 +141,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
snprintf(buf1, sizeof(buf1), "v:%05u", vidx1);
}
seq_printf(s, "%05u(%#x) %s\n", idx1, pf1,
seq_printf(s, "%05u(%#x)%c %s\n", idx1, pf1,
test_bit(idx1, npc_priv->en_map) ? '+' : ' ',
map ? buf1 : " ");
}
next:
@ -145,6 +153,136 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
DEFINE_SHOW_ATTRIBUTE(npc_mcam_layout);
#define __OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name) \
static const struct file_operations __name ## _fops = { \
.owner = THIS_MODULE, \
.open = __name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(__name, __size) \
static int __name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open_size(file, __name ## _show, inode->i_private, \
__size); \
} \
__OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name)
static DEFINE_MUTEX(stats_lock);
/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent
* hard limit on all silicon variants, preventing any possibility of
* out-of-bounds access.
*/
static u64 dstats[MAX_NUM_BANKS][MAX_SUBBANK_DEPTH * MAX_NUM_SUB_BANKS] = {};
static int npc_mcam_dstats_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
int blkaddr, pf, mcam_idx;
u64 stats, delta;
struct rvu *rvu;
char buff[64];
u8 key_type;
void *map;
npc_priv = npc_priv_get();
rvu = s->private;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
if (blkaddr < 0)
return 0;
mutex_lock(&stats_lock);
seq_puts(s, "idx\tpfunc\tstats\n");
for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) {
for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) {
mcam_idx = bank * npc_priv->bank_depth + idx;
if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &key_type))
continue;
if (key_type == NPC_MCAM_KEY_X4 && bank != 0)
continue;
if (!test_bit(mcam_idx, npc_priv->en_map))
continue;
stats = rvu_read64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(idx, bank));
if (!stats)
continue;
if (stats == dstats[bank][idx])
continue;
if (stats < dstats[bank][idx])
dstats[bank][idx] = 0;
pf = 0xFFFF;
map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx);
if (map)
pf = xa_to_value(map);
delta = stats - dstats[bank][idx];
snprintf(buff, sizeof(buff), "%u\t%#04x\t%llu\n",
mcam_idx, pf, delta);
seq_puts(s, buff);
dstats[bank][idx] = stats;
}
}
mutex_unlock(&stats_lock);
return 0;
}
/* "%u\t%#04x\t%llu\n" needs less than 64 characters to print */
#define TOTAL_SZ (MAX_NUM_BANKS * MAX_NUM_SUB_BANKS * MAX_SUBBANK_DEPTH * 64)
DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_dstats, TOTAL_SZ);
static int npc_mcam_mismatch_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
struct npc_subbank *sb;
int mcam_idx, sb_off;
struct rvu *rvu;
char buff[64];
void *map;
int rc;
npc_priv = npc_priv_get();
rvu = s->private;
seq_puts(s, "index\tsb idx\tkw type\n");
for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) {
for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) {
mcam_idx = bank * npc_priv->bank_depth + idx;
if (!test_bit(mcam_idx, npc_priv->en_map))
continue;
map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx);
if (map)
continue;
rc = npc_mcam_idx_2_subbank_idx(rvu, mcam_idx,
&sb, &sb_off);
if (rc)
continue;
snprintf(buff, sizeof(buff), "%u\t%d\t%u\n",
mcam_idx, sb->idx, sb->key_type);
seq_puts(s, buff);
}
}
return 0;
}
/* "%u\t%d\t%u\n" needs less than 64 characters to print. */
DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_mismatch, TOTAL_SZ);
static int npc_mcam_default_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
@ -259,6 +397,12 @@ int npc_cn20k_debugfs_init(struct rvu *rvu)
debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
npc_priv, &npc_vidx2idx_map_fops);
debugfs_create_file("dstats", 0444, rvu->rvu_dbg.npc, rvu,
&npc_mcam_dstats_fops);
debugfs_create_file("mismatch", 0444, rvu->rvu_dbg.npc, rvu,
&npc_mcam_mismatch_fops);
debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
npc_priv, &npc_idx2vidx_map_fops);

View File

@ -824,7 +824,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
cfg);
return 0;
goto update_en_map;
}
/* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks
@ -842,6 +842,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
cfg);
}
update_en_map:
if (enable)
set_bit(index, npc_priv.en_map);
else
clear_bit(index, npc_priv.en_map);
return 0;
}
@ -1785,9 +1791,9 @@ static int npc_subbank_idx_2_mcam_idx(struct rvu *rvu, struct npc_subbank *sb,
return 0;
}
static int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx,
struct npc_subbank **sb,
int *sb_off)
int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx,
struct npc_subbank **sb,
int *sb_off)
{
int bank_off, sb_id;
@ -4494,11 +4500,17 @@ static int npc_priv_init(struct rvu *rvu)
npc_const2 = rvu_read64(rvu, blkaddr, NPC_AF_CONST2);
num_banks = mcam->banks;
bank_depth = mcam->banksize;
if (!num_banks || num_banks > MAX_NUM_BANKS) {
dev_err(rvu->dev,
"Number of banks(%u) is invalid\n", num_banks);
return -EINVAL;
}
num_subbanks = FIELD_GET(GENMASK_ULL(39, 32), npc_const2);
if (!num_subbanks) {
dev_err(rvu->dev, "Number of subbanks is zero\n");
if (!num_subbanks || num_subbanks > MAX_NUM_SUB_BANKS) {
dev_err(rvu->dev,
"Number of subbanks is invalid %u\n",
num_subbanks);
return -EFAULT;
}
@ -4509,10 +4521,23 @@ static int npc_priv_init(struct rvu *rvu)
return -EINVAL;
}
npc_priv.num_subbanks = num_subbanks;
bank_depth = mcam->banksize;
if (!bank_depth || bank_depth % num_subbanks) {
dev_err(rvu->dev,
"Bank depth(%u) should be a multiple of num_subbanks(%u)\n",
bank_depth, num_subbanks);
return -EINVAL;
}
subbank_depth = bank_depth / num_subbanks;
if (!subbank_depth || subbank_depth > MAX_SUBBANK_DEPTH) {
dev_err(rvu->dev,
"Invalid subbank depth %u\n",
subbank_depth);
return -EINVAL;
}
npc_priv.num_subbanks = num_subbanks;
npc_priv.bank_depth = bank_depth;
npc_priv.subbank_depth = subbank_depth;
@ -4601,6 +4626,8 @@ void npc_cn20k_deinit(struct rvu *rvu)
*/
kfree(npc_priv.sb);
kfree(subbank_srch_order);
bitmap_clear(npc_priv.en_map, 0, MAX_NUM_BANKS * MAX_NUM_SUB_BANKS *
MAX_SUBBANK_DEPTH);
}
static int npc_setup_mcam_section(struct rvu *rvu, int key_type)

View File

@ -10,6 +10,10 @@
#define MKEX_CN20K_SIGN 0x19bbfdbd160
/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent
* hard limit on all silicon variants, preventing any possibility of
* out-of-bounds access on matrix defined using these values.
*/
#define MAX_NUM_BANKS 2
#define MAX_NUM_SUB_BANKS 32
#define MAX_SUBBANK_DEPTH 256
@ -170,6 +174,7 @@ struct npc_defrag_show_node {
* @num_banks: Number of banks.
* @num_subbanks: Number of subbanks.
* @subbank_depth: Depth of subbank.
* @en_map: Enable/disable status.
* @kw: Kex configured key type.
* @sb: Subbank array.
* @xa_sb_used: Array of used subbanks.
@ -193,6 +198,9 @@ struct npc_priv_t {
const int num_banks;
int num_subbanks;
int subbank_depth;
DECLARE_BITMAP(en_map, MAX_NUM_BANKS *
MAX_NUM_SUB_BANKS *
MAX_SUBBANK_DEPTH);
u8 kw;
struct npc_subbank *sb;
struct xarray xa_sb_used;
@ -336,5 +344,8 @@ u16 npc_cn20k_vidx2idx(u16 index);
u16 npc_cn20k_idx2vidx(u16 idx);
int npc_cn20k_defrag(struct rvu *rvu);
bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc);
int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx,
struct npc_subbank **sb,
int *sb_off);
#endif /* NPC_CN20K_H */