Re: [PATCH 1/2] perf jevents: Add NVIDIA Tegra410 uncore DDR metrics
From: Ian Rogers
Date: Tue Oct 13 2026 - 19:15:06 EST
On Tue, Oct 13, 2026 at 2:07 PM Besar Wicaksono <bwicaksono@xxxxxxxxxx> wrote:
>
> Hi Chun-Tse,
>
> I tested this patch on 2-socket Tegra410. Both DDR bandwidth and
> read latency metrics give correct values in the default, --per-socket,
> and -A modes.
>
> Some small comments inline below.
>
> > -----Original Message-----
> > From: Chun-Tse Shao <ctshao@xxxxxxxxxx>
> > Sent: Monday, September 28, 2026 5:51 PM
> > To: acme@xxxxxxxxxx; namhyung@xxxxxxxxxx; irogers@xxxxxxxxxx
> > Cc: peterz@xxxxxxxxxxxxx; mingo@xxxxxxxxxx; mark.rutland@xxxxxxx;
> > alexander.shishkin@xxxxxxxxxxxxxxx; jolsa@xxxxxxxxxx;
> > adrian.hunter@xxxxxxxxx; james.clark@xxxxxxxxxx; Besar Wicaksono
> > <bwicaksono@xxxxxxxxxx>; linux-perf-users@xxxxxxxxxxxxxxx; linux-
> > kernel@xxxxxxxxxxxxxxx; Chun-Tse Shao <ctshao@xxxxxxxxxx>
> > Subject: [PATCH 1/2] perf jevents: Add NVIDIA Tegra410 uncore DDR
> > metrics
> >
> > External email: Use caution opening links or attachments
> >
> >
> > Add DDR bandwidth and latency metrics for the NVIDIA Tegra410 SoC. The
> > formulas follow Documentation/admin-guide/perf/nvidia-tegra410-
> > pmu.rst:
> >
> > AVG_MEM_READ_BANDWIDTH_IN_GBPS = MEM_BYTES_RD /
> > ELAPSED_TIME_IN_NS
> > AVG_MEM_WRITE_BANDWIDTH_IN_GBPS = MEM_BYTES_WR /
> > ELAPSED_TIME_IN_NS
> >
> > FREQ_IN_GHZ = CYCLES / ELAPSED_TIME_IN_NS
> > AVG_LATENCY_IN_CYCLES = RD_CUM_OUTS / RD_REQ
> > AVERAGE_LATENCY_IN_NS = AVG_LATENCY_IN_CYCLES / FREQ_IN_GHZ
> >
> > The bandwidth metrics use the mem_bytes_rd/mem_bytes_wr events of
> > the
> > Unified Coherence Fabric (UCF) PMU with source/destination filters:
> >
> > lpm_ddr_bw, lpm_ddr_rd_bw, lpm_ddr_wr_bw: all sources to local CMEM.
> > lpm_ddr_loc_rd_bw, lpm_ddr_loc_wr_bw: local CPU and non-CPU sources
> > to
> > local CMEM.
> > lpm_ddr_rem_rd_bw, lpm_ddr_rem_wr_bw: local CPU and non-CPU
> > sources to
> > remote memory.
> >
> > The latency metrics use the CPU Memory (CMEM) latency PMU:
> >
> > lpm_ddr_lat, lpm_ddr_rd_lat: average DDR read latency in ns.
> > lpm_ddr_wr_lat: the PMU only measures reads, so this is always 0.
> >
>
> A question on naming: what does the "lpm_" prefix stand for? Is it the
> convention for all metrics generated by the Python scripts (I see it on
> the Intel/AMD and common metrics too)?
Hi Besar, naming is one of the hardest problems in Computer Science
:-) When we first added the metrics there was a request to
differentiate between metrics coming from the python scripts and
metrics coming from vendor code, like Intel top-down. We agreed on
having a prefix, and lpm_ was suggested as meaning Linux perf metric.
All of those words seem redundant given the metric is being measured
on Linux using perf, but it was adopted as the best suggestion. If you
have a better proposal, I think we can change it.
Thanks,
Ian
> > There is one CMEM latency PMU per socket and perf sums the counts of the
> > PMUs in an aggregation, e.g. of both sockets in the default aggregation
> > mode. Use aggr_nr() to divide the summed cycles by the number of PMUs,
> > otherwise the frequency is too high and the latency too low by that
> > factor.
> >
> > The events are sysfs events of the nvidia_ucf_pmu_<socket-id> and
> > nvidia_cmem_latency_pmu_<socket-id> PMUs rather than json events, so
> > add
> > the "nvidia" PMU prefix to the prefixes that skip the named json event
> > check in metric.py.
> >
> > pmu-events/Build only runs arm64_metrics.py for the "arm" vendor
> > models,
> > so also run it for the "nvidia" vendor models to generate the Tegra410
> > metrics.
> >
> > Signed-off-by: Chun-Tse Shao <ctshao@xxxxxxxxxx>
> > Assisted-by: Gemini:gemini-3.1-pro-preview
> > ---
> > tools/perf/pmu-events/Build | 2 +-
> > tools/perf/pmu-events/arm64_metrics.py | 66
> > +++++++++++++++++++++++++-
> > tools/perf/pmu-events/metric.py | 2 +-
> > 3 files changed, 66 insertions(+), 4 deletions(-)
> >
> > diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
> > index 1ac067be9d34..8dc8cfcb8b04 100644
> > --- a/tools/perf/pmu-events/Build
> > +++ b/tools/perf/pmu-events/Build
> > @@ -78,7 +78,7 @@ endif
> >
> > ifeq ($(JEVENTS_ARCH),$(filter $(JEVENTS_ARCH),arm64 all))
> > # Generate ARM Json
> > -ARMS := $(shell ls -d pmu-events/arch/arm64/arm/*|grep -v cmn)
> > +ARMS := $(shell ls -d pmu-events/arch/arm64/arm/* pmu-
> > events/arch/arm64/nvidia/*|grep -v cmn)
> > ARM_METRICS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metrics.json)
> > ARM_METRICGROUPS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-
> > metricgroups.json)
> > GEN_JSON += $(ARM_METRICS) $(ARM_METRICGROUPS)
> > diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-
> > events/arm64_metrics.py
> > index 4ecda96d11fa..b0d0651fec12 100755
> > --- a/tools/perf/pmu-events/arm64_metrics.py
> > +++ b/tools/perf/pmu-events/arm64_metrics.py
> > @@ -2,14 +2,75 @@
> > # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
> > import argparse
> > import os
> > -from metric import (JsonEncodeMetric,
> > JsonEncodeMetricGroupDescriptions, LoadEvents,
> > - MetricGroup)
> > +from typing import Optional
> > +from metric import (aggr_nr, d_ratio, Event, JsonEncodeMetric,
> > + JsonEncodeMetricGroupDescriptions, LoadEvents, Metric,
> > MetricGroup)
> > from common_metrics import Cycles
> >
> > # Global command line arguments.
> > _args = None
> >
> >
> > +def NvidiaT410Uncore() -> Optional[MetricGroup]:
> > + """Uncore metrics for the NVIDIA Tegra410 SoC.
> > +
> > + The formulas follow Documentation/admin-guide/perf/nvidia-tegra410-
> > pmu.rst.
> > + """
> > + assert _args is not None
> > + if _args.vendor != "nvidia" or _args.model != "t410":
> > + return None
> > +
> > + interval_sec = Event("duration_time")
> > + mb_per_sec = f"{1/1e6}MB/s"
> > +
> > + def DdrBw() -> MetricGroup:
> > + # Unified Coherence Fabric (UCF) PMU:
> > + # AVG_MEM_{READ,WRITE}_BANDWIDTH = MEM_BYTES_{RD,WR} /
> > ELAPSED_TIME
> > + loc_src = "src_loc_cpu=0x1,src_loc_noncpu=0x1"
> > + rd = Event("nvidia_ucf_pmu/mem_bytes_rd,dst_loc_cmem=0x1/")
> > + wr = Event("nvidia_ucf_pmu/mem_bytes_wr,dst_loc_cmem=0x1/")
> > + loc_rd =
> > Event(f"nvidia_ucf_pmu/mem_bytes_rd,{loc_src},dst_loc_cmem=0x1/")
> > + loc_wr =
> > Event(f"nvidia_ucf_pmu/mem_bytes_wr,{loc_src},dst_loc_cmem=0x1/")
> > + rem_rd =
> > Event(f"nvidia_ucf_pmu/mem_bytes_rd,{loc_src},dst_rem=0x1/")
> > + rem_wr =
> > Event(f"nvidia_ucf_pmu/mem_bytes_wr,{loc_src},dst_rem=0x1/")
> > + return MetricGroup("lpm_ddr_bw", [
> > + Metric("lpm_ddr_bw", "Total DDR bandwidth",
> > + d_ratio(rd + wr, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_rd_bw", "DDR read bandwidth",
> > + d_ratio(rd, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_wr_bw", "DDR write bandwidth",
> > + d_ratio(wr, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_loc_rd_bw", "Local DDR read bandwidth",
> > + d_ratio(loc_rd, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_loc_wr_bw", "Local DDR write bandwidth",
> > + d_ratio(loc_wr, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_rem_rd_bw", "Remote DDR read bandwidth",
> > + d_ratio(rem_rd, interval_sec), mb_per_sec),
> > + Metric("lpm_ddr_rem_wr_bw", "Remote DDR write bandwidth",
> > + d_ratio(rem_wr, interval_sec), mb_per_sec),
> > + ], description="NVIDIA Tegra410 DDR bandwidth")
> > +
> > + def DdrLat() -> MetricGroup:
> > + # CPU Memory (CMEM) latency PMU, one per socket:
> > + # AVERAGE_LATENCY_IN_NS = (RD_CUM_OUTS / RD_REQ) / (CYCLES /
> > ELAPSED_TIME_IN_NS)
> > + rd_cum_outs = Event("nvidia_cmem_latency_pmu/rd_cum_outs/")
> > + rd_req = Event("nvidia_cmem_latency_pmu/rd_req/")
> > + cycles = Event("nvidia_cmem_latency_pmu/cycles/")
> > + # perf sums the counts of the PMUs in an aggregation, e.g. both
> > sockets
> > + # by default, so divide cycles by their number to get the frequency.
> > + rd_lat = d_ratio(1e9 * interval_sec * rd_cum_outs * aggr_nr(cycles),
> > rd_req * cycles)
> > + return MetricGroup("lpm_ddr_lat", [
> > + Metric("lpm_ddr_lat", "Average DDR read latency in ns", rd_lat,
> > "1ns"),
> > + Metric("lpm_ddr_rd_lat", "DDR read latency in ns", rd_lat, "1ns"),
>
> Could we keep just lpm_ddr_rd_lat ?
>
> > + # The CMEM latency PMU only measures reads, write latency is
> > always 0.
> > + Metric("lpm_ddr_wr_lat", "DDR write latency in ns",
> > + rd_cum_outs - rd_cum_outs, "1ns"),
> > + ], description="NVIDIA Tegra410 DDR latency")
>
> This always prints 0.0 ns, which reads like a real measurement of
> zero-latency writes. Since the hardware can't measure write latency, I'd
> drop this metric rather than report a placeholder.
>
> Tested-by: Besar Wicaksono <bwicaksono@xxxxxxxxxx>
>
> Thanks,
> Besar
>
> > +
> > + return MetricGroup("lpm_uncore", [DdrBw(), DdrLat()],
> > + description="NVIDIA Tegra410 uncore metrics")
> > +
> > +
> > def main() -> None:
> > global _args
> >
> > @@ -37,6 +98,7 @@ def main() -> None:
> >
> > all_metrics = MetricGroup("", [
> > Cycles(),
> > + NvidiaT410Uncore(),
> > ])
> >
> > if _args.metricgroups:
> > diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-
> > events/metric.py
> > index f5d81eccd5c3..6fb69024d1fd 100644
> > --- a/tools/perf/pmu-events/metric.py
> > +++ b/tools/perf/pmu-events/metric.py
> > @@ -91,7 +91,7 @@ def CheckEveryEvent(*names: str) -> None:
> > name = name[:name.find(':')]
> > elif '/' in name:
> > name = name[:name.find('/')]
> > - if any([name.startswith(x) for x in ['amd', 'arm', 'cpu', 'msr', 'power',
> > 'cha', 'uncore']]):
> > + if any([name.startswith(x) for x in ['amd', 'arm', 'cpu', 'msr', 'power',
> > 'cha', 'uncore', 'nvidia']]):
> > continue
> > if name not in all_events_all_models:
> > raise ValueError(f"Is {name} a named json event?")
> > --
> > 2.56.0.rc1.315.gc6ed9934b7-goog
>