Skip to content

32-bit GDM counters wrap silently on MT7988 - #196

Open
meehien wants to merge 1 commit into
frank-w:7.1-mainfrom
meehien:bpi/mtk-eth-gdm-mib-drain
Open

32-bit GDM counters wrap silently on MT7988#196
meehien wants to merge 1 commit into
frank-w:7.1-mainfrom
meehien:bpi/mtk-eth-gdm-mib-drain

Conversation

@meehien

@meehien meehien commented Jul 30, 2026

Copy link
Copy Markdown

The GDMA MIB counters are clear-on-read, so a count never read is a count
lost. The hardware raises an almost-full condition to ask for a drain, and
mtk_handle_status_irq() tests for it — but MTK_GDM1_AF/MTK_GDM2_AF are
BIT(28)/BIT(29), unconditional definitions describing a two-GDM frame
engine from the MT7621/MT7623 layout. MT7988 has three GDMs and there is no
MTK_GDM3_AF.

Measured on MT7988 with sticky per-bit accumulators on both frontend status
registers, across idle and saturated 1G and 10G: bits 28 and 29 of
MTK_FE_INT_STATUS are never set. Bits 23–25 are permanently asserted.
mtk_stats_update() is never reached from the IRQ path on this SoC.

That leaves mtk_get_stats64() and the ethtool op, which drain only when
userspace asks. With nothing asking, the 32-bit packet, error and drop
counters roll over silently — 2³² packets is under five minutes of 10G
minimum-size traffic. Byte counters are a 64-bit pair and unaffected.

The correct v3 bit assignment isn't in any public source and three
permanently-asserted bits are not evidence of almost-full, so I'm not
guessing at a new mask. This adds a delayed work that drains once a second
regardless, shaped like reset.monitor_work — three orders of magnitude
inside the wrap interval, ~40 register reads per second. The AF test stays;
it's free and will start working if the right bits are ever established.

Note for anyone testing: nothing may read /proc/mtketh/dbg_regs during a
comparison run — the two paths steal clear-on-read counts from each other.

The GDMA MIB counters are clear-on-read: mtk_stats_update_mac() adds
each register into a software accumulator, so a count that is never
read is a count that is lost.  The hardware raises an "almost full"
condition to ask for a drain before that happens, and the driver tests
for it in mtk_handle_status_irq():

	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF)))
		mtk_stats_update(eth);

MTK_GDM1_AF and MTK_GDM2_AF are BIT(28) and BIT(29).  Those definitions
are unconditional and describe a two-GDM frame engine; they date from
the MT7621/MT7623 layout.  MT7988 has three GDMs, and there is no
MTK_GDM3_AF, so at best the third GDM has never had a trigger at all.

Measured on MT7988 (BPI-R4, NETSYS v3) with sticky per-bit accumulators
on both frontend status registers, across idle and saturated 1G and 10G
traffic: bits 28 and 29 of MTK_FE_INT_STATUS are never observed set.
Bits 23, 24 and 25 are permanently asserted.  mtk_stats_update() is
therefore never reached through mtk_handle_status_irq() on this SoC.

What remains is mtk_get_stats64() and the ethtool stats op, both of
which drain the counters but only when userspace asks.  With nothing
asking, the 32-bit packet, error and drop counters roll over silently.
2**32 packets is under five minutes of 10G minimum-size traffic and
under an hour at 1G.  The byte counters are a 64-bit pair and are not
affected.

The correct v3 bit assignment is not documented in any public source,
and three permanently asserted bits are not evidence of an almost-full
condition, so do not guess at a new mask.  Add a delayed work that
drains the counters once a second regardless, in the same shape as
reset.monitor_work.  That is three orders of magnitude inside the wrap
interval and costs about forty register reads per second.  The AF test
is left in place; it is free, and it will start working if and when the
correct bits are established.

Signed-off-by: Mihai Ordean <research@mihaiordean.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant