RE: [PATCH 1/2] perf jevents: Add NVIDIA Tegra410 uncore DDR metrics

From: Besar Wicaksono

Date: Tue Oct 13 2026 - 17:28:30 EST


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)?

> 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