mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
perf drm_pmu: Add a tool like PMU to expose DRM information
DRM clients expose information through usage stats as documented in
Documentation/gpu/drm-usage-stats.rst (available online at
https://docs.kernel.org/gpu/drm-usage-stats.html). Add a tool like
PMU, similar to the hwmon PMU, that exposes DRM information. For
example on a tigerlake laptop:
```
$ perf list drm
List of pre-defined events (to be used in -e or -M):
drm:
drm-active-stolen-system0
[Total memory active in one or more engines. Unit: drm_i915]
drm-active-system0
[Total memory active in one or more engines. Unit: drm_i915]
drm-engine-capacity-video
[Engine capacity. Unit: drm_i915]
drm-engine-copy
[Utilization in ns. Unit: drm_i915]
drm-engine-render
[Utilization in ns. Unit: drm_i915]
drm-engine-video
[Utilization in ns. Unit: drm_i915]
drm-engine-video-enhance
[Utilization in ns. Unit: drm_i915]
drm-purgeable-stolen-system0
[Size of resident and purgeable memory bufers. Unit: drm_i915]
drm-purgeable-system0
[Size of resident and purgeable memory bufers. Unit: drm_i915]
drm-resident-stolen-system0
[Size of resident memory bufers. Unit: drm_i915]
drm-resident-system0
[Size of resident memory bufers. Unit: drm_i915]
drm-shared-stolen-system0
[Size of shared memory bufers. Unit: drm_i915]
drm-shared-system0
[Size of shared memory bufers. Unit: drm_i915]
drm-total-stolen-system0
[Size of shared and private memory. Unit: drm_i915]
drm-total-system0
[Size of shared and private memory. Unit: drm_i915]
```
System wide data can be gathered:
```
$ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm-engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,drm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,drm-shared-system0,drm-total-stolen-system0,drm-total-system0
1.000904910,0,bytes,drm-active-stolen-system0,1,100.00,,
1.000904910,0,bytes,drm-active-system0,1,100.00,,
1.000904910,36,capacity,drm-engine-capacity-video,1,100.00,,
1.000904910,0,ns,drm-engine-copy,1,100.00,,
1.000904910,1472970566175,ns,drm-engine-render,1,100.00,,
1.000904910,0,ns,drm-engine-video,1,100.00,,
1.000904910,0,ns,drm-engine-video-enhance,1,100.00,,
1.000904910,0,bytes,drm-purgeable-stolen-system0,1,100.00,,
1.000904910,38199296,bytes,drm-purgeable-system0,1,100.00,,
1.000904910,0,bytes,drm-resident-stolen-system0,1,100.00,,
1.000904910,4643196928,bytes,drm-resident-system0,1,100.00,,
1.000904910,0,bytes,drm-shared-stolen-system0,1,100.00,,
1.000904910,1886871552,bytes,drm-shared-system0,1,100.00,,
1.000904910,0,bytes,drm-total-stolen-system0,1,100.00,,
1.000904910,4643196928,bytes,drm-total-system0,1,100.00,,
2.264426839,0,bytes,drm-active-stolen-system0,1,100.00,,
```
Or for a particular process:
```
$ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm-engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,drm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,drm-shared-system0,drm-total-stolen-system0,drm-total-system0 -p 200027
1.001040274,0,bytes,drm-active-stolen-system0,6,100.00,,
1.001040274,0,bytes,drm-active-system0,6,100.00,,
1.001040274,12,capacity,drm-engine-capacity-video,6,100.00,,
1.001040274,0,ns,drm-engine-copy,6,100.00,,
1.001040274,1542300,ns,drm-engine-render,6,100.00,,
1.001040274,0,ns,drm-engine-video,6,100.00,,
1.001040274,0,ns,drm-engine-video-enhance,6,100.00,,
1.001040274,0,bytes,drm-purgeable-stolen-system0,6,100.00,,
1.001040274,13516800,bytes,drm-purgeable-system0,6,100.00,,
1.001040274,0,bytes,drm-resident-stolen-system0,6,100.00,,
1.001040274,27746304,bytes,drm-resident-system0,6,100.00,,
1.001040274,0,bytes,drm-shared-stolen-system0,6,100.00,,
1.001040274,0,bytes,drm-shared-system0,6,100.00,,
1.001040274,0,bytes,drm-total-stolen-system0,6,100.00,,
1.001040274,27746304,bytes,drm-total-system0,6,100.00,,
2.016629075,0,bytes,drm-active-stolen-system0,6,100.00,,
```
As with the hwmon PMU, high numbered PMU types are used to encode
multiple possible "DRM" PMUs. The appropriate fdinfo is found by
scanning /proc and filtering which fdinfos to read with stat. To avoid
some unneeding scanning, events not starting with "drm-" are
ignored. The patch builds on commit 57e13264dc ("perf pmus:
Restructure pmu_read_sysfs to scan fewer PMUs") and later so that only
if full wild carding is being done, the PMU starts with "drm_" or the
event starts with "drm-" will /proc be scanned. That is there should
be little to no cost in this PMU unless DRM events are requested.
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250624231837.179536-3-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
@@ -84,6 +84,7 @@ perf-util-y += pmu.o
|
||||
perf-util-y += pmus.o
|
||||
perf-util-y += pmu-flex.o
|
||||
perf-util-y += pmu-bison.o
|
||||
perf-util-y += drm_pmu.o
|
||||
perf-util-y += hwmon_pmu.o
|
||||
perf-util-y += tool_pmu.o
|
||||
perf-util-y += svghelper.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __DRM_PMU_H
|
||||
#define __DRM_PMU_H
|
||||
/*
|
||||
* Linux DRM clients expose information through usage stats as documented in
|
||||
* Documentation/gpu/drm-usage-stats.rst (available online at
|
||||
* https://docs.kernel.org/gpu/drm-usage-stats.html). This is a tool like PMU
|
||||
* that exposes DRM information.
|
||||
*/
|
||||
|
||||
#include "pmu.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
struct list_head;
|
||||
struct perf_thread_map;
|
||||
|
||||
void drm_pmu__exit(struct perf_pmu *pmu);
|
||||
bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name);
|
||||
int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb);
|
||||
size_t drm_pmu__num_events(const struct perf_pmu *pmu);
|
||||
int drm_pmu__config_terms(const struct perf_pmu *pmu,
|
||||
struct perf_event_attr *attr,
|
||||
struct parse_events_terms *terms,
|
||||
struct parse_events_error *err);
|
||||
int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_terms *terms,
|
||||
struct perf_pmu_info *info, struct parse_events_error *err);
|
||||
|
||||
|
||||
bool perf_pmu__is_drm(const struct perf_pmu *pmu);
|
||||
bool evsel__is_drm(const struct evsel *evsel);
|
||||
|
||||
int perf_pmus__read_drm_pmus(struct list_head *pmus);
|
||||
|
||||
int evsel__drm_pmu_open(struct evsel *evsel,
|
||||
struct perf_thread_map *threads,
|
||||
int start_cpu_map_idx, int end_cpu_map_idx);
|
||||
int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread);
|
||||
|
||||
#endif /* __DRM_PMU_H */
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "off_cpu.h"
|
||||
#include "pmu.h"
|
||||
#include "pmus.h"
|
||||
#include "drm_pmu.h"
|
||||
#include "hwmon_pmu.h"
|
||||
#include "tool_pmu.h"
|
||||
#include "rlimit.h"
|
||||
@@ -1889,6 +1890,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
|
||||
if (evsel__is_hwmon(evsel))
|
||||
return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread);
|
||||
|
||||
if (evsel__is_drm(evsel))
|
||||
return evsel__drm_pmu_read(evsel, cpu_map_idx, thread);
|
||||
|
||||
if (evsel__is_retire_lat(evsel))
|
||||
return evsel__tpebs_read(evsel, cpu_map_idx, thread);
|
||||
|
||||
@@ -2610,6 +2614,11 @@ fallback_missing_features:
|
||||
start_cpu_map_idx,
|
||||
end_cpu_map_idx);
|
||||
}
|
||||
if (evsel__is_drm(evsel)) {
|
||||
return evsel__drm_pmu_open(evsel, threads,
|
||||
start_cpu_map_idx,
|
||||
end_cpu_map_idx);
|
||||
}
|
||||
|
||||
for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
|
||||
cpu = perf_cpu_map__cpu(cpus, idx);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "debug.h"
|
||||
#include "evsel.h"
|
||||
#include "pmu.h"
|
||||
#include "drm_pmu.h"
|
||||
#include "hwmon_pmu.h"
|
||||
#include "pmus.h"
|
||||
#include "tool_pmu.h"
|
||||
@@ -1627,6 +1628,8 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu,
|
||||
|
||||
if (perf_pmu__is_hwmon(pmu))
|
||||
return hwmon_pmu__config_terms(pmu, attr, terms, err);
|
||||
if (perf_pmu__is_drm(pmu))
|
||||
return drm_pmu__config_terms(pmu, attr, terms, err);
|
||||
|
||||
list_for_each_entry(term, &terms->terms, list) {
|
||||
if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err))
|
||||
@@ -1767,6 +1770,10 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
|
||||
ret = hwmon_pmu__check_alias(head_terms, info, err);
|
||||
goto out;
|
||||
}
|
||||
if (perf_pmu__is_drm(pmu)) {
|
||||
ret = drm_pmu__check_alias(pmu, head_terms, info, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Fake PMU doesn't rewrite terms. */
|
||||
if (perf_pmu__is_fake(pmu))
|
||||
@@ -1949,6 +1956,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
|
||||
return false;
|
||||
if (perf_pmu__is_hwmon(pmu))
|
||||
return hwmon_pmu__have_event(pmu, name);
|
||||
if (perf_pmu__is_drm(pmu))
|
||||
return drm_pmu__have_event(pmu, name);
|
||||
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
|
||||
return true;
|
||||
if (pmu->cpu_aliases_added || !pmu->events_table)
|
||||
@@ -1962,6 +1971,8 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
|
||||
|
||||
if (perf_pmu__is_hwmon(pmu))
|
||||
return hwmon_pmu__num_events(pmu);
|
||||
if (perf_pmu__is_drm(pmu))
|
||||
return drm_pmu__num_events(pmu);
|
||||
|
||||
pmu_aliases_parse(pmu);
|
||||
nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
|
||||
@@ -2030,6 +2041,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
|
||||
|
||||
if (perf_pmu__is_hwmon(pmu))
|
||||
return hwmon_pmu__for_each_event(pmu, state, cb);
|
||||
if (perf_pmu__is_drm(pmu))
|
||||
return drm_pmu__for_each_event(pmu, state, cb);
|
||||
|
||||
strbuf_init(&sb, /*hint=*/ 0);
|
||||
pmu_aliases_parse(pmu);
|
||||
@@ -2511,6 +2524,8 @@ void perf_pmu__delete(struct perf_pmu *pmu)
|
||||
|
||||
if (perf_pmu__is_hwmon(pmu))
|
||||
hwmon_pmu__exit(pmu);
|
||||
else if (perf_pmu__is_drm(pmu))
|
||||
drm_pmu__exit(pmu);
|
||||
|
||||
perf_pmu__del_formats(&pmu->format);
|
||||
perf_pmu__del_aliases(pmu);
|
||||
|
||||
@@ -39,7 +39,9 @@ struct perf_pmu_caps {
|
||||
|
||||
enum {
|
||||
PERF_PMU_TYPE_PE_START = 0,
|
||||
PERF_PMU_TYPE_PE_END = 0xFFFEFFFF,
|
||||
PERF_PMU_TYPE_PE_END = 0xFFFDFFFF,
|
||||
PERF_PMU_TYPE_DRM_START = 0xFFFE0000,
|
||||
PERF_PMU_TYPE_DRM_END = 0xFFFEFFFF,
|
||||
PERF_PMU_TYPE_HWMON_START = 0xFFFF0000,
|
||||
PERF_PMU_TYPE_HWMON_END = 0xFFFFFFFD,
|
||||
PERF_PMU_TYPE_TOOL = 0xFFFFFFFE,
|
||||
|
||||
+26
-4
@@ -12,6 +12,7 @@
|
||||
#include <unistd.h>
|
||||
#include "cpumap.h"
|
||||
#include "debug.h"
|
||||
#include "drm_pmu.h"
|
||||
#include "evsel.h"
|
||||
#include "pmus.h"
|
||||
#include "pmu.h"
|
||||
@@ -43,16 +44,19 @@ enum perf_tool_pmu_type {
|
||||
PERF_TOOL_PMU_TYPE_PE_OTHER,
|
||||
PERF_TOOL_PMU_TYPE_TOOL,
|
||||
PERF_TOOL_PMU_TYPE_HWMON,
|
||||
PERF_TOOL_PMU_TYPE_DRM,
|
||||
|
||||
#define PERF_TOOL_PMU_TYPE_PE_CORE_MASK (1 << PERF_TOOL_PMU_TYPE_PE_CORE)
|
||||
#define PERF_TOOL_PMU_TYPE_PE_OTHER_MASK (1 << PERF_TOOL_PMU_TYPE_PE_OTHER)
|
||||
#define PERF_TOOL_PMU_TYPE_TOOL_MASK (1 << PERF_TOOL_PMU_TYPE_TOOL)
|
||||
#define PERF_TOOL_PMU_TYPE_HWMON_MASK (1 << PERF_TOOL_PMU_TYPE_HWMON)
|
||||
#define PERF_TOOL_PMU_TYPE_DRM_MASK (1 << PERF_TOOL_PMU_TYPE_DRM)
|
||||
|
||||
#define PERF_TOOL_PMU_TYPE_ALL_MASK (PERF_TOOL_PMU_TYPE_PE_CORE_MASK | \
|
||||
PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | \
|
||||
PERF_TOOL_PMU_TYPE_TOOL_MASK | \
|
||||
PERF_TOOL_PMU_TYPE_HWMON_MASK)
|
||||
PERF_TOOL_PMU_TYPE_HWMON_MASK | \
|
||||
PERF_TOOL_PMU_TYPE_DRM_MASK)
|
||||
};
|
||||
static unsigned int read_pmu_types;
|
||||
|
||||
@@ -173,6 +177,8 @@ struct perf_pmu *perf_pmus__find(const char *name)
|
||||
/* Looking up an individual perf event PMU failed, check if a tool PMU should be read. */
|
||||
if (!strncmp(name, "hwmon_", 6))
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK;
|
||||
else if (!strncmp(name, "drm_", 4))
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK;
|
||||
else if (!strcmp(name, "tool"))
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_TOOL_MASK;
|
||||
|
||||
@@ -273,6 +279,10 @@ skip_pe_pmus:
|
||||
(read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) == 0)
|
||||
perf_pmus__read_hwmon_pmus(&other_pmus);
|
||||
|
||||
if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) != 0 &&
|
||||
(read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) == 0)
|
||||
perf_pmus__read_drm_pmus(&other_pmus);
|
||||
|
||||
list_sort(NULL, &other_pmus, pmus_cmp);
|
||||
|
||||
read_pmu_types |= to_read_types;
|
||||
@@ -305,6 +315,8 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
|
||||
if (type >= PERF_PMU_TYPE_PE_START && type <= PERF_PMU_TYPE_PE_END) {
|
||||
to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK |
|
||||
PERF_TOOL_PMU_TYPE_PE_OTHER_MASK;
|
||||
} else if (type >= PERF_PMU_TYPE_DRM_START && type <= PERF_PMU_TYPE_DRM_END) {
|
||||
to_read_pmus = PERF_TOOL_PMU_TYPE_DRM_MASK;
|
||||
} else if (type >= PERF_PMU_TYPE_HWMON_START && type <= PERF_PMU_TYPE_HWMON_END) {
|
||||
to_read_pmus = PERF_TOOL_PMU_TYPE_HWMON_MASK;
|
||||
} else {
|
||||
@@ -371,6 +383,10 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *eve
|
||||
if (parse_hwmon_filename(event, &type, &number, /*item=*/NULL, /*alarm=*/NULL))
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK;
|
||||
|
||||
/* Could the event be a DRM event? */
|
||||
if (strlen(event) > 4 && strncmp("drm-", event, 4) == 0)
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK;
|
||||
|
||||
pmu_read_sysfs(to_read_pmus);
|
||||
pmu = list_prepare_entry(pmu, &core_pmus, list);
|
||||
}
|
||||
@@ -403,11 +419,17 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const c
|
||||
* Hwmon PMUs have an alias from a sysfs name like hwmon0,
|
||||
* hwmon1, etc. or have a name of hwmon_<name>. They therefore
|
||||
* can only have a wildcard match if the wildcard begins with
|
||||
* "hwmon".
|
||||
* "hwmon". Similarly drm PMUs must start "drm_", avoid reading
|
||||
* such events unless the PMU could match.
|
||||
*/
|
||||
if (strisglob(wildcard) ||
|
||||
(strlen(wildcard) >= 5 && strncmp("hwmon", wildcard, 5) == 0))
|
||||
if (strisglob(wildcard)) {
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK |
|
||||
PERF_TOOL_PMU_TYPE_DRM_MASK;
|
||||
} else if (strlen(wildcard) >= 4 && strncmp("drm_", wildcard, 4) == 0) {
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK;
|
||||
} else if (strlen(wildcard) >= 5 && strncmp("hwmon", wildcard, 5) == 0) {
|
||||
to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK;
|
||||
}
|
||||
|
||||
pmu_read_sysfs(to_read_pmus);
|
||||
pmu = list_prepare_entry(pmu, &core_pmus, list);
|
||||
|
||||
Reference in New Issue
Block a user